From 0ecc35c062c4e40fa7165e0837c5db7d04bc3729 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 2 May 2025 20:17:16 -0700 Subject: [PATCH] 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? --- atmosphere.mk | 3 + .../nintendo/nx/kern_k_system_control.cpp | 4 +- .../impl/ams_system_thread_definitions.hpp | 2 + .../ncm/ncm_system_content_meta_id.hpp | 4 +- .../boot2/boot2_api.board.nintendo_nx.cpp | 3 + .../impl/os_shared_memory_impl.os.horizon.cpp | 1 + stratosphere/ams_mitm/source/amsmitm_main.cpp | 2 +- .../ams_mitm/source/fs_mitm/fsmitm_romfs.cpp | 93 ++++++++++++- .../ams_mitm/source/fs_mitm/memlet/memlet.c | 41 ++++++ .../ams_mitm/source/fs_mitm/memlet/memlet.h | 32 +++++ .../source/fs_mitm/memlet/service_guard.h | 65 +++++++++ stratosphere/memlet/Makefile | 41 ++++++ stratosphere/memlet/memlet.json | 93 +++++++++++++ stratosphere/memlet/source/memlet_main.cpp | 75 ++++++++++ stratosphere/memlet/source/memlet_service.cpp | 58 ++++++++ stratosphere/memlet/source/memlet_service.hpp | 32 +++++ stratosphere/memlet/system_module.mk | 131 ++++++++++++++++++ stratosphere/pm/source/impl/pm_spec.cpp | 2 +- stratosphere/stratosphere.mk | 2 +- 19 files changed, 672 insertions(+), 12 deletions(-) create mode 100644 stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.c create mode 100644 stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.h create mode 100644 stratosphere/ams_mitm/source/fs_mitm/memlet/service_guard.h create mode 100644 stratosphere/memlet/Makefile create mode 100644 stratosphere/memlet/memlet.json create mode 100644 stratosphere/memlet/source/memlet_main.cpp create mode 100644 stratosphere/memlet/source/memlet_service.cpp create mode 100644 stratosphere/memlet/source/memlet_service.hpp create mode 100644 stratosphere/memlet/system_module.mk diff --git a/atmosphere.mk b/atmosphere.mk index cca6fc106..846ca1c22 100644 --- a/atmosphere.mk +++ b/atmosphere.mk @@ -50,6 +50,7 @@ dist: dist-no-debug cp $(CURRENT_DIRECTORY)/stratosphere/sm/$(ATMOSPHERE_OUT_DIR)/sm.elf $(DIST_DIR)/sm.elf cp $(CURRENT_DIRECTORY)/stratosphere/spl/$(ATMOSPHERE_OUT_DIR)/spl.elf $(DIST_DIR)/spl.elf cp $(CURRENT_DIRECTORY)/stratosphere/TioServer/$(ATMOSPHERE_OUT_DIR)/TioServer.elf $(DIST_DIR)/TioServer.elf + cp $(CURRENT_DIRECTORY)/stratosphere/memlet/$(ATMOSPHERE_OUT_DIR)/memlet.elf $(DIST_DIR)/memlet.elf cp $(CURRENT_DIRECTORY)/troposphere/daybreak/daybreak.elf $(DIST_DIR)/daybreak.elf cp $(CURRENT_DIRECTORY)/troposphere/haze/haze.elf $(DIST_DIR)/haze.elf cp $(CURRENT_DIRECTORY)/troposphere/reboot_to_payload/reboot_to_payload.elf $(DIST_DIR)/reboot_to_payload.elf @@ -87,6 +88,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR) #mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000042 mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000420 + mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000421 mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000b240 mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000d609 mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000d623 @@ -104,6 +106,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR) cp stratosphere/htc/$(ATMOSPHERE_OUT_DIR)/htc.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000b240/exefs.nsp cp stratosphere/dmnt.gen2/$(ATMOSPHERE_OUT_DIR)/dmnt.gen2.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000d609/exefs.nsp cp stratosphere/TioServer/$(ATMOSPHERE_OUT_DIR)/TioServer.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000d623/exefs.nsp + cp stratosphere/memlet/$(ATMOSPHERE_OUT_DIR)/memlet.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000421/exefs.nsp @build_romfs $(DIST_DIR)/stratosphere_romfs $(DIST_DIR)/atmosphere/stratosphere.romfs rm -r $(DIST_DIR)/stratosphere_romfs cp troposphere/reboot_to_payload/reboot_to_payload.nro $(DIST_DIR)/switch/reboot_to_payload.nro diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 22e99faf0..58e86f152 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -361,7 +361,9 @@ namespace ams::kern::board::nintendo::nx { }(); /* Return (possibly) adjusted size. */ - constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB; + /* NOTE: On 20.0.0+ the browser requires much more memory in the applet pool in order to function. */ + /* Thus, we have to reduce our extra system memory size by 26 MB to compensate. */ + const size_t ExtraSystemMemoryForAtmosphere = kern::GetTargetFirmware() >= ams::TargetFirmware_20_0_0 ? 14_MB : 40_MB; return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize; } diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index d51eb90e8..ca1c5cb5d 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -171,6 +171,8 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(21, TioServer, FileServerHtcsServer); AMS_DEFINE_SYSTEM_THREAD(21, TioServer, SdCardObserver); + AMS_DEFINE_SYSTEM_THREAD(16, memlet, Main); + /* ServiceProfile */ AMS_DEFINE_SYSTEM_THREAD(-1, sprofile, IpcServer); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp index b02c38fc5..b8e7bcd41 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_system_content_meta_id.hpp @@ -121,13 +121,15 @@ namespace ams::ncm { static const AtmosphereProgramId Mitm; static const AtmosphereProgramId AtmosphereLogManager; + static const AtmosphereProgramId AtmosphereMemlet; }; inline constexpr const AtmosphereProgramId AtmosphereProgramId::Mitm = { 0x010041544D530000ul }; inline constexpr const AtmosphereProgramId AtmosphereProgramId::AtmosphereLogManager = { 0x0100000000000420ul }; + inline constexpr const AtmosphereProgramId AtmosphereProgramId::AtmosphereMemlet = { 0x0100000000000421ul }; inline constexpr bool IsAtmosphereProgramId(const ProgramId &program_id) { - return program_id == AtmosphereProgramId::Mitm || program_id == AtmosphereProgramId::AtmosphereLogManager; + return program_id == AtmosphereProgramId::Mitm || program_id == AtmosphereProgramId::AtmosphereLogManager || program_id == AtmosphereProgramId::AtmosphereMemlet; } inline constexpr bool IsSystemProgramId(const AtmosphereProgramId &) { diff --git a/libraries/libstratosphere/source/boot2/boot2_api.board.nintendo_nx.cpp b/libraries/libstratosphere/source/boot2/boot2_api.board.nintendo_nx.cpp index e5f27fce6..5f7f296a6 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.board.nintendo_nx.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.board.nintendo_nx.cpp @@ -450,6 +450,9 @@ namespace ams::boot2 { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Migration, ncm::StorageId::BuiltInSystem), 0); } + /* Launch atmosphere's applet memory service program. */ + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::AtmosphereProgramId::AtmosphereMemlet, ncm::StorageId::None), 0); + /* Launch user programs off of the SD. */ LaunchFlaggedProgramsOnSdCard(); } diff --git a/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp index b336576f1..94450ef26 100644 --- a/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_shared_memory_impl.os.horizon.cpp @@ -43,6 +43,7 @@ namespace ams::os::impl { R_TRY_CATCH(svc::CreateSharedMemory(std::addressof(handle), size, svc_my_perm, svc_other_perm)) { R_CONVERT(svc::ResultOutOfHandles, os::ResultOutOfHandles()) R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource()) + R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory()) } R_END_TRY_CATCH_WITH_ABORT_UNLESS; *out = handle; diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index 97e735f6e..9e0538135 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 = 32_MB; + constexpr size_t MallocBufferSize = 12_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 62fd74ef3..025fcaba3 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -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(this->heap_address), this->heap_size); + if (this->heap_size > 0) { + util::ConstructAt(this->heap, reinterpret_cast(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(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 g_dynamic_app_heap; constinit DynamicHeap g_dynamic_sys_heap; + constinit DynamicHeap 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; diff --git a/stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.c b/stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.c new file mode 100644 index 000000000..ad14ebb2a --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.c @@ -0,0 +1,41 @@ +/* + * 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 "memlet.h" + +static Service g_memletSrv; + +NX_GENERATE_SERVICE_GUARD(memlet); + +Result _memletInitialize(void) { + return smGetService(&g_memletSrv, "memlet"); +} + +void _memletCleanup(void) { + serviceClose(&g_memletSrv); +} + +Service* memletGetServiceSession(void) { + return &g_memletSrv; +} + +Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size) { + return serviceDispatchInOut(&g_memletSrv, 65000, desired_size, *out_size, + .out_handle_attrs = { SfOutHandleAttr_HipcMove }, + .out_handles = out_shmem_h, + ); +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.h b/stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.h new file mode 100644 index 000000000..218761936 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/memlet/memlet.h @@ -0,0 +1,32 @@ +/* + * 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 + +Result memletInitialize(void); +void memletExit(void); +Service* memletGetServiceSession(void); + +Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size); + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/ams_mitm/source/fs_mitm/memlet/service_guard.h b/stratosphere/ams_mitm/source/fs_mitm/memlet/service_guard.h new file mode 100644 index 000000000..d7ce9a7a9 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/memlet/service_guard.h @@ -0,0 +1,65 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +typedef struct ServiceGuard { + Mutex mutex; + u32 refCount; +} ServiceGuard; + +NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g) +{ + mutexLock(&g->mutex); + return (g->refCount++) == 0; +} + +NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void)) +{ + if (R_FAILED(rc)) { + cleanupFunc(); + --g->refCount; + } + mutexUnlock(&g->mutex); + return rc; +} + +NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void)) +{ + mutexLock(&g->mutex); + if (g->refCount && (--g->refCount) == 0) + cleanupFunc(); + mutexUnlock(&g->mutex); +} + +#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \ +\ +static ServiceGuard g_##name##Guard; \ +NX_INLINE Result _##name##Initialize _paramdecl; \ +static void _##name##Cleanup(void); \ +\ +Result name##Initialize _paramdecl \ +{ \ + Result rc = 0; \ + if (serviceGuardBeginInit(&g_##name##Guard)) \ + rc = _##name##Initialize _parampass; \ + return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \ +} \ +\ +void name##Exit(void) \ +{ \ + serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \ +} + +#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ()) + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/memlet/Makefile b/stratosphere/memlet/Makefile new file mode 100644 index 000000000..d681240e5 --- /dev/null +++ b/stratosphere/memlet/Makefile @@ -0,0 +1,41 @@ +ATMOSPHERE_BUILD_CONFIGS := +all: nx_release + +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): + @echo "Building $(strip $1)" + @$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5) + +clean-$(strip $1): + @echo "Cleaning $(strip $1)" + @$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5) + +endef + +define ATMOSPHERE_ADD_TARGETS + +$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \ + ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \ + ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \ + ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \ +)) + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,)) + +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config)) diff --git a/stratosphere/memlet/memlet.json b/stratosphere/memlet/memlet.json new file mode 100644 index 000000000..8f44c0ce2 --- /dev/null +++ b/stratosphere/memlet/memlet.json @@ -0,0 +1,93 @@ +{ + "name": "memlet", + "title_id": "0x0100000000000421", + "title_id_range_min": "0x0100000000000421", + "title_id_range_max": "0x0100000000000421", + "main_thread_stack_size": "0x00002000", + "main_thread_priority": 44, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 1, + "is_64_bit": true, + "address_space_type": 3, + "disable_device_address_space_merge": true, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["fatal:u"], + "service_host": ["memlet"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcSynchronizePreemptionState": "0x36", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateSharedMemory": "0x50", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "application_type", + "value": 2 + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/memlet/source/memlet_main.cpp b/stratosphere/memlet/source/memlet_main.cpp new file mode 100644 index 000000000..ea0ee7c10 --- /dev/null +++ b/stratosphere/memlet/source/memlet_main.cpp @@ -0,0 +1,75 @@ +/* + * 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 "memlet_service.hpp" + +namespace ams { + + namespace memlet { + + namespace { + + using ServerOptions = sf::hipc::DefaultServerManagerOptions; + + constexpr sm::ServiceName MemServiceName = sm::ServiceName::Encode("memlet"); + constexpr size_t MemMaxSessions = 1; + + /* memlet. */ + constexpr size_t NumServers = 1; + constexpr size_t NumSessions = MemMaxSessions; + + sf::hipc::ServerManager g_server_manager; + + constinit sf::UnmanagedServiceObject g_mem_service_object; + + void InitializeAndLoopIpcServer() { + /* Create services. */ + R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_mem_service_object.GetShared(), MemServiceName, MemMaxSessions)); + + /* Loop forever, servicing our services. */ + g_server_manager.LoopProcess(); + } + + } + + } + + namespace init { + + void InitializeSystemModule() { + /* Initialize our connection to sm. */ + R_ABORT_UNLESS(sm::Initialize()); + + /* Verify that we can sanely execute. */ + ams::CheckApiVersion(); + } + + void FinalizeSystemModule() { /* ... */ } + + void Startup() { /* ... */ } + + } + + void Main() { + /* Set thread name. */ + os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(memlet, Main)); + AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(memlet, Main)); + + /* Initialize and service our ipc service. */ + memlet::InitializeAndLoopIpcServer(); + } + +} diff --git a/stratosphere/memlet/source/memlet_service.cpp b/stratosphere/memlet/source/memlet_service.cpp new file mode 100644 index 000000000..ffccc6e8e --- /dev/null +++ b/stratosphere/memlet/source/memlet_service.cpp @@ -0,0 +1,58 @@ +/* + * 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 "memlet_service.hpp" + +namespace ams::memlet { + + Result Service::CreateAppletSharedMemory(sf::Out out_size, sf::OutMoveHandle out_handle, u64 desired_size) { + /* Create a handle to set the output to when done. */ + os::NativeHandle handle = os::InvalidNativeHandle; + ON_SCOPE_EXIT { out_handle.SetValue(handle, true); }; + + /* Check that the requested size has megabyte alignment and isn't too big. */ + R_UNLESS(util::IsAligned(desired_size, 1_MB), os::ResultInvalidParameter()); + R_UNLESS(desired_size <= 128_MB, os::ResultInvalidParameter()); + + /* Try to create a shared memory of the desired size, giving up 1 MB each iteration. */ + os::SharedMemoryType shmem = {}; + while (true) { + /* If we have zero desired-size left, we've failed. */ + R_UNLESS(desired_size > 0, os::ResultOutOfMemory()); + + /* Try to create a shared memory. */ + if (R_FAILED(os::CreateSharedMemory(std::addressof(shmem), desired_size, os::MemoryPermission_ReadWrite, os::MemoryPermission_ReadWrite))) { + /* We failed, so decrease the size to see if that works. */ + desired_size -= 1_MB; + continue; + } + + /* We successfully created the shared memory. */ + break; + } + + /* Get the native handle for the shared memory we created. */ + handle = os::GetSharedMemoryHandle(std::addressof(shmem)); + + /* HACK: Clear the shared memory object, since we've stolen its handle, and there's no "correct" way to detach. */ + shmem = {}; + + /* We successfully created a shared memory! */ + *out_size = desired_size; + R_SUCCEED(); + } + +} diff --git a/stratosphere/memlet/source/memlet_service.hpp b/stratosphere/memlet/source/memlet_service.hpp new file mode 100644 index 000000000..6d003753e --- /dev/null +++ b/stratosphere/memlet/source/memlet_service.hpp @@ -0,0 +1,32 @@ +/* + * 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 + +#define AMS_MEMLET_I_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 65000, Result, CreateAppletSharedMemory, (sf::Out out_size, sf::OutMoveHandle out_handle, u64 desired_size), (out_size, out_handle, desired_size)) + +AMS_SF_DEFINE_INTERFACE(ams::memlet::impl, IService, AMS_MEMLET_I_SERVICE_INTERFACE_INFO, 0x00000000) + +namespace ams::memlet { + + class Service { + public: + Result CreateAppletSharedMemory(sf::Out out_size, sf::OutMoveHandle out_handle, u64 desired_size); + }; + static_assert(impl::IsIService); + +} diff --git a/stratosphere/memlet/system_module.mk b/stratosphere/memlet/system_module.mk new file mode 100644 index 000000000..d6de42efc --- /dev/null +++ b/stratosphere/memlet/system_module.mk @@ -0,0 +1,131 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +ATMOSPHERE_SYSTEM_MODULE_TARGETS := nsp + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(__RECURSIVE__),1) +#--------------------------------------------------------------------------------- + +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: clean all check_lib + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + @$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(THIS_MAKEFILE) + +$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib + @$(SILENTCMD)echo "Checked library." + +ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1) +check_lib: +else +check_lib: + @$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk +endif + +$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target)) + +$(OUTPUT).kip : $(OUTPUT).elf +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + +%.npdm : %.npdm.json + @echo built ... $< $@ + @npdmtool $< $@ + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/pm/source/impl/pm_spec.cpp b/stratosphere/pm/source/impl/pm_spec.cpp index 9127d2e6a..ee0e38060 100644 --- a/stratosphere/pm/source/impl/pm_spec.cpp +++ b/stratosphere/pm/source/impl/pm_spec.cpp @@ -82,7 +82,7 @@ namespace ams::pm::impl { [svc::LimitableResource_ThreadCountMax] = BaseAppletThreads, [svc::LimitableResource_EventCountMax] = 0, [svc::LimitableResource_TransferMemoryCountMax] = 32, - [svc::LimitableResource_SessionCountMax] = 5, + [svc::LimitableResource_SessionCountMax] = 5 + 1, /* Add a session for atmosphere's memlet system module. */ }, }; diff --git a/stratosphere/stratosphere.mk b/stratosphere/stratosphere.mk index 5b06a6d98..b20244ec6 100644 --- a/stratosphere/stratosphere.mk +++ b/stratosphere/stratosphere.mk @@ -5,7 +5,7 @@ THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) include $(CURRENT_DIRECTORY)/../libraries/config/common.mk -ALL_MODULES := loader boot ncm pm sm ams_mitm spl eclct.stub ro creport fatal dmnt boot2 erpt pgl jpegdec LogManager cs htc TioServer dmnt.gen2 +ALL_MODULES := loader boot ncm pm sm ams_mitm spl eclct.stub ro creport fatal dmnt boot2 erpt pgl jpegdec LogManager cs htc TioServer dmnt.gen2 memlet all: $(ALL_MODULES)