From 576f1c43a4cfe0378ddbb27a655cfb439e606f47 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 11 May 2023 23:39:02 -0700 Subject: [PATCH] romfs: zelda is a blight upon this earth --- stratosphere/ams_mitm/source/amsmitm_main.cpp | 2 +- .../ams_mitm/source/fs_mitm/fsmitm_romfs.cpp | 208 ++++++++++++------ .../pm/source/impl/pm_process_manager.cpp | 2 +- .../pm/source/impl/pm_resource_manager.cpp | 2 +- 4 files changed, 147 insertions(+), 67 deletions(-) diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index d52b6c595..97e735f6e 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -24,7 +24,7 @@ namespace ams { namespace { /* TODO: we really shouldn't be using malloc just to avoid dealing with real allocator separation. */ - constexpr size_t MallocBufferSize = 16_MB; + constexpr size_t MallocBufferSize = 32_MB; alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize]; } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp index fac9aa3b6..62fd74ef3 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -28,80 +28,161 @@ namespace ams::mitm::fs { struct ApplicationWithDynamicHeapInfo { ncm::ProgramId program_id; - size_t dynamic_heap_size; + size_t dynamic_app_heap_size; + size_t dynamic_system_heap_size; }; constexpr const ApplicationWithDynamicHeapInfo ApplicationsWithDynamicHeap[] = { - { 0x01006F8002326000, 32_MB }, /* Animal Crossing: New Horizons. */ - { 0x0100A6301214E000, 40_MB }, /* Fire Emblem: Engage. */ + /* 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 GetDynamicHeapSize(ncm::ProgramId program_id) { + constexpr size_t GetDynamicAppHeapSize(ncm::ProgramId program_id) { for (const auto &info : ApplicationsWithDynamicHeap) { if (info.program_id == program_id) { - return info.dynamic_heap_size; + 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 size_t g_dynamic_heap_size = 0; - constinit os::SdkMutex g_release_dynamic_heap_lock; constinit bool g_building_from_dynamic_heap = false; - constinit uintptr_t g_dynamic_heap_address = 0; - constinit size_t g_dynamic_heap_outstanding_allocations = 0; - constinit util::TypedStorage g_dynamic_heap{}; - - bool IsAllocatedFromDynamicHeap(void *p) { - const uintptr_t address = reinterpret_cast(p); - - return g_dynamic_heap_address != 0 && (g_dynamic_heap_address <= address && address < g_dynamic_heap_address + g_dynamic_heap_size); - } + 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_heap_size > 0) { + 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; - if (g_dynamic_heap_address == 0) { - /* NOTE: Lock not necessary, because this is the only location which do 0 -> non-zero. */ + g_dynamic_app_heap.Map(); - /* Map application memory as heap. */ - R_ABORT_UNLESS(os::AllocateUnsafeMemory(std::addressof(g_dynamic_heap_address), g_dynamic_heap_size)); - AMS_ABORT_UNLESS(g_dynamic_heap_address != 0); - - /* Create exp heap. */ - util::ConstructAt(g_dynamic_heap, reinterpret_cast(g_dynamic_heap_address), g_dynamic_heap_size); + if (g_dynamic_sys_heap.heap_size > 0) { + g_dynamic_sys_heap.Map(); } } } - void ReleaseDynamicHeap() { - std::scoped_lock lk(g_release_dynamic_heap_lock); - - if (g_dynamic_heap_address != 0) { - util::DestroyAt(g_dynamic_heap); - g_dynamic_heap = {}; - - R_ABORT_UNLESS(os::FreeUnsafeMemory(g_dynamic_heap_address, g_dynamic_heap_size)); - - g_dynamic_heap_address = 0; - } - } - void FinalizeDynamicHeapForBuildRomfs() { /* We are definitely no longer building out of dynamic heap. */ g_building_from_dynamic_heap = false; - if (g_dynamic_heap_outstanding_allocations == 0) { - ReleaseDynamicHeap(); - } + g_dynamic_app_heap.TryRelease(); } } @@ -110,10 +191,15 @@ namespace ams::mitm::fs { AMS_UNUSED(type); if (g_building_from_dynamic_heap) { - void * const ret = util::GetReference(g_dynamic_heap).Allocate(size); - AMS_ABORT_UNLESS(ret != nullptr); + void *ret = g_dynamic_app_heap.Allocate(size); - ++g_dynamic_heap_outstanding_allocations; + 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 { @@ -125,12 +211,13 @@ namespace ams::mitm::fs { AMS_UNUSED(type); AMS_UNUSED(size); - if (IsAllocatedFromDynamicHeap(p)) { - --g_dynamic_heap_outstanding_allocations; - util::GetReference(g_dynamic_heap).Free(p); - - if (!g_building_from_dynamic_heap && g_dynamic_heap_outstanding_allocations == 0) { - ReleaseDynamicHeap(); + 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); @@ -916,20 +1003,12 @@ namespace ams::mitm::fs { R_SUCCEED_IF(!is_application); /* First, we need to ensure that, if the game used dynamic heap, we clear it. */ - if (g_dynamic_heap_size > 0) { + if (g_dynamic_app_heap.heap_size > 0) { mitm::fs::FinalizeLayeredRomfsStorage(g_dynamic_heap_program_id); - /* This should have freed any remaining allocations. */ - AMS_ABORT_UNLESS(g_dynamic_heap_outstanding_allocations == 0); - /* Free the heap. */ - if (g_dynamic_heap_address != 0) { - ReleaseDynamicHeap(); - } - - /* Clear the heap globals. */ - g_dynamic_heap_address = 0; - g_dynamic_heap_size = 0; + 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. */ @@ -939,11 +1018,12 @@ namespace ams::mitm::fs { 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_heap_size = GetDynamicHeapSize(g_dynamic_heap_program_id); + 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_heap_size; + *out_size = g_dynamic_app_heap.heap_size; R_SUCCEED(); } diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 040d027d3..db0648acc 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -259,8 +259,8 @@ namespace ams::pm::impl { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(mitm_boost_size)); - ON_RESULT_FAILURE_2 { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); }; } + 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)); diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp index ff016dafb..6c5805b5d 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.cpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -33,7 +33,7 @@ namespace ams::pm::resource { constexpr size_t ReservedMemorySize600 = 5_MB; /* Atmosphere always allocates extra memory for system usage. */ - constexpr size_t ExtraSystemMemorySizeAtmosphere = 24_MB; + constexpr size_t ExtraSystemMemorySizeAtmosphere = 32_MB; /* Desired extra threads. */ constexpr u64 BaseApplicationThreads = 96;