sysupdater: Add ValidateUpdate, begin implementing Async logic

This commit is contained in:
Michael Scire 2020-06-26 11:36:38 -07:00 committed by SciresM
parent 1bc271bab1
commit f223c27bb0
19 changed files with 677 additions and 3 deletions

View file

@ -45,6 +45,7 @@
#include <stratosphere/cfg.hpp>
#include <stratosphere/dmnt.hpp>
#include <stratosphere/erpt.hpp>
#include <stratosphere/err.hpp>
#include <stratosphere/fatal.hpp>
#include <stratosphere/hid.hpp>
#include <stratosphere/hos.hpp>

View file

@ -62,6 +62,7 @@ namespace ams::impl {
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, IpcServer);
AMS_DEFINE_SYSTEM_THREAD(21, mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
/* boot2. */
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
@ -94,6 +95,7 @@ namespace ams::impl {
/* ns.*/
AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession);
AMS_DEFINE_SYSTEM_THREAD(21, nssrv, AsyncPrepareCardUpdateTask);
/* settings. */
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);

View 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>

View file

@ -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);
}

View file

@ -45,7 +45,7 @@ namespace ams::ncm {
};
}
constexpr ContentMetaKey ToKey() {
constexpr ContentMetaKey ToKey() const {
return ContentMetaKey::Make(this->id, this->version, this->type);
}
};

View file

@ -27,6 +27,8 @@ namespace ams::os {
constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2;
constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1;
constexpr inline s32 InvalidThreadPriority = 127;
constexpr inline s32 LowestSystemThreadPriority = 35;
constexpr inline s32 HighestSystemThreadPriority = -12;

View file

@ -13,7 +13,6 @@
* 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
namespace ams::sf {

View file

@ -37,8 +37,10 @@
#include <vapours/results/kvdb_results.hpp>
#include <vapours/results/loader_results.hpp>
#include <vapours/results/lr_results.hpp>
#include <vapours/results/os_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/pm_results.hpp>
#include <vapours/results/psc_results.hpp>

View 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);
}

View 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);
}

View file

@ -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();
}
}

View file

@ -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; }
};
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -17,6 +17,7 @@
#include "../amsmitm_initialization.hpp"
#include "sysupdater_module.hpp"
#include "sysupdater_service.hpp"
#include "sysupdater_async_impl.hpp"
namespace ams::mitm::sysupdater {

View file

@ -122,6 +122,159 @@ namespace ams::mitm::sysupdater {
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() {
/* TODO: Don't use gamecard db. */
return ncm::ActivateContentMetaDatabase(ncm::StorageId::GameCard);
@ -244,4 +397,29 @@ namespace ams::mitm::sysupdater {
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();
};
}

View file

@ -27,16 +27,24 @@ namespace ams::mitm::sysupdater {
ncm::FirmwareVariationId firmware_variation_ids[FirmwareVariationCountMax];
};
struct UpdateValidationInfo {
ncm::ContentMetaKey invalid_key;
ncm::ContentId invalid_content_id;
};
class SystemUpdateService final : public sf::IServiceObject {
private:
enum class CommandId {
GetUpdateInformation = 0,
ValidateUpdate = 1,
};
private:
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:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(GetUpdateInformation),
MAKE_SERVICE_COMMAND_META(ValidateUpdate),
};
};

View file

@ -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");
}
}

View file

@ -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);
};
}