diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index bc5547f97..92ac1defe 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/mitm.hpp b/libraries/libstratosphere/include/stratosphere/mitm.hpp new file mode 100644 index 000000000..c4e7afe64 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/mitm.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 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 . + */ + +#pragma once + +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/mitm/impl/mitm_pm_interface.hpp b/libraries/libstratosphere/include/stratosphere/mitm/impl/mitm_pm_interface.hpp new file mode 100644 index 000000000..704fdb3ab --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/mitm/impl/mitm_pm_interface.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 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 . + */ + +#pragma once +#include +#include +#include +#include + +#define AMS_MITM_PM_IMPL_I_PM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 65000, Result, PrepareLaunchProgram, (sf::Out out_boost_size, ncm::ProgramId program_id, const cfg::OverrideStatus &override_status, bool is_application), (out_boost_size, program_id, override_status, is_application)) + +AMS_SF_DEFINE_INTERFACE(ams::mitm::pm::impl, IPmInterface, AMS_MITM_PM_IMPL_I_PM_INTERFACE_INFO, 0xEA88789C) + diff --git a/libraries/libstratosphere/include/stratosphere/mitm/mitm_pm_api.hpp b/libraries/libstratosphere/include/stratosphere/mitm/mitm_pm_api.hpp new file mode 100644 index 000000000..feab8db66 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/mitm/mitm_pm_api.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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 . + */ + +#pragma once +#include +#include + +namespace ams::mitm::pm { + + /* PM API. */ + void Initialize(); + void Finalize(); + + Result PrepareLaunchProgram(u64 *out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp b/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp index 1da93af4c..1cccdab65 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp @@ -19,31 +19,32 @@ #include #include -#define AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \ - AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 5, void, NotifyBootFinished, (), ()) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, GetApplicationProcessIdForShell, (sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 7, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size)) \ - AMS_SF_METHOD_INFO(C, H, 8, Result, BoostApplicationThreadResourceLimit, (), ()) \ - AMS_SF_METHOD_INFO(C, H, 9, void, GetBootFinishedEventHandle, (sf::OutCopyHandle out), (out), hos::Version_8_0_0) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit, (), ()) +#define AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \ + AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 5, void, NotifyBootFinished, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetApplicationProcessIdForShell, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, BoostApplicationThreadResourceLimit, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 9, void, GetBootFinishedEventHandle, (sf::OutCopyHandle out), (out), hos::Version_8_0_0) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit, (), ()) AMS_SF_DEFINE_INTERFACE(ams::pm::impl, IShellInterface, AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO, 0x387D60C0) -#define AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \ - AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, CleanupProcess, (os::ProcessId process_id), (process_id)) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, ClearExceptionOccurred, (os::ProcessId process_id), (process_id)) \ - AMS_SF_METHOD_INFO(C, H, 7, void, NotifyBootFinished, (), ()) \ - AMS_SF_METHOD_INFO(C, H, 8, Result, GetApplicationProcessIdForShell, (sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 9, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size), hos::Version_4_0_0) +#define AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \ + AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, CleanupProcess, (os::ProcessId process_id), (process_id)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, ClearExceptionOccurred, (os::ProcessId process_id), (process_id)) \ + AMS_SF_METHOD_INFO(C, H, 7, void, NotifyBootFinished, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, GetApplicationProcessIdForShell, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size), hos::Version_4_0_0) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit, (), ()) AMS_SF_DEFINE_INTERFACE(ams::pm::impl, IDeprecatedShellInterface, AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO, 0x387D60C0) diff --git a/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.c b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.c new file mode 100644 index 000000000..adfabd4fa --- /dev/null +++ b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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 . + */ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include "../service_guard.h" +#include "mitm_pm.os.horizon.h" + +static Service g_amsMitmPmSrv; + +NX_GENERATE_SERVICE_GUARD(amsMitmPm); + +Result _amsMitmPmInitialize(void) { + return smGetService(&g_amsMitmPmSrv, "mitm:pm"); +} + +void _amsMitmPmCleanup(void) { + serviceClose(&g_amsMitmPmSrv); +} + +Service *amsMitmPmGetServiceSession(void) { + return &g_amsMitmPmSrv; +} + +Result amsMitmPmPrepareLaunchProgram(u64 *out, u64 program_id, const CfgOverrideStatus *status, bool is_application) { + const struct { + u8 is_application; + u64 program_id; + CfgOverrideStatus status; + } in = { is_application ? 1 : 0, program_id, *status }; + + return serviceDispatchInOut(&g_amsMitmPmSrv, 65000, in, *out); +} diff --git a/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.h b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.h new file mode 100644 index 000000000..2d8da12c4 --- /dev/null +++ b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 . + */ + +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + u64 keys_down; + u64 flags; +} CfgOverrideStatus; + +Result amsMitmPmInitialize(void); +void amsMitmPmExit(void); +Service *amsMitmPmGetServiceSession(void); + +Result amsMitmPmPrepareLaunchProgram(u64 *out, u64 program_id, const CfgOverrideStatus *status, bool is_application); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libraries/libstratosphere/source/mitm/mitm_pm_api.cpp b/libraries/libstratosphere/source/mitm/mitm_pm_api.cpp new file mode 100644 index 000000000..a49cf9ce9 --- /dev/null +++ b/libraries/libstratosphere/source/mitm/mitm_pm_api.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 . + */ +#include +#include "mitm_pm.os.horizon.h" + +namespace ams::mitm::pm { + + /* PM API. */ + #if defined(ATMOSPHERE_OS_HORIZON) + void Initialize() { + R_ABORT_UNLESS(amsMitmPmInitialize()); + } + + void Finalize() { + amsMitmPmExit(); + } + + Result PrepareLaunchProgram(u64 *out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) { + static_assert(sizeof(status) == sizeof(CfgOverrideStatus), "CfgOverrideStatus definition!"); + R_RETURN(amsMitmPmPrepareLaunchProgram(out, program_id.value, reinterpret_cast(std::addressof(status)), is_application)); + } + #endif + +} diff --git a/libraries/libstratosphere/source/pm/pm_shell_api.cpp b/libraries/libstratosphere/source/pm/pm_shell_api.cpp index 03470e47f..247d88cbb 100644 --- a/libraries/libstratosphere/source/pm/pm_shell_api.cpp +++ b/libraries/libstratosphere/source/pm/pm_shell_api.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include "pm_ams.os.horizon.h" namespace ams::pm::shell { diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index e47e9f55b..104a299f3 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -41,8 +41,9 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(MountNameAlreadyExists, 60); R_DEFINE_ERROR_RANGE(HandledBySystemProcess, 1000, 2999); - R_DEFINE_ERROR_RESULT(PartitionNotFound, 1001); - R_DEFINE_ERROR_RESULT(TargetNotFound, 1002); + R_DEFINE_ERROR_RESULT(PartitionNotFound, 1001); + R_DEFINE_ERROR_RESULT(TargetNotFound, 1002); + R_DEFINE_ERROR_RESULT(NcaExternalKeyNotFound, 1004); R_DEFINE_ERROR_RANGE(SdCardAccessFailed, 2000, 2499); R_DEFINE_ERROR_RESULT(SdCardNotPresent, 2001); diff --git a/stratosphere/ams_mitm/ams_mitm.json b/stratosphere/ams_mitm/ams_mitm.json index 8eae1c76f..0b98a2588 100644 --- a/stratosphere/ams_mitm/ams_mitm.json +++ b/stratosphere/ams_mitm/ams_mitm.json @@ -65,6 +65,8 @@ "svcReplyAndReceive": "0x43", "svcReplyAndReceiveWithUserBuffer": "0x44", "svcCreateEvent": "0x45", + "svcMapPhysicalMemoryUnsafe": "0x48", + "svcUnmapPhysicalMemoryUnsafe": "0x49", "svcMapTransferMemory": "0x51", "svcUnmapTransferMemory": "0x52", "svcCreateInterruptEvent": "0x53", diff --git a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp index 6905f5e6a..c8087a075 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp @@ -24,6 +24,7 @@ #include "ns_mitm/nsmitm_module.hpp" #include "dns_mitm/dnsmitm_module.hpp" #include "sysupdater/sysupdater_module.hpp" +#include "mitm_pm/mitm_pm_module.hpp" namespace ams::mitm { @@ -37,6 +38,7 @@ namespace ams::mitm { ModuleId_NsMitm, ModuleId_DnsMitm, ModuleId_Sysupdater, + ModuleId_PmService, ModuleId_Count, }; @@ -70,6 +72,7 @@ namespace ams::mitm { GetModuleDefinition(), GetModuleDefinition(), GetModuleDefinition(), + GetModuleDefinition(), }; } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 12074c135..f82a6e806 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -71,7 +71,7 @@ namespace ams::mitm::fs { Result OpenHblWebContentFileSystem(sf::Out> &out, ncm::ProgramId program_id) { /* Verify eligibility. */ bool is_hbl; - R_UNLESS(R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession()); + R_UNLESS(R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession()); R_UNLESS(is_hbl, sm::mitm::ResultShouldForwardToSession()); /* Hbl html directory must exist. */ diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp index de41c800f..f46e041de 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp @@ -78,6 +78,7 @@ namespace ams::mitm::fs { constinit os::SdkRecursiveMutex g_storage_set_mutex; constinit LayeredRomfsStorageSet g_storage_set; + constinit os::SdkMutex g_initialization_mutex; void OpenReference(LayeredRomfsStorageImpl *impl) { std::scoped_lock lk(g_storage_set_mutex); @@ -106,6 +107,8 @@ namespace ams::mitm::fs { auto *impl = reinterpret_cast(storage_uptr); g_ack_mq.Send(storage_uptr); + std::scoped_lock lk(g_initialization_mutex); + impl->InitializeImpl(); /* Close the initial reference. */ @@ -255,6 +258,21 @@ namespace ams::mitm::fs { return std::make_shared(impl); } + void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id) { + std::scoped_lock lk(g_initialization_mutex); + std::scoped_lock lk2(g_storage_set_mutex); + + /* Find an existing storage. */ + if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) { + /* We need to delete the process romfs. Require invariant that it is unreferenced, by this point. */ + AMS_ABORT_UNLESS(it->GetReferenceCount() == 0); + + auto *holder = std::addressof(*it); + it = g_storage_set.erase(it); + delete holder; + } + } + LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr s_r, std::unique_ptr f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) { /* ... */ } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp index ad22120c0..337e47c47 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp @@ -22,7 +22,7 @@ namespace ams::mitm::fs { class LayeredRomfsStorageImpl { private: - std::vector m_source_infos; + romfs::Builder::SourceInfoVector m_source_infos; std::unique_ptr m_storage_romfs; std::unique_ptr m_file_romfs; os::Event m_initialize_event; @@ -51,4 +51,6 @@ namespace ams::mitm::fs { std::shared_ptr GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs); + void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id); + } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp index 542d5e202..62fd74ef3 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -16,6 +16,7 @@ #include #include "../amsmitm_fs_utils.hpp" #include "fsmitm_romfs.hpp" +#include "fsmitm_layered_romfs_storage.hpp" namespace ams::mitm::fs { @@ -23,6 +24,206 @@ namespace ams::mitm::fs { namespace romfs { + namespace { + + struct ApplicationWithDynamicHeapInfo { + ncm::ProgramId program_id; + size_t dynamic_app_heap_size; + size_t dynamic_system_heap_size; + }; + + constexpr const ApplicationWithDynamicHeapInfo ApplicationsWithDynamicHeap[] = { + /* Animal Crossing: New Horizons. */ + /* Requirement ~24 MB. */ + /* No particular heap sensitivity. */ + { 0x01006F8002326000, 16_MB, 0_MB }, + + /* Fire Emblem: Engage. */ + /* Requirement ~32+ MB. */ + /* No particular heap sensitivity. */ + { 0x0100A6301214E000, 16_MB, 0_MB }, + + /* The Legend of Zelda: Tears of the Kingdom. */ + /* Requirement ~48 MB. */ + /* Game is highly sensitive to memory stolen from application heap. */ + /* 1.0.0 tolerates no more than 16 MB stolen. 1.1.0 no more than 12 MB. */ + { 0x0100F2C0115B6000, 10_MB, 8_MB }, + }; + + constexpr size_t GetDynamicAppHeapSize(ncm::ProgramId program_id) { + for (const auto &info : ApplicationsWithDynamicHeap) { + if (info.program_id == program_id) { + return info.dynamic_app_heap_size; + } + } + + return 0; + } + + constexpr size_t GetDynamicSysHeapSize(ncm::ProgramId program_id) { + for (const auto &info : ApplicationsWithDynamicHeap) { + if (info.program_id == program_id) { + return info.dynamic_system_heap_size; + } + } + + return 0; + } + + template + struct DynamicHeap { + uintptr_t heap_address{}; + size_t heap_size{}; + size_t outstanding_allocations{}; + util::TypedStorage heap{}; + os::SdkMutex release_heap_lock{}; + + constexpr DynamicHeap() = default; + + void Map() { + if (this->heap_address == 0) { + /* NOTE: Lock not necessary, because this is the only location which do 0 -> non-zero. */ + + R_ABORT_UNLESS(MapImpl(std::addressof(this->heap_address), this->heap_size)); + AMS_ABORT_UNLESS(this->heap_address != 0); + + /* Create heap. */ + util::ConstructAt(this->heap, reinterpret_cast(this->heap_address), this->heap_size); + } + } + + void TryRelease() { + if (this->outstanding_allocations == 0) { + std::scoped_lock lk(this->release_heap_lock); + + if (this->heap_address != 0) { + util::DestroyAt(this->heap); + this->heap = {}; + + R_ABORT_UNLESS(UnmapImpl(this->heap_address, this->heap_size)); + + this->heap_address = 0; + } + } + } + + void *Allocate(size_t size) { + void * const ret = util::GetReference(this->heap).Allocate(size); + if (AMS_LIKELY(ret != nullptr)) { + ++this->outstanding_allocations; + } + return ret; + } + + bool TryFree(void *p) { + if (this->IsAllocated(p)) { + --this->outstanding_allocations; + + util::GetReference(this->heap).Free(p); + + return true; + } else { + return false; + } + } + + bool IsAllocated(void *p) const { + const uintptr_t address = reinterpret_cast(p); + + return this->heap_address != 0 && (this->heap_address <= address && address < this->heap_address + this->heap_size); + } + + void Reset() { + /* This should require no remaining allocations. */ + AMS_ABORT_UNLESS(this->outstanding_allocations == 0); + + /* Free the heap. */ + this->TryRelease(); + AMS_ABORT_UNLESS(this->heap_address == 0); + + /* Clear the heap size. */ + this->heap_size = 0; + } + }; + + Result MapByHeap(uintptr_t *out, size_t size) { + R_TRY(os::SetMemoryHeapSize(size)); + R_RETURN(os::AllocateMemoryBlock(out, size)); + } + + Result UnmapByHeap(uintptr_t address, size_t size) { + os::FreeMemoryBlock(address, size); + R_RETURN(os::SetMemoryHeapSize(0)); + } + + /* Dynamic allocation globals. */ + constinit os::SdkMutex g_romfs_build_lock; + constinit ncm::ProgramId g_dynamic_heap_program_id{}; + + constinit bool g_building_from_dynamic_heap = false; + + constinit DynamicHeap g_dynamic_app_heap; + constinit DynamicHeap g_dynamic_sys_heap; + + void InitializeDynamicHeapForBuildRomfs(ncm::ProgramId program_id) { + if (program_id == g_dynamic_heap_program_id && g_dynamic_app_heap.heap_size > 0) { + /* This romfs will build out of dynamic heap. */ + g_building_from_dynamic_heap = true; + + g_dynamic_app_heap.Map(); + + if (g_dynamic_sys_heap.heap_size > 0) { + g_dynamic_sys_heap.Map(); + } + } + } + + void FinalizeDynamicHeapForBuildRomfs() { + /* We are definitely no longer building out of dynamic heap. */ + g_building_from_dynamic_heap = false; + + g_dynamic_app_heap.TryRelease(); + } + + } + + void *AllocateTracked(AllocationType type, size_t size) { + AMS_UNUSED(type); + + if (g_building_from_dynamic_heap) { + void *ret = g_dynamic_app_heap.Allocate(size); + + if (ret == nullptr && g_dynamic_sys_heap.heap_address != 0) { + ret = g_dynamic_sys_heap.Allocate(size); + } + + if (ret == nullptr) { + ret = std::malloc(size); + } + + return ret; + } else { + return std::malloc(size); + } + } + + void FreeTracked(AllocationType type, void *p, size_t size) { + AMS_UNUSED(type); + AMS_UNUSED(size); + + if (g_dynamic_app_heap.TryFree(p)) { + if (!g_building_from_dynamic_heap) { + g_dynamic_app_heap.TryRelease(); + } + } else if (g_dynamic_sys_heap.TryFree(p)) { + if (!g_building_from_dynamic_heap) { + g_dynamic_sys_heap.TryRelease(); + } + } else { + std::free(p); + } + } + namespace { constexpr u32 EmptyEntry = 0xFFFFFFFF; @@ -71,22 +272,23 @@ namespace ams::mitm::fs { static constexpr size_t MaxCachedSize = (1_MB / 4); private: size_t m_cache_bitsize; + size_t m_cache_size; protected: void *m_cache; protected: DynamicTableCache(size_t sz) { - size_t cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize)); - m_cache = std::malloc(cache_size); + m_cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize)); + m_cache = AllocateTracked(AllocationType_TableCache, m_cache_size); while (m_cache == nullptr) { - cache_size >>= 1; - AMS_ABORT_UNLESS(cache_size >= 16_KB); - m_cache = std::malloc(cache_size); + m_cache_size >>= 1; + AMS_ABORT_UNLESS(m_cache_size >= 16_KB); + m_cache = AllocateTracked(AllocationType_TableCache, m_cache_size); } - m_cache_bitsize = util::CountTrailingZeros(cache_size); + m_cache_bitsize = util::CountTrailingZeros(m_cache_size); } ~DynamicTableCache() { - std::free(m_cache); + FreeTracked(AllocationType_TableCache, m_cache, m_cache_size); } ALWAYS_INLINE size_t GetCacheSize() const { return static_cast(1) << m_cache_bitsize; } @@ -113,21 +315,33 @@ namespace ams::mitm::fs { size_t m_cache_idx; u8 m_fallback_cache[FallbackCacheSize]; private: - ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) { - R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size)); + ALWAYS_INLINE bool Read(size_t ofs, void *dst, size_t size) { + R_TRY_CATCH(m_storage->Read(m_offset + ofs, dst, size)) { + R_CATCH(fs::ResultNcaExternalKeyNotFound) { return false; } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return true; } - ALWAYS_INLINE void ReloadCacheImpl(size_t idx) { + ALWAYS_INLINE bool ReloadCacheImpl(size_t idx) { const size_t rel_ofs = idx * this->GetCacheSize(); AMS_ABORT_UNLESS(rel_ofs < m_size); const size_t new_cache_size = std::min(m_size - rel_ofs, this->GetCacheSize()); - this->Read(rel_ofs, m_cache, new_cache_size); + if (!this->Read(rel_ofs, m_cache, new_cache_size)) { + return false; + } + m_cache_idx = idx; + return true; } - ALWAYS_INLINE void ReloadCache(size_t idx) { + ALWAYS_INLINE bool ReloadCache(size_t idx) { if (m_cache_idx != idx) { - this->ReloadCacheImpl(idx); + if (!this->ReloadCacheImpl(idx)) { + return false; + } } + + return true; } ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) { @@ -140,13 +354,18 @@ namespace ams::mitm::fs { } const Entry *GetEntry(u32 entry_offset) { - this->ReloadCache(this->GetCacheIndex(entry_offset)); + if (!this->ReloadCache(this->GetCacheIndex(entry_offset))) { + return nullptr; + } const size_t ofs = entry_offset % this->GetCacheSize(); const Entry *entry = reinterpret_cast(reinterpret_cast(m_cache) + ofs); if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) { - this->Read(entry_offset, m_fallback_cache, std::min(m_size - entry_offset, FallbackCacheSize)); + if (!this->Read(entry_offset, m_fallback_cache, std::min(m_size - entry_offset, FallbackCacheSize))) { + return nullptr; + } + entry = reinterpret_cast(m_fallback_cache); } return entry; @@ -293,13 +512,28 @@ namespace ams::mitm::fs { } Builder::Builder(ncm::ProgramId pr_id) : m_program_id(pr_id), m_num_dirs(0), m_num_files(0), m_dir_table_size(0), m_file_table_size(0), m_dir_hash_table_size(0), m_file_hash_table_size(0), m_file_partition_size(0) { - auto res = m_directories.emplace(std::make_unique(BuildDirectoryContext::RootTag{})); + /* Ensure only one romfs is built at any time. */ + g_romfs_build_lock.Lock(); + + /* If we should be using dynamic heap, turn it on. */ + InitializeDynamicHeapForBuildRomfs(m_program_id); + + auto res = m_directories.emplace(std::unique_ptr(AllocateTyped(AllocationType_BuildDirContext, BuildDirectoryContext::RootTag{}))); AMS_ABORT_UNLESS(res.second); m_root = res.first->get(); m_num_dirs = 1; m_dir_table_size = 0x18; } + Builder::~Builder() { + /* If we have nothing remaining in dynamic heap, release it. */ + FinalizeDynamicHeapForBuildRomfs(); + + /* Release the romfs build lock. */ + g_romfs_build_lock.Unlock(); + } + + void Builder::AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr child_ctx) { /* Set parent context member. */ child_ctx->parent = parent_ctx; @@ -347,9 +581,9 @@ namespace ams::mitm::fs { AMS_ABORT_UNLESS(num_child_dirs >= 0); { - BuildDirectoryContext **child_dirs = num_child_dirs != 0 ? reinterpret_cast(std::malloc(sizeof(BuildDirectoryContext *) * num_child_dirs)) : nullptr; + BuildDirectoryContext **child_dirs = num_child_dirs != 0 ? reinterpret_cast(AllocateTracked(AllocationType_DirPointerArray, sizeof(BuildDirectoryContext *) * num_child_dirs)) : nullptr; AMS_ABORT_UNLESS(num_child_dirs == 0 || child_dirs != nullptr); - ON_SCOPE_EXIT { std::free(child_dirs); }; + ON_SCOPE_EXIT { if (child_dirs != nullptr) { FreeTracked(AllocationType_DirPointerArray, child_dirs, sizeof(BuildDirectoryContext *) * num_child_dirs); } }; s64 cur_child_dir_ind = 0; { @@ -368,12 +602,12 @@ namespace ams::mitm::fs { AMS_ABORT_UNLESS(child_dirs != nullptr); BuildDirectoryContext *real_child = nullptr; - this->AddDirectory(std::addressof(real_child), parent, std::make_unique(m_dir_entry.name, strlen(m_dir_entry.name))); + this->AddDirectory(std::addressof(real_child), parent, std::unique_ptr(AllocateTyped(AllocationType_BuildDirContext, m_dir_entry.name, strlen(m_dir_entry.name)))); AMS_ABORT_UNLESS(real_child != nullptr); child_dirs[cur_child_dir_ind++] = real_child; AMS_ABORT_UNLESS(cur_child_dir_ind <= num_child_dirs); } else /* if (m_dir_entry.type == FsDirEntryType_File) */ { - this->AddFile(parent, std::make_unique(m_dir_entry.name, strlen(m_dir_entry.name), m_dir_entry.file_size, 0, m_cur_source_type)); + this->AddFile(parent, std::unique_ptr(AllocateTyped(AllocationType_BuildFileContext, m_dir_entry.name, strlen(m_dir_entry.name), m_dir_entry.file_size, 0, m_cur_source_type))); } } } @@ -398,12 +632,18 @@ namespace ams::mitm::fs { void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table) { const DirectoryEntry *parent_entry = dir_table.GetEntry(parent_offset); + if (AMS_UNLIKELY(parent_entry == nullptr)) { + return; + } u32 cur_file_offset = parent_entry->file; while (cur_file_offset != EmptyEntry) { const FileEntry *cur_file = file_table.GetEntry(cur_file_offset); + if (AMS_UNLIKELY(cur_file == nullptr)) { + return; + } - this->AddFile(parent, std::make_unique(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, m_cur_source_type)); + this->AddFile(parent, std::unique_ptr(AllocateTyped(AllocationType_BuildFileContext, cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, m_cur_source_type))); cur_file_offset = cur_file->sibling; } @@ -414,8 +654,11 @@ namespace ams::mitm::fs { u32 next_child_offset = 0; { const DirectoryEntry *cur_child = dir_table.GetEntry(cur_child_offset); + if (AMS_UNLIKELY(cur_child == nullptr)) { + return; + } - this->AddDirectory(std::addressof(real_child), parent, std::make_unique(cur_child->name, cur_child->name_size)); + this->AddDirectory(std::addressof(real_child), parent, std::unique_ptr(AllocateTyped(AllocationType_BuildDirContext, cur_child->name, cur_child->name_size))); AMS_ABORT_UNLESS(real_child != nullptr); next_child_offset = cur_child->sibling; @@ -438,7 +681,7 @@ namespace ams::mitm::fs { /* If there is no romfs folder on the SD, don't bother continuing. */ { FsDir dir; - if (R_FAILED(mitm::fs::OpenAtmosphereRomfsDirectory(std::addressof(dir), m_program_id, m_root->path.get(), OpenDirectoryMode_Directory, std::addressof(sd_filesystem)))) { + if (R_FAILED(mitm::fs::OpenAtmosphereRomfsDirectory(std::addressof(dir), m_program_id, m_root->path, OpenDirectoryMode_Directory, std::addressof(sd_filesystem)))) { return; } fsDirClose(std::addressof(dir)); @@ -461,7 +704,7 @@ namespace ams::mitm::fs { this->VisitDirectory(m_root, 0x0, dir_table, file_table); } - void Builder::Build(std::vector *out_infos) { + void Builder::Build(SourceInfoVector *out_infos) { /* Clear output. */ out_infos->clear(); @@ -477,7 +720,7 @@ namespace ams::mitm::fs { m_file_hash_table_size = sizeof(u32) * num_file_hash_table_entries; /* Allocate metadata, make pointers. */ - Header *header = reinterpret_cast
(std::malloc(sizeof(Header))); + Header *header = reinterpret_cast
(AllocateTracked(AllocationType_Memory, sizeof(Header))); std::memset(header, 0x00, sizeof(*header)); /* Open metadata file. */ @@ -552,13 +795,13 @@ namespace ams::mitm::fs { /* Set all files' hash value = hash index. */ for (const auto &it : m_files) { BuildFileContext *cur_file = it.get(); - cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path.get(), 0, cur_file->path_len) % num_file_hash_table_entries; + cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path, 0, cur_file->path_len) % num_file_hash_table_entries; } /* Set all directories' hash value = hash index. */ for (const auto &it : m_directories) { BuildDirectoryContext *cur_dir = it.get(); - cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path.get(), 0, cur_dir->path_len) % num_dir_hash_table_entries; + cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path, 0, cur_dir->path_len) % num_dir_hash_table_entries; } /* Write hash tables. */ @@ -661,7 +904,7 @@ namespace ams::mitm::fs { const u32 name_size = cur_file->path_len; cur_entry->name_size = name_size; if (name_size) { - std::memcpy(cur_entry->name, cur_file->path.get(), name_size); + std::memcpy(cur_entry->name, cur_file->path, name_size); for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) { cur_entry->name[i] = 0; } @@ -688,9 +931,10 @@ namespace ams::mitm::fs { AMS_ABORT_UNLESS(path_needed_size <= sizeof(full_path)); cur_file->GetPath(full_path); - cur_file->path.reset(); + FreeTracked(AllocationType_FileName, cur_file->path, cur_file->path_len + 1); + cur_file->path = nullptr; - char *new_path = new char[path_needed_size]; + char *new_path = static_cast(AllocateTracked(AllocationType_FullPath, path_needed_size)); std::memcpy(new_path, full_path, path_needed_size); out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path); } @@ -719,7 +963,7 @@ namespace ams::mitm::fs { const u32 name_size = cur_dir->path_len; cur_entry->name_size = name_size; if (name_size) { - std::memcpy(cur_entry->name, cur_dir->path.get(), name_size); + std::memcpy(cur_entry->name, cur_dir->path, name_size); for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) { cur_entry->name[i] = 0; } @@ -751,6 +995,39 @@ namespace ams::mitm::fs { } } + Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) { + /* Baseline: use no dynamic heap. */ + *out_size = 0; + + /* If the process is not an application, we do not care about dynamic heap. */ + R_SUCCEED_IF(!is_application); + + /* First, we need to ensure that, if the game used dynamic heap, we clear it. */ + if (g_dynamic_app_heap.heap_size > 0) { + mitm::fs::FinalizeLayeredRomfsStorage(g_dynamic_heap_program_id); + + /* Free the heap. */ + g_dynamic_app_heap.Reset(); + g_dynamic_sys_heap.Reset(); + } + + /* Next, if we aren't going to end up building a romfs, we can ignore dynamic heap. */ + R_SUCCEED_IF(!status.IsProgramSpecific()); + + /* Only mitm if there is actually an override romfs. */ + R_SUCCEED_IF(!mitm::fs::HasSdRomfsContent(program_id)); + + /* Next, set the new program id for dynamic heap. */ + g_dynamic_heap_program_id = program_id; + g_dynamic_app_heap.heap_size = GetDynamicAppHeapSize(g_dynamic_heap_program_id); + g_dynamic_sys_heap.heap_size = GetDynamicSysHeapSize(g_dynamic_heap_program_id); + + /* Set output. */ + *out_size = g_dynamic_app_heap.heap_size; + + R_SUCCEED(); + } + } } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp index 98e74c430..5eeb7dc08 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp @@ -27,6 +27,52 @@ namespace ams::mitm::fs::romfs { Memory, }; + enum AllocationType { + AllocationType_FileName, + AllocationType_DirName, + AllocationType_FullPath, + AllocationType_SourceInfo, + AllocationType_BuildFileContext, + AllocationType_BuildDirContext, + AllocationType_TableCache, + AllocationType_DirPointerArray, + AllocationType_DirContextSet, + AllocationType_FileContextSet, + AllocationType_Memory, + + AllocationType_Count, + }; + + void *AllocateTracked(AllocationType type, size_t size); + void FreeTracked(AllocationType type, void *p, size_t size); + + template + T *AllocateTyped(AllocationType type, Args &&... args) { + void *mem = AllocateTracked(type, sizeof(T)); + return std::construct_at(static_cast(mem), std::forward(args)...); + } + + template + class TrackedAllocator { + public: + using value_type = T; + + template + struct rebind { + using other = TrackedAllocator; + }; + public: + TrackedAllocator() = default; + + T *allocate(size_t n) { + return static_cast(AllocateTracked(AllocType, sizeof(T) * n)); + } + + void deallocate(T *p, size_t n) { + FreeTracked(AllocType, p, sizeof(T) * n); + } + }; + struct SourceInfo { s64 virtual_offset; s64 size; @@ -89,10 +135,10 @@ namespace ams::mitm::fs::romfs { delete this->metadata_source_info.file; break; case DataSourceType::LooseSdFile: - delete[] this->loose_source_info.path; + FreeTracked(AllocationType_FullPath, this->loose_source_info.path, std::strlen(this->loose_source_info.path) + 1); break; case DataSourceType::Memory: - std::free(static_cast(this->memory_source_info.data)); + FreeTracked(AllocationType_Memory, this->memory_source_info.data, this->size); break; AMS_UNREACHABLE_DEFAULT_CASE(); } @@ -113,7 +159,7 @@ namespace ams::mitm::fs::romfs { NON_COPYABLE(BuildDirectoryContext); NON_MOVEABLE(BuildDirectoryContext); - std::unique_ptr path; + char *path; union { BuildDirectoryContext *parent; }; @@ -139,16 +185,28 @@ namespace ams::mitm::fs::romfs { struct RootTag{}; BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) { - this->path = std::make_unique(1); + this->path = static_cast(AllocateTracked(AllocationType_DirName, 1)); + this->path[0] = '\x00'; } BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) { this->path_len = entry_name_len; - this->path = std::unique_ptr(new char[this->path_len + 1]); - std::memcpy(this->path.get(), entry_name, entry_name_len); + this->path = static_cast(AllocateTracked(AllocationType_DirName, this->path_len + 1)); + std::memcpy(this->path, entry_name, entry_name_len); this->path[this->path_len] = '\x00'; } + ~BuildDirectoryContext() { + if (this->path != nullptr) { + FreeTracked(AllocationType_DirName, this->path, this->path_len + 1); + this->path = nullptr; + } + } + + void operator delete(void *p) { + FreeTracked(AllocationType_BuildDirContext, p, sizeof(BuildDirectoryContext)); + } + size_t GetPathLength() const { if (this->parent == nullptr) { return 0; @@ -165,7 +223,7 @@ namespace ams::mitm::fs::romfs { const size_t parent_len = this->parent->GetPath(dst); dst[parent_len] = '/'; - std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len); + std::memcpy(dst + parent_len + 1, this->path, this->path_len); dst[parent_len + 1 + this->path_len] = '\x00'; return parent_len + 1 + this->path_len; } @@ -187,7 +245,7 @@ namespace ams::mitm::fs::romfs { NON_COPYABLE(BuildFileContext); NON_MOVEABLE(BuildFileContext); - std::unique_ptr path; + char *path; BuildDirectoryContext *parent; union { BuildFileContext *sibling; @@ -203,11 +261,22 @@ namespace ams::mitm::fs::romfs { BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) { this->path_len = entry_name_len; - this->path = std::unique_ptr(new char[this->path_len + 1]); - std::memcpy(this->path.get(), entry_name, entry_name_len); + this->path = static_cast(AllocateTracked(AllocationType_FileName, this->path_len + 1)); + std::memcpy(this->path, entry_name, entry_name_len); this->path[this->path_len] = 0; } + ~BuildFileContext() { + if (this->path != nullptr) { + FreeTracked(AllocationType_FileName, this->path, this->path_len + 1); + this->path = nullptr; + } + } + + void operator delete(void *p) { + FreeTracked(AllocationType_BuildFileContext, p, sizeof(BuildFileContext)); + } + size_t GetPathLength() const { if (this->parent == nullptr) { return 0; @@ -224,7 +293,7 @@ namespace ams::mitm::fs::romfs { const size_t parent_len = this->parent->GetPath(dst); dst[parent_len] = '/'; - std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len); + std::memcpy(dst + parent_len + 1, this->path, this->path_len); dst[parent_len + 1 + this->path_len] = '\x00'; return parent_len + 1 + this->path_len; } @@ -248,6 +317,8 @@ namespace ams::mitm::fs::romfs { class Builder { NON_COPYABLE(Builder); NON_MOVEABLE(Builder); + public: + using SourceInfoVector = std::vector>; private: template struct Comparator { @@ -270,13 +341,13 @@ namespace ams::mitm::fs::romfs { } }; - template - using ContextSet = std::set, Comparator>; + template + using ContextSet = std::set, Comparator, TrackedAllocator>>; private: ncm::ProgramId m_program_id; BuildDirectoryContext *m_root; - ContextSet m_directories; - ContextSet m_files; + ContextSet m_directories; + ContextSet m_files; size_t m_num_dirs; size_t m_num_files; size_t m_dir_table_size; @@ -295,11 +366,14 @@ namespace ams::mitm::fs::romfs { void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr file_ctx); public: Builder(ncm::ProgramId pr_id); + ~Builder(); void AddSdFiles(); void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type); - void Build(std::vector *out_infos); + void Build(SourceInfoVector *out_infos); }; + Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application); + } diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.cpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.cpp new file mode 100644 index 000000000..3de64b402 --- /dev/null +++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 . + */ +#include +#include "../amsmitm_initialization.hpp" +#include "mitm_pm_module.hpp" +#include "mitm_pm_service.hpp" + +namespace ams::mitm::pm { + + namespace { + + constexpr sm::ServiceName PmServiceName = sm::ServiceName::Encode("mitm:pm"); + constexpr size_t PmMaxSessions = 1; + + constexpr size_t MaxServers = 1; + constexpr size_t MaxSessions = PmMaxSessions; + using ServerOptions = sf::hipc::DefaultServerManagerOptions; + sf::hipc::ServerManager g_server_manager; + + constinit sf::UnmanagedServiceObject g_pm_service_object; + + } + + void MitmModule::ThreadFunction(void *) { + /* Create bpc:ams. */ + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service_object.GetShared(), PmServiceName, PmMaxSessions)); + + /* Loop forever, servicing our services. */ + g_server_manager.LoopProcess(); + } + +} diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.hpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.hpp new file mode 100644 index 000000000..d4e061b8d --- /dev/null +++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 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 . + */ +#pragma once +#include +#include "../amsmitm_module.hpp" + +namespace ams::mitm::pm { + + DEFINE_MITM_MODULE_CLASS(0x1000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 2); + +} diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.cpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.cpp new file mode 100644 index 000000000..8428e74f2 --- /dev/null +++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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 . + */ +#include +#include "../amsmitm_initialization.hpp" +#include "mitm_pm_service.hpp" +#include "mitm_pm_service.hpp" +#include "../fs_mitm/fsmitm_romfs.hpp" + +namespace ams::mitm::pm { + + Result PmService::PrepareLaunchProgram(sf::Out out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) { + /* Default to zero heap. */ + *out = 0; + + /* Actually configure the required boost size for romfs. */ + R_TRY(mitm::fs::romfs::ConfigureDynamicHeap(out.GetPointer(), program_id, status, is_application)); + + /* TODO: Is there anything else we should do, while we have the opportunity? */ + R_SUCCEED(); + } + +} diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.hpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.hpp new file mode 100644 index 000000000..2d28ed182 --- /dev/null +++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 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 . + */ +#pragma once +#include + +namespace ams::mitm::pm { + + class PmService { + public: + Result PrepareLaunchProgram(sf::Out out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application); + }; + static_assert(impl::IsIPmInterface); + +} diff --git a/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp b/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp index 11f89bafe..0754f8a7b 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp @@ -27,7 +27,7 @@ namespace ams::mitm::ns { Result NsAmMitmService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) { /* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */ bool is_hbl = false; - if ((R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) { + if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) { nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast(application_id), static_cast(content_type)); R_SUCCEED(); } diff --git a/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp b/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp index aa9972f65..9568f9108 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp @@ -27,7 +27,7 @@ namespace ams::mitm::ns { Result NsDocumentService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) { /* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */ bool is_hbl = false; - if ((R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) { + if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) { nswebResolveApplicationContentPath(m_srv.get(), static_cast(application_id), static_cast(content_type)); R_SUCCEED(); } diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp index 07686ff77..0915a10ab 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp @@ -32,7 +32,7 @@ namespace ams::mitm::settings { SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward>(s), c) { if (m_client_info.program_id == ncm::SystemProgramId::Ns) { os::ProcessId application_process_id; - if (R_SUCCEEDED(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) { + if (R_SUCCEEDED(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) { m_locale = g_application_locale; m_is_valid_language = g_valid_language; m_is_valid_region = g_valid_region; @@ -61,8 +61,8 @@ namespace ams::mitm::settings { if (is_ns) { /* When NS asks for a locale, refresh to get the current application locale. */ - R_TRY(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))); - R_TRY(pm::info::GetProgramId(std::addressof(program_id), application_process_id)); + R_TRY(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))); + R_TRY(ams::pm::info::GetProgramId(std::addressof(program_id), application_process_id)); } m_locale = cfg::GetOverrideLocale(program_id); m_is_valid_language = settings::IsValidLanguageCode(m_locale.language_code); diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp index 68579bb40..c756c028f 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp @@ -29,7 +29,7 @@ namespace ams::mitm::settings { PortIndex_Count, }; - constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set"); + constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set"); constexpr sm::ServiceName SetSysMitmServiceName = sm::ServiceName::Encode("set:sys"); struct ServerOptions { diff --git a/stratosphere/fatal/fatal.json b/stratosphere/fatal/fatal.json index 70212ded5..45b718089 100644 --- a/stratosphere/fatal/fatal.json +++ b/stratosphere/fatal/fatal.json @@ -79,7 +79,7 @@ "svcReplyAndReceiveWithUserBuffer": "0x44", "svcCreateEvent": "0x45", "svcMapPhysicalMemoryUnsafe": "0x48", - "svcUnmapPhysicalMemoryUnsafe": "0x48", + "svcUnmapPhysicalMemoryUnsafe": "0x49", "svcSetUnsafeLimit": "0x4A", "svcReadWriteRegister": "0x4E", "svcDebugActiveProcess": "0x60", diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 03078706e..08db5be4e 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -69,7 +69,7 @@ namespace ams::fatal::srv { /* Neither heap nor insecure is available, so we're going to have to try to raid the unsafe pool. */ { /* First, increase the limit to an extremely high value. */ - size_t large_size = std::max(64_MB, FrameBufferRequiredSizeHeapAligned); + size_t large_size = std::max(128_MB, FrameBufferRequiredSizeHeapAligned); while (svc::ResultLimitReached::Includes(svc::SetUnsafeLimit(large_size))) { large_size *= 2; } diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 2f2d1b89a..db0648acc 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -244,6 +244,24 @@ namespace ams::pm::impl { /* If we fail after now, unpin. */ ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); }; + /* Ensure we can talk to mitm services. */ + { + AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized_mitm, false); + if (!s_initialized_mitm) { + mitm::pm::Initialize(); + s_initialized_mitm = true; + } + } + + /* Determine boost size for mitm. */ + u64 mitm_boost_size = 0; + R_TRY(mitm::pm::PrepareLaunchProgram(std::addressof(mitm_boost_size), program_info.program_id, override_status, is_application)); + + if (mitm_boost_size > 0 || is_application) { + R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(mitm_boost_size)); + } + ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } }; + /* Ensure resources are available. */ resource::WaitResourceAvailable(std::addressof(program_info)); @@ -713,4 +731,8 @@ namespace ams::pm::impl { R_RETURN(resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast(group), static_cast(resource))); } + Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) { + R_RETURN(resource::BoostSystemMemoryResourceLimitForMitm(boost_size)); + } + } diff --git a/stratosphere/pm/source/impl/pm_process_manager.hpp b/stratosphere/pm/source/impl/pm_process_manager.hpp index d2af22ac2..83ccc7a85 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.hpp +++ b/stratosphere/pm/source/impl/pm_process_manager.hpp @@ -55,5 +55,6 @@ namespace ams::pm::impl { Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out); Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out); Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource); + Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size); } diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp index 644a8bb0e..6c5805b5d 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.cpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -52,9 +52,16 @@ namespace ams::pm::resource { constinit os::SdkMutex g_resource_limit_lock; constinit os::NativeHandle g_resource_limit_handles[ResourceLimitGroup_Count]; constinit spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard; - constinit u64 g_system_memory_boost_size = 0; constinit u64 g_extra_threads_available[ResourceLimitGroup_Count]; + constinit os::SdkMutex g_system_memory_boost_lock; + constinit u64 g_system_memory_boost_size = 0; + constinit u64 g_system_memory_boost_size_for_mitm = 0; + + ALWAYS_INLINE u64 GetCurrentSystemMemoryBoostSize() { + return g_system_memory_boost_size + g_system_memory_boost_size_for_mitm; + } + constinit u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = { [ResourceLimitGroup_System] = { [svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */ @@ -220,6 +227,47 @@ namespace ams::pm::resource { R_SUCCEED(); } + Result BoostSystemMemoryResourceLimitLocked(u64 normal_boost, u64 mitm_boost) { + /* Check pre-conditions. */ + AMS_ASSERT(g_system_memory_boost_lock.IsLockedByCurrentThread()); + + /* Determine total boost. */ + const u64 boost_size = normal_boost + mitm_boost; + + /* Don't allow all application memory to be taken away. */ + R_UNLESS(boost_size <= g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize()); + + const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size; + { + std::scoped_lock lk(g_resource_limit_lock); + + if (hos::GetVersion() >= hos::Version_5_0_0) { + /* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */ + if (boost_size < GetCurrentSystemMemoryBoostSize()) { + R_TRY(svc::SetUnsafeLimit(boost_size)); + R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + } else { + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size)); + } + } else { + const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size; + if (boost_size < GetCurrentSystemMemoryBoostSize()) { + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size)); + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + } else { + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); + R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size)); + } + } + + g_system_memory_boost_size = normal_boost; + g_system_memory_boost_size_for_mitm = mitm_boost; + } + + R_SUCCEED(); + } + } /* Resource API. */ @@ -352,37 +400,19 @@ namespace ams::pm::resource { } Result BoostSystemMemoryResourceLimit(u64 boost_size) { - /* Don't allow all application memory to be taken away. */ - R_UNLESS(boost_size <= g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize()); + /* Ensure only one boost change happens at a time. */ + std::scoped_lock lk(g_system_memory_boost_lock); - const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size; - { - std::scoped_lock lk(g_resource_limit_lock); + /* Boost to the appropriate total amount. */ + R_RETURN(BoostSystemMemoryResourceLimitLocked(boost_size, g_system_memory_boost_size_for_mitm)); + } - if (hos::GetVersion() >= hos::Version_5_0_0) { - /* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */ - if (boost_size < g_system_memory_boost_size) { - R_TRY(svc::SetUnsafeLimit(boost_size)); - R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); - } else { - R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); - R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size)); - } - } else { - const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size; - if (boost_size < g_system_memory_boost_size) { - R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size)); - R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); - } else { - R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size)); - R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size)); - } - } + Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) { + /* Ensure only one boost change happens at a time. */ + std::scoped_lock lk(g_system_memory_boost_lock); - g_system_memory_boost_size = boost_size; - } - - R_SUCCEED(); + /* Boost to the appropriate total amount. */ + R_RETURN(BoostSystemMemoryResourceLimitLocked(g_system_memory_boost_size, boost_size)); } Result BoostApplicationThreadResourceLimit() { diff --git a/stratosphere/pm/source/impl/pm_resource_manager.hpp b/stratosphere/pm/source/impl/pm_resource_manager.hpp index bd59f28c8..029458203 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.hpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.hpp @@ -24,6 +24,8 @@ namespace ams::pm::resource { Result BoostApplicationThreadResourceLimit(); Result BoostSystemThreadResourceLimit(); + Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size); + os::NativeHandle GetResourceLimitHandle(ResourceLimitGroup group); os::NativeHandle GetResourceLimitHandle(const ldr::ProgramInfo *info);