fs.mitm: add and use memlet module to temporarily steal applet memory while building romfs images.

Starting in 20.0.0, the browser needs more applet memory to function, so we can't steal as much any more.
Thus, we now steal 14 MB on 20.0.0+ instead of 40MB.

However, since this reduces memory available for custom system modules, we are adjusting to compensate.
ams.mitm's heap size has been reduced from 32MB to 12MB (recovering 20MB).
In addition, fs.mitm now uses a new mechanism for stealing memory from the applet pool while romfs is being built.

On net, we are compromising:
    * Custom sysmodules lose memory available to them.
        On 19.0.0/AMS 1.8.0, there was 30 MB available for custom sysmodules.
        Stealing 14 MB instead of 40 MB, we lose 26 MB of that. Reducing ams.mitm's usage will gain us back 20.
        Nintendo also appears to...use 4 extra MB, in 20.0.0, from my test homebrew.
        So on 20.0.0/AMS 1.9.0, there should be 20 MB available for custom sysmodules.
        On the bright side, on <20.0.0/AMS 1.9.0, I guess there will be 50 MB available for custom sysmodules now?
    * totk mods will lose the ability to...put every file in the romfs on sd card. There will be some unknown maximum filecount for totk mods.
        On the bright side, implementing the transient memory stealing should improve compatibility for some mods which strictly add files?
This commit is contained in:
Michael Scire
2025-05-02 20:17:16 -07:00
parent d0cb9b0eb7
commit 0ecc35c062
19 changed files with 672 additions and 12 deletions

View File

@@ -17,6 +17,7 @@
#include "../amsmitm_fs_utils.hpp"
#include "fsmitm_romfs.hpp"
#include "fsmitm_layered_romfs_storage.hpp"
#include "memlet/memlet.h"
namespace ams::mitm::fs {
@@ -26,6 +27,8 @@ namespace ams::mitm::fs {
namespace {
constexpr size_t MaximumRomfsBuildAppletMemorySize = 32_MB;
struct ApplicationWithDynamicHeapInfo {
ncm::ProgramId program_id;
size_t dynamic_app_heap_size;
@@ -41,7 +44,7 @@ namespace ams::mitm::fs {
/* Fire Emblem: Engage. */
/* Requirement ~32+ MB. */
/* No particular heap sensitivity. */
{ 0x0100A6301214E000, 16_MB, 0_MB },
{ 0x0100A6301214E000, 20_MB, 0_MB },
/* The Legend of Zelda: Tears of the Kingdom. */
/* Requirement ~48 MB. */
@@ -85,10 +88,12 @@ namespace ams::mitm::fs {
/* 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);
AMS_ABORT_UNLESS(this->heap_address != 0 || this->heap_size == 0);
/* Create heap. */
util::ConstructAt(this->heap, reinterpret_cast<void *>(this->heap_address), this->heap_size);
if (this->heap_size > 0) {
util::ConstructAt(this->heap, reinterpret_cast<void *>(this->heap_address), this->heap_size);
}
}
}
@@ -156,6 +161,52 @@ namespace ams::mitm::fs {
R_RETURN(os::SetMemoryHeapSize(0));
}
constinit os::SharedMemoryType g_applet_shared_memory;
Result MapAppletMemory(uintptr_t *out, size_t &size) {
/* Ensure that we can try to get a native handle for the shared memory. */
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized_memlet, false);
if (AMS_UNLIKELY(!s_initialized_memlet)) {
R_ABORT_UNLESS(::memletInitialize());
s_initialized_memlet = true;
}
/* Try to get a shared handle for the memory. */
::Handle shmem_handle = INVALID_HANDLE;
u64 shmem_size = 0;
if (R_FAILED(::memletCreateAppletSharedMemory(std::addressof(shmem_handle), std::addressof(shmem_size), size))) {
/* If we fail, set the heap size to 0. */
size = 0;
R_SUCCEED();
}
/* Set the output size. */
size = shmem_size;
/* Setup the shared memory. */
os::AttachSharedMemory(std::addressof(g_applet_shared_memory), shmem_size, shmem_handle, true);
/* Map the shared memory. */
void *mem = os::MapSharedMemory(std::addressof(g_applet_shared_memory), os::MemoryPermission_ReadWrite);
AMS_ABORT_UNLESS(mem != nullptr);
/* Set the output. */
*out = reinterpret_cast<uintptr_t>(mem);
R_SUCCEED();
}
Result UnmapAppletMemory(uintptr_t, size_t) {
/* Check that it's possible for us to unmap. */
AMS_ABORT_UNLESS(os::GetSharedMemoryHandle(std::addressof(g_applet_shared_memory)) != os::InvalidNativeHandle);
/* Unmap. */
os::DestroySharedMemory(std::addressof(g_applet_shared_memory));
/* Check that we unmapped successfully. */
AMS_ABORT_UNLESS(os::GetSharedMemoryHandle(std::addressof(g_applet_shared_memory)) == os::InvalidNativeHandle);
R_SUCCEED();
}
/* Dynamic allocation globals. */
constinit os::SdkMutex g_romfs_build_lock;
constinit ncm::ProgramId g_dynamic_heap_program_id{};
@@ -164,6 +215,7 @@ namespace ams::mitm::fs {
constinit DynamicHeap<os::AllocateUnsafeMemory, os::FreeUnsafeMemory> g_dynamic_app_heap;
constinit DynamicHeap<MapByHeap, UnmapByHeap> g_dynamic_sys_heap;
constinit DynamicHeap<MapAppletMemory, UnmapAppletMemory> g_dynamic_let_heap;
void InitializeDynamicHeapForBuildRomfs(ncm::ProgramId program_id) {
if (program_id == g_dynamic_heap_program_id && g_dynamic_app_heap.heap_size > 0) {
@@ -175,6 +227,10 @@ namespace ams::mitm::fs {
if (g_dynamic_sys_heap.heap_size > 0) {
g_dynamic_sys_heap.Map();
}
if (g_dynamic_let_heap.heap_size > 0) {
g_dynamic_let_heap.Map();
}
}
}
@@ -183,15 +239,33 @@ namespace ams::mitm::fs {
g_building_from_dynamic_heap = false;
g_dynamic_app_heap.TryRelease();
g_dynamic_let_heap.Reset();
}
constexpr bool CanAllocateFromDynamicAppletHeap(AllocationType type) {
switch (type) {
case AllocationType_FullPath:
case AllocationType_SourceInfo:
case AllocationType_Memory:
case AllocationType_TableCache:
return false;
default:
return true;
}
}
}
void *AllocateTracked(AllocationType type, size_t size) {
AMS_UNUSED(type);
if (g_building_from_dynamic_heap) {
void *ret = g_dynamic_app_heap.Allocate(size);
void *ret = nullptr;
if (CanAllocateFromDynamicAppletHeap(type) && g_dynamic_let_heap.heap_address != 0) {
ret = g_dynamic_let_heap.Allocate(size);
}
if (ret == nullptr && g_dynamic_app_heap.heap_address != 0) {
ret = g_dynamic_app_heap.Allocate(size);
}
if (ret == nullptr && g_dynamic_sys_heap.heap_address != 0) {
ret = g_dynamic_sys_heap.Allocate(size);
@@ -211,7 +285,11 @@ namespace ams::mitm::fs {
AMS_UNUSED(type);
AMS_UNUSED(size);
if (g_dynamic_app_heap.TryFree(p)) {
if (g_dynamic_let_heap.TryFree(p)) {
if (!g_building_from_dynamic_heap) {
g_dynamic_let_heap.TryRelease();
}
} else if (g_dynamic_app_heap.TryFree(p)) {
if (!g_building_from_dynamic_heap) {
g_dynamic_app_heap.TryRelease();
}
@@ -1021,6 +1099,7 @@ namespace ams::mitm::fs {
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);
g_dynamic_let_heap.heap_size = MaximumRomfsBuildAppletMemorySize;
/* Set output. */
*out_size = g_dynamic_app_heap.heap_size;