mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
sysupdater: Add ValidateUpdate, begin implementing Async logic
This commit is contained in:
parent
1bc271bab1
commit
f223c27bb0
19 changed files with 677 additions and 3 deletions
|
@ -45,6 +45,7 @@
|
||||||
#include <stratosphere/cfg.hpp>
|
#include <stratosphere/cfg.hpp>
|
||||||
#include <stratosphere/dmnt.hpp>
|
#include <stratosphere/dmnt.hpp>
|
||||||
#include <stratosphere/erpt.hpp>
|
#include <stratosphere/erpt.hpp>
|
||||||
|
#include <stratosphere/err.hpp>
|
||||||
#include <stratosphere/fatal.hpp>
|
#include <stratosphere/fatal.hpp>
|
||||||
#include <stratosphere/hid.hpp>
|
#include <stratosphere/hid.hpp>
|
||||||
#include <stratosphere/hos.hpp>
|
#include <stratosphere/hos.hpp>
|
||||||
|
|
|
@ -62,6 +62,7 @@ namespace ams::impl {
|
||||||
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
|
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
|
||||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
|
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
|
||||||
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
|
||||||
|
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||||
|
|
||||||
/* boot2. */
|
/* boot2. */
|
||||||
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
|
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
|
||||||
|
@ -94,6 +95,7 @@ namespace ams::impl {
|
||||||
|
|
||||||
/* ns.*/
|
/* ns.*/
|
||||||
AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession);
|
AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession);
|
||||||
|
AMS_DEFINE_SYSTEM_THREAD(21, nssrv, AsyncPrepareCardUpdateTask);
|
||||||
|
|
||||||
/* settings. */
|
/* settings. */
|
||||||
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);
|
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);
|
||||||
|
|
19
libraries/libstratosphere/include/stratosphere/err.hpp
Normal file
19
libraries/libstratosphere/include/stratosphere/err.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stratosphere/err/err_error_context.hpp>
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||||
|
|
||||||
|
namespace ams::err {
|
||||||
|
|
||||||
|
enum class ErrorContextType : u8 {
|
||||||
|
None = 0,
|
||||||
|
Http = 1,
|
||||||
|
FileSystem = 2,
|
||||||
|
WebMediaPlayer = 3,
|
||||||
|
LocalContentShare = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PaddingErrorContext {
|
||||||
|
u8 padding[0x200 - 8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorContext : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||||
|
ErrorContextType type;
|
||||||
|
u8 reserved[7];
|
||||||
|
|
||||||
|
union {
|
||||||
|
PaddingErrorContext padding;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ErrorContext) == 0x200);
|
||||||
|
static_assert(util::is_pod<ErrorContext>::value);
|
||||||
|
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ namespace ams::ncm {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ContentMetaKey ToKey() {
|
constexpr ContentMetaKey ToKey() const {
|
||||||
return ContentMetaKey::Make(this->id, this->version, this->type);
|
return ContentMetaKey::Make(this->id, this->version, this->type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace ams::os {
|
||||||
constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2;
|
constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2;
|
||||||
constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1;
|
constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1;
|
||||||
|
|
||||||
|
constexpr inline s32 InvalidThreadPriority = 127;
|
||||||
|
|
||||||
constexpr inline s32 LowestSystemThreadPriority = 35;
|
constexpr inline s32 LowestSystemThreadPriority = 35;
|
||||||
constexpr inline s32 HighestSystemThreadPriority = -12;
|
constexpr inline s32 HighestSystemThreadPriority = -12;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace ams::sf {
|
namespace ams::sf {
|
||||||
|
|
|
@ -37,8 +37,10 @@
|
||||||
#include <vapours/results/kvdb_results.hpp>
|
#include <vapours/results/kvdb_results.hpp>
|
||||||
#include <vapours/results/loader_results.hpp>
|
#include <vapours/results/loader_results.hpp>
|
||||||
#include <vapours/results/lr_results.hpp>
|
#include <vapours/results/lr_results.hpp>
|
||||||
#include <vapours/results/os_results.hpp>
|
|
||||||
#include <vapours/results/ncm_results.hpp>
|
#include <vapours/results/ncm_results.hpp>
|
||||||
|
#include <vapours/results/nim_results.hpp>
|
||||||
|
#include <vapours/results/ns_results.hpp>
|
||||||
|
#include <vapours/results/os_results.hpp>
|
||||||
#include <vapours/results/pgl_results.hpp>
|
#include <vapours/results/pgl_results.hpp>
|
||||||
#include <vapours/results/pm_results.hpp>
|
#include <vapours/results/pm_results.hpp>
|
||||||
#include <vapours/results/psc_results.hpp>
|
#include <vapours/results/psc_results.hpp>
|
||||||
|
|
26
libraries/libvapours/include/vapours/results/nim_results.hpp
Normal file
26
libraries/libvapours/include/vapours/results/nim_results.hpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/results/results_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::nim {
|
||||||
|
|
||||||
|
R_DEFINE_NAMESPACE_RESULT_MODULE(137);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(HttpConnectionCanceled, 70);
|
||||||
|
|
||||||
|
}
|
27
libraries/libvapours/include/vapours/results/ns_results.hpp
Normal file
27
libraries/libvapours/include/vapours/results/ns_results.hpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/results/results_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::ns {
|
||||||
|
|
||||||
|
R_DEFINE_NAMESPACE_RESULT_MODULE(16);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(Canceled, 90);
|
||||||
|
R_DEFINE_ERROR_RESULT(OutOfMaxRunningTask, 110);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sysupdater_async_impl.hpp"
|
||||||
|
#include "sysupdater_async_thread_allocator.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
Result AsyncBase::ToAsyncResult(Result result) {
|
||||||
|
R_TRY_CATCH(result) {
|
||||||
|
R_CONVERT(nim::ResultHttpConnectionCanceled, ns::ResultCanceled());
|
||||||
|
R_CONVERT(ncm::ResultInstallTaskCancelled, ns::ResultCanceled());
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncPrepareSdCardUpdateImpl::~AsyncPrepareSdCardUpdateImpl() {
|
||||||
|
if (this->thread_info) {
|
||||||
|
os::WaitThread(this->thread_info->thread);
|
||||||
|
os::DestroyThread(this->thread_info->thread);
|
||||||
|
GetAsyncThreadAllocator()->Free(*this->thread_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AsyncPrepareSdCardUpdateImpl::Run() {
|
||||||
|
/* Get a thread info. */
|
||||||
|
ThreadInfo info;
|
||||||
|
R_TRY(GetAsyncThreadAllocator()->Allocate(std::addressof(info)));
|
||||||
|
|
||||||
|
/* Set the thread info's priority. */
|
||||||
|
info.priority = AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||||
|
|
||||||
|
/* Ensure that we clean up appropriately. */
|
||||||
|
ON_SCOPE_EXIT {
|
||||||
|
if (!this->thread_info) {
|
||||||
|
GetAsyncThreadAllocator()->Free(info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Create a thread for the task. */
|
||||||
|
R_TRY(os::CreateThread(info.thread, [](void *arg) {
|
||||||
|
auto *_this = reinterpret_cast<AsyncPrepareSdCardUpdateImpl *>(arg);
|
||||||
|
_this->result = _this->Execute();
|
||||||
|
_this->event.Signal();
|
||||||
|
}, this, info.stack, info.stack_size, info.priority));
|
||||||
|
|
||||||
|
/* Set the thread name. */
|
||||||
|
os::SetThreadNamePointer(info.thread, AMS_GET_SYSTEM_THREAD_NAME(mitm_sysupdater, AsyncPrepareSdCardUpdateTask));
|
||||||
|
|
||||||
|
/* Start the thread. */
|
||||||
|
os::StartThread(info.thread);
|
||||||
|
|
||||||
|
/* Set our thread info. */
|
||||||
|
this->thread_info = info;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AsyncPrepareSdCardUpdateImpl::Execute() {
|
||||||
|
return this->task->PrepareAndExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncPrepareSdCardUpdateImpl::CancelImpl() {
|
||||||
|
this->task->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sysupdater_thread_allocator.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
class ErrorContextHolder {
|
||||||
|
private:
|
||||||
|
err::ErrorContext error_context;
|
||||||
|
public:
|
||||||
|
constexpr ErrorContextHolder() : error_context{} { /* ... */ }
|
||||||
|
|
||||||
|
virtual ~ErrorContextHolder() { /* ... */ }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Result SaveErrorContextIfFailed(T &async, Result result) {
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
async.GetErrorContext(std::addressof(this->error_context));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Result GetAndSaveErrorContext(T &async) {
|
||||||
|
R_TRY(this->SaveErrorContextIfFailed(async, async.Get()));
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Result SaveInternalTaskErrorContextIfFailed(T &async, Result result) {
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
async.CreateErrorContext(std::addressof(this->error_context));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
const err::ErrorContext &GetErrorContextImpl() {
|
||||||
|
return this->error_context;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncBase {
|
||||||
|
public:
|
||||||
|
virtual ~AsyncBase() { /* ... */ }
|
||||||
|
|
||||||
|
Result Cancel() {
|
||||||
|
this->CancelImpl();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result ToAsyncResult(Result result);
|
||||||
|
|
||||||
|
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) {
|
||||||
|
*out = {};
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
virtual void CancelImpl() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncResultBase : public AsyncBase {
|
||||||
|
public:
|
||||||
|
Result Get() {
|
||||||
|
return ToAsyncResult(this->GetImpl());
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
virtual Result GetImpl() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* NOTE: Based off of ns AsyncPrepareCardUpdateImpl. */
|
||||||
|
/* We don't implement the RequestServer::ManagedStop details, as we don't implement stoppable request list. */
|
||||||
|
class AsyncPrepareSdCardUpdateImpl : public AsyncResultBase, private ErrorContextHolder {
|
||||||
|
private:
|
||||||
|
Result result;
|
||||||
|
os::SystemEvent event;
|
||||||
|
std::optional<ThreadInfo> thread_info;
|
||||||
|
ncm::InstallTaskBase *task;
|
||||||
|
public:
|
||||||
|
AsyncPrepareSdCardUpdateImpl(ncm::InstallTaskBase *task) : result(ResultSuccess()), event(os::EventClearMode_ManualClear, true), thread_info(), task(task) { /* ... */ }
|
||||||
|
virtual ~AsyncPrepareSdCardUpdateImpl();
|
||||||
|
|
||||||
|
os::SystemEvent &GetEvent() { return this->event; }
|
||||||
|
|
||||||
|
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) override {
|
||||||
|
*out = ErrorContextHolder::GetErrorContextImpl();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Run();
|
||||||
|
private:
|
||||||
|
Result Execute();
|
||||||
|
|
||||||
|
virtual void CancelImpl() override;
|
||||||
|
virtual Result GetImpl() override { return this->result; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sysupdater_async_thread_allocator.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline int AsyncThreadCount = 1;
|
||||||
|
constexpr inline size_t AsyncThreadStackSize = 16_KB;
|
||||||
|
|
||||||
|
os::ThreadType g_async_threads[AsyncThreadCount];
|
||||||
|
alignas(os::ThreadStackAlignment) u8 g_async_thread_stack_heap[AsyncThreadCount * AsyncThreadStackSize];
|
||||||
|
|
||||||
|
constinit ThreadAllocator g_async_thread_allocator(g_async_threads, AsyncThreadCount, os::InvalidThreadPriority, g_async_thread_stack_heap, sizeof(g_async_thread_stack_heap), AsyncThreadStackSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadAllocator *GetAsyncThreadAllocator() {
|
||||||
|
return std::addressof(g_async_thread_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sysupdater_thread_allocator.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
ThreadAllocator *GetAsyncThreadAllocator();
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
#include "../amsmitm_initialization.hpp"
|
#include "../amsmitm_initialization.hpp"
|
||||||
#include "sysupdater_module.hpp"
|
#include "sysupdater_module.hpp"
|
||||||
#include "sysupdater_service.hpp"
|
#include "sysupdater_service.hpp"
|
||||||
|
#include "sysupdater_async_impl.hpp"
|
||||||
|
|
||||||
namespace ams::mitm::sysupdater {
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,159 @@ namespace ams::mitm::sysupdater {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ValidateSystemUpdate(Result *out_result, UpdateValidationInfo *out_info, const ncm::PackagedContentMetaReader &update_reader, const char *package_root) {
|
||||||
|
/* Clear output. */
|
||||||
|
*out_result = ResultSuccess();
|
||||||
|
|
||||||
|
/* We want to track all content the update requires. */
|
||||||
|
const size_t num_content_metas = update_reader.GetContentMetaCount();
|
||||||
|
bool content_meta_valid[num_content_metas] = {};
|
||||||
|
|
||||||
|
/* Allocate a buffer to use for validation. */
|
||||||
|
size_t data_buffer_size = 1_MB;
|
||||||
|
void *data_buffer;
|
||||||
|
do {
|
||||||
|
data_buffer = std::malloc(data_buffer_size);
|
||||||
|
if (data_buffer != nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_buffer_size /= 2;
|
||||||
|
} while (data_buffer_size >= 16_KB);
|
||||||
|
R_UNLESS(data_buffer != nullptr, fs::ResultAllocationFailureInNew());
|
||||||
|
|
||||||
|
ON_SCOPE_EXIT { std::free(data_buffer); };
|
||||||
|
|
||||||
|
/* Declare helper for result validation. */
|
||||||
|
auto ValidateResult = [&] ALWAYS_INLINE_LAMBDA (Result result) -> Result {
|
||||||
|
*out_result = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Iterate over all files to find all content metas. */
|
||||||
|
R_TRY(ForEachFileInDirectory(package_root, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
|
||||||
|
/* Clear output. */
|
||||||
|
*out_info = {};
|
||||||
|
|
||||||
|
/* Don't early terminate by default. */
|
||||||
|
*done = false;
|
||||||
|
|
||||||
|
/* We have nothing to list if we're not looking at a meta. */
|
||||||
|
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(".cnmt.nca"));
|
||||||
|
|
||||||
|
/* Read the content meta path, and build. */
|
||||||
|
ncm::AutoBuffer package_meta;
|
||||||
|
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root, entry));
|
||||||
|
|
||||||
|
/* Create a reader. */
|
||||||
|
const auto reader = ncm::PackagedContentMetaReader(package_meta.Get(), package_meta.GetSize());
|
||||||
|
|
||||||
|
/* Get the key for the reader. */
|
||||||
|
const auto key = reader.GetKey();
|
||||||
|
|
||||||
|
/* Check if we need to validate this content. */
|
||||||
|
bool need_validate = false;
|
||||||
|
size_t validation_index = 0;
|
||||||
|
for (size_t i = 0; i < num_content_metas; ++i) {
|
||||||
|
if (update_reader.GetContentMetaInfo(i)->ToKey() == key) {
|
||||||
|
need_validate = true;
|
||||||
|
validation_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we don't need to validate, continue. */
|
||||||
|
R_SUCCEED_IF(!need_validate);
|
||||||
|
|
||||||
|
/* We're validating. */
|
||||||
|
out_info->invalid_key = key;
|
||||||
|
|
||||||
|
/* Validate all contents. */
|
||||||
|
for (size_t i = 0; i < reader.GetContentCount(); ++i) {
|
||||||
|
const auto *content_info = reader.GetContentInfo(i);
|
||||||
|
const auto &content_id = content_info->GetId();
|
||||||
|
const s64 content_size = content_info->info.GetSize();
|
||||||
|
out_info->invalid_content_id = content_id;
|
||||||
|
|
||||||
|
/* Get the content id string. */
|
||||||
|
auto content_id_str = ncm::GetContentIdString(content_id);
|
||||||
|
|
||||||
|
/* Open the file. */
|
||||||
|
fs::FileHandle file;
|
||||||
|
{
|
||||||
|
char path[fs::EntryNameLengthMax];
|
||||||
|
std::snprintf(path, sizeof(path), "%s%s%s", package_root, content_id_str.data, content_info->GetType() == ncm::ContentType::Meta ? ".cnmt.nca" : ".nca");
|
||||||
|
if (R_FAILED(ValidateResult(fs::OpenFile(std::addressof(file), path, ams::fs::OpenMode_Read)))) {
|
||||||
|
*done = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||||
|
|
||||||
|
/* Validate the file size is correct. */
|
||||||
|
s64 file_size;
|
||||||
|
if (R_FAILED(ValidateResult(fs::GetFileSize(std::addressof(file_size), file)))) {
|
||||||
|
*done = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
if (file_size != content_size) {
|
||||||
|
*out_result = ncm::ResultInvalidContentHash();
|
||||||
|
*done = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and hash the file in chunks. */
|
||||||
|
crypto::Sha256Generator sha;
|
||||||
|
sha.Initialize();
|
||||||
|
|
||||||
|
s64 ofs = 0;
|
||||||
|
while (ofs < content_size) {
|
||||||
|
const size_t cur_size = std::min(static_cast<size_t>(content_size - ofs), data_buffer_size);
|
||||||
|
if (R_FAILED(ValidateResult(fs::ReadFile(file, ofs, data_buffer, cur_size)))) {
|
||||||
|
*done = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
sha.Update(data_buffer, cur_size);
|
||||||
|
|
||||||
|
ofs += cur_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the hash. */
|
||||||
|
ncm::Digest calc_digest;
|
||||||
|
sha.GetHash(std::addressof(calc_digest), sizeof(calc_digest));
|
||||||
|
|
||||||
|
/* Validate the hash. */
|
||||||
|
if (std::memcmp(std::addressof(calc_digest), std::addressof(content_info->digest), sizeof(ncm::Digest)) != 0) {
|
||||||
|
*out_result = ncm::ResultInvalidContentHash();
|
||||||
|
*done = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark the relevant content as validated. */
|
||||||
|
content_meta_valid[validation_index] = true;
|
||||||
|
*out_info = {};
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* If we're otherwise going to succeed, ensure that every content was found. */
|
||||||
|
if (R_SUCCEEDED(*out_result)) {
|
||||||
|
for (size_t i = 0; i < num_content_metas; ++i) {
|
||||||
|
if (!content_meta_valid[i]) {
|
||||||
|
*out_result = fs::ResultPathNotFound();
|
||||||
|
*out_info = {
|
||||||
|
.invalid_key = update_reader.GetContentMetaInfo(i)->ToKey(),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
Result ActivateSystemUpdateContentMetaDatabase() {
|
Result ActivateSystemUpdateContentMetaDatabase() {
|
||||||
/* TODO: Don't use gamecard db. */
|
/* TODO: Don't use gamecard db. */
|
||||||
return ncm::ActivateContentMetaDatabase(ncm::StorageId::GameCard);
|
return ncm::ActivateContentMetaDatabase(ncm::StorageId::GameCard);
|
||||||
|
@ -244,4 +397,29 @@ namespace ams::mitm::sysupdater {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result SystemUpdateService::ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path) {
|
||||||
|
/* Adjust the path. */
|
||||||
|
ncm::Path package_root;
|
||||||
|
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
|
||||||
|
|
||||||
|
/* Parse the update. */
|
||||||
|
{
|
||||||
|
/* Get the content info for the system update. */
|
||||||
|
ncm::ContentInfo content_info;
|
||||||
|
R_TRY(GetSystemUpdateUpdateContentInfoFromPackage(std::addressof(content_info), package_root.str));
|
||||||
|
|
||||||
|
/* Read the content meta. */
|
||||||
|
ncm::AutoBuffer content_meta_buffer;
|
||||||
|
R_TRY(ReadContentMetaPath(std::addressof(content_meta_buffer), package_root.str, content_info));
|
||||||
|
|
||||||
|
/* Create a reader. */
|
||||||
|
const auto reader = ncm::PackagedContentMetaReader(content_meta_buffer.Get(), content_meta_buffer.GetSize());
|
||||||
|
|
||||||
|
/* Validate the update. */
|
||||||
|
R_TRY(ValidateSystemUpdate(out_validate_result.GetPointer(), out_validate_info.GetPointer(), reader, package_root.str));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,24 @@ namespace ams::mitm::sysupdater {
|
||||||
ncm::FirmwareVariationId firmware_variation_ids[FirmwareVariationCountMax];
|
ncm::FirmwareVariationId firmware_variation_ids[FirmwareVariationCountMax];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UpdateValidationInfo {
|
||||||
|
ncm::ContentMetaKey invalid_key;
|
||||||
|
ncm::ContentId invalid_content_id;
|
||||||
|
};
|
||||||
|
|
||||||
class SystemUpdateService final : public sf::IServiceObject {
|
class SystemUpdateService final : public sf::IServiceObject {
|
||||||
private:
|
private:
|
||||||
enum class CommandId {
|
enum class CommandId {
|
||||||
GetUpdateInformation = 0,
|
GetUpdateInformation = 0,
|
||||||
|
ValidateUpdate = 1,
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
Result GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path);
|
Result GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path);
|
||||||
|
Result ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path);
|
||||||
public:
|
public:
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
MAKE_SERVICE_COMMAND_META(GetUpdateInformation),
|
MAKE_SERVICE_COMMAND_META(GetUpdateInformation),
|
||||||
|
MAKE_SERVICE_COMMAND_META(ValidateUpdate),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sysupdater_thread_allocator.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
Result ThreadAllocator::Allocate(ThreadInfo *out) {
|
||||||
|
std::scoped_lock lk(this->mutex);
|
||||||
|
|
||||||
|
for (int i = 0; i < this->thread_count; ++i) {
|
||||||
|
const u64 mask = (static_cast<u64>(1) << i);
|
||||||
|
if ((this->bitmap & mask) == 0) {
|
||||||
|
*out = {
|
||||||
|
.thread = this->thread_list + i,
|
||||||
|
.priority = this->thread_priority,
|
||||||
|
.stack = this->stack_heap + (this->stack_size * i),
|
||||||
|
.stack_size = this->stack_size,
|
||||||
|
};
|
||||||
|
this->bitmap |= mask;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns::ResultOutOfMaxRunningTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadAllocator::Free(const ThreadInfo &info) {
|
||||||
|
std::scoped_lock lk(this->mutex);
|
||||||
|
|
||||||
|
for (int i = 0; i < this->thread_count; ++i) {
|
||||||
|
if (info.thread == std::addressof(this->thread_list[i])) {
|
||||||
|
const u64 mask = (static_cast<u64>(1) << i);
|
||||||
|
this->bitmap &= ~mask;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMS_ABORT("Invalid thread passed to ThreadAllocator::Free");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::mitm::sysupdater {
|
||||||
|
|
||||||
|
struct ThreadInfo {
|
||||||
|
os::ThreadType *thread;
|
||||||
|
int priority;
|
||||||
|
void *stack;
|
||||||
|
size_t stack_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* NOTE: Nintendo uses a util::BitArray, but this seems excessive. */
|
||||||
|
class ThreadAllocator {
|
||||||
|
private:
|
||||||
|
os::ThreadType *thread_list;
|
||||||
|
const int thread_priority;
|
||||||
|
const int thread_count;
|
||||||
|
u8 *stack_heap;
|
||||||
|
const size_t stack_heap_size;
|
||||||
|
const size_t stack_size;
|
||||||
|
u64 bitmap;
|
||||||
|
os::SdkMutex mutex;
|
||||||
|
public:
|
||||||
|
constexpr ThreadAllocator(os::ThreadType *thread_list, int count, int priority, u8 *stack_heap, size_t stack_heap_size, size_t stack_size)
|
||||||
|
: thread_list(thread_list), thread_priority(priority), thread_count(count), stack_heap(stack_heap), stack_heap_size(stack_heap_size), stack_size(stack_size), bitmap()
|
||||||
|
{
|
||||||
|
AMS_ASSERT(count <= static_cast<int>(stack_heap_size / stack_size));
|
||||||
|
AMS_ASSERT(count <= static_cast<int>(BITSIZEOF(this->bitmap)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Allocate(ThreadInfo *out);
|
||||||
|
void Free(const ThreadInfo &info);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue