diff --git a/README.md b/README.md index 8880e441..b0aba0d7 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Overclocking suite for Nintendo Switch™ Horizon OS (HOS) running on Atmosphere This project will not be actively maintained or regularly updated along with Atmosphere CFW. +For users in China mainland facing connection or downloading issues on GitHub, go to [Gitee mirror](https://gitee.com/kazushi/Switch-OC-Suite/). + ## DISCLAIMER: USE AT YOUR OWN RISK! @@ -176,9 +178,9 @@ Patched sysmodules would be persistent until pcv or ptm was updated in new HOS ( Grab necessary patches from the repo, then compile sys-clk, ReverseNX-RT and Atmosphere loader with devkitpro. -If you are to install nro forwarders, remove `R_TRY(ValidateAcidSignature(std::addressof(g_original_meta_cache.meta)));` in `Atmosphere/stratosphere/loader/source/ldr_meta.cpp` to make them work again. +If you are to install nro forwarders, stub `ValidateAcidSignature()` with `R_SUCCEED();` in `Atmosphere/stratosphere/loader/source/ldr_meta.cpp` to make them work again. -Uncompress the kip to make it work with config editor: `hactool -t kip1 Atmosphere/stratosphere/loader/loader.kip --uncompress=Atmosphere/stratosphere/loader/loader.kip` +Uncompress the kip to make it work with config editor: `hactool -t kip1 Atmosphere/stratosphere/loader/out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=./loader.kip` diff --git a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp index 02222352..8c2b3383 100644 --- a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see . */ #include -#include "ldr_auto_close.hpp" #include "ldr_capabilities.hpp" #include "ldr_content_management.hpp" #include "ldr_development_manager.hpp" @@ -87,9 +86,6 @@ namespace ams::ldr { bool g_is_pcv; bool g_is_ptm; - /* Anti-downgrade. */ - #include "ldr_anti_downgrade_tables.inc" - Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) { /* No version verification is done before 8.1.0. */ R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0); @@ -97,41 +93,10 @@ namespace ams::ldr { /* No verification is done if development. */ R_SUCCEED_IF(IsDevelopmentForAntiDowngradeCheck()); - /* Do version-dependent validation, if compiled to do so. */ -#ifdef LDR_VALIDATE_PROCESS_VERSION - const MinimumProgramVersion *entries = nullptr; - size_t num_entries = 0; - - const auto hos_version = hos::GetVersion(); - if (hos_version >= hos::Version_11_0_0) { - entries = g_MinimumProgramVersions1100; - num_entries = g_MinimumProgramVersionsCount1100; - } else if (hos_version >= hos::Version_10_1_0) { - entries = g_MinimumProgramVersions1010; - num_entries = g_MinimumProgramVersionsCount1010; - } else if (hos_version >= hos::Version_10_0_0) { - entries = g_MinimumProgramVersions1000; - num_entries = g_MinimumProgramVersionsCount1000; - } else if (hos_version >= hos::Version_9_1_0) { - entries = g_MinimumProgramVersions910; - num_entries = g_MinimumProgramVersionsCount910; - } else if (hos_version >= hos::Version_9_0_0) { - entries = g_MinimumProgramVersions900; - num_entries = g_MinimumProgramVersionsCount900; - } else if (hos_version >= hos::Version_8_1_0) { - entries = g_MinimumProgramVersions810; - num_entries = g_MinimumProgramVersionsCount810; - } - - for (size_t i = 0; i < num_entries; i++) { - if (entries[i].program_id == program_id) { - R_UNLESS(entries[i].version <= version, ldr::ResultInvalidVersion()); - } - } -#else + /* TODO: Anti-downgrade checking does not make very much sense for us. Should we do anything? */ AMS_UNUSED(program_id, version); -#endif - return ResultSuccess(); + + R_SUCCEED(); } /* Helpers. */ @@ -162,7 +127,7 @@ namespace ams::ldr { /* Copy flags. */ out->flags = MakeProgramInfoFlag(static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)); - return ResultSuccess(); + R_SUCCEED(); } bool IsApplet(const Meta *meta) { @@ -181,7 +146,7 @@ namespace ams::ldr { return static_cast((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift); } - Result LoadNsoHeaders(NsoHeader *nso_headers, bool *has_nso) { + Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) { /* Clear NSOs. */ std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count); std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count); @@ -200,10 +165,10 @@ namespace ams::ldr { } } - return ResultSuccess(); + R_SUCCEED(); } - Result ValidateNsoHeaders(const NsoHeader *nso_headers, const bool *has_nso) { + Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) { /* We must always have a main. */ R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso()); @@ -219,7 +184,61 @@ namespace ams::ldr { R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso()); } - return ResultSuccess(); + R_SUCCEED(); + } + + constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = { + { 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */ + { 0x0100152000022000 }, /* Mario Kart 8 Deluxe */ + { 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */ + { 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */ + { 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */ + { 0x01002FF008C24000 }, /* Ring Fit Adventure */ + { 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */ + { 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */ + { 0x01006F8002326000 }, /* Animal Crossing: New Horizons */ + { 0x01006FB00F50E000 }, /* [???] */ + { 0x010070300F50C000 }, /* [???] */ + { 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */ + { 0x01008DB008C2C000 }, /* Pokemon Shield */ + { 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */ + { 0x0100A66003384000 }, /* Hulu */ + { 0x0100ABF008968000 }, /* Pokemon Sword */ + { 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */ + { 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */ + { 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */ + { 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */ + }; + + /* Check that the unqualified approval programs are sorted. */ + static_assert([]() -> bool { + for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) { + if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) { + return false; + } + } + + return true; + }()); + + bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) { + /* Check if the program id is one with unqualified approval. */ + return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id); + } + + bool IsUnqualifiedApproval(const Meta *meta) { + /* If the meta has unqualified approval flag, it's unqualified approval. */ + if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) { + return true; + } + + /* If the unqualified approval flag is not set, the program must be an application. */ + if (!IsApplication(meta)) { + return false; + } + + /* The program id must be a force unqualified approval program id. */ + return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max; } Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) { @@ -238,7 +257,7 @@ namespace ams::ldr { R_TRY(TestCapability(static_cast(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32))); /* If we have data to validate, validate it. */ - if (code_verification_data.has_data && meta->check_verification_data) { + if (meta->check_verification_data) { const u8 *sig = code_verification_data.signature; const size_t sig_size = sizeof(code_verification_data.signature); const u8 *mod = static_cast(meta->modulus); @@ -249,11 +268,19 @@ namespace ams::ldr { const size_t hsh_size = sizeof(code_verification_data.target_hash); const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size); - R_UNLESS(is_signature_valid, ldr::ResultInvalidNcaSignature()); + /* If the signature check fails, we need to check if this is allowable. */ + if (!is_signature_valid) { + /* We have to enforce signature checks on prod and when we have a signature to check on dev. */ + R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature()); + R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature()); + + /* There was no signature to check on dev. Check if this is acceptable. */ + R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature()); + } } /* All good. */ - return ResultSuccess(); + R_SUCCEED(); } Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) { @@ -281,7 +308,7 @@ namespace ams::ldr { flags |= svc::CreateProcessFlag_AddressSpace64Bit; break; default: - return ldr::ResultInvalidMeta(); + R_THROW(ldr::ResultInvalidMeta()); } /* Set Enable Debug. */ @@ -308,6 +335,8 @@ namespace ams::ldr { /* 5.0.0+ Set Pool Partition. */ if (hos::GetVersion() >= hos::Version_5_0_0) { + /* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */ + /* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */ switch (GetPoolPartition(meta)) { case Acid::PoolPartition_Application: if (IsApplet(meta)) { @@ -326,7 +355,7 @@ namespace ams::ldr { flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure; break; default: - return ldr::ResultInvalidMeta(); + R_THROW(ldr::ResultInvalidMeta()); } } else if (hos::GetVersion() >= hos::Version_4_0_0) { /* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */ @@ -341,7 +370,7 @@ namespace ams::ldr { } *out = flags; - return ResultSuccess(); + R_SUCCEED(); } Result GetCreateProcessParameter(svc::CreateProcessParameter *out, const Meta *meta, u32 flags, os::NativeHandle resource_limit) { @@ -376,64 +405,7 @@ namespace ams::ldr { out->system_resource_num_pages = meta->npdm->system_resource_size >> 12; } - return ResultSuccess(); - } - - ALWAYS_INLINE u64 GetCurrentProcessInfo(svc::InfoType info_type) { - u64 value; - R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), info_type, svc::PseudoHandle::CurrentProcess, 0)); - return value; - } - - Result SearchFreeRegion(uintptr_t *out, size_t mapping_size) { - /* Get address space extents. */ - const uintptr_t heap_start = GetCurrentProcessInfo(svc::InfoType_HeapRegionAddress); - const size_t heap_size = GetCurrentProcessInfo(svc::InfoType_HeapRegionSize); - const uintptr_t alias_start = GetCurrentProcessInfo(svc::InfoType_AliasRegionAddress); - const size_t alias_size = GetCurrentProcessInfo(svc::InfoType_AliasRegionSize); - const uintptr_t aslr_start = GetCurrentProcessInfo(svc::InfoType_AslrRegionAddress); - const size_t aslr_size = GetCurrentProcessInfo(svc::InfoType_AslrRegionSize); - - /* Iterate upwards to find a free region. */ - uintptr_t address = aslr_start; - while (true) { - /* Declare variables for memory querying. */ - svc::MemoryInfo mem_info; - svc::PageInfo page_info; - - /* Check that we're still within bounds. */ - R_UNLESS(address < address + mapping_size, svc::ResultOutOfMemory()); - - /* If we're within the heap region, skip to the end of the heap region. */ - if (heap_size != 0 && !(address + mapping_size - 1 < heap_start || heap_start + heap_size - 1 < address)) { - R_UNLESS(address < heap_start + heap_size, svc::ResultOutOfMemory()); - address = heap_start + heap_size; - continue; - } - - /* If we're within the alias region, skip to the end of the alias region. */ - if (alias_size != 0 && !(address + mapping_size - 1 < alias_start || alias_start + alias_size - 1 < address)) { - R_UNLESS(address < alias_start + alias_size, svc::ResultOutOfMemory()); - address = alias_start + alias_size; - continue; - } - - /* Get the current memory range. */ - R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)); - - /* If the memory range is free and big enough, use it. */ - if (mem_info.state == svc::MemoryState_Free && mapping_size <= ((mem_info.base_address + mem_info.size) - address)) { - *out = address; - return ResultSuccess(); - } - - /* Check that we can advance. */ - R_UNLESS(address < mem_info.base_address + mem_info.size, svc::ResultOutOfMemory()); - R_UNLESS(mem_info.base_address + mem_info.size - 1 < aslr_start + aslr_size - 1, svc::ResultOutOfMemory()); - - /* Advance. */ - address = mem_info.base_address + mem_info.size; - } + R_SUCCEED(); } Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) { @@ -523,28 +495,10 @@ namespace ams::ldr { out_param->code_address = aslr_start; out_param->code_num_pages = total_size >> 12; - return ResultSuccess(); + R_SUCCEED(); } - Result CreateProcessImpl(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) { - /* Get CreateProcessParameter. */ - svc::CreateProcessParameter param; - R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit)); - - /* Decide on an NSO layout. */ - R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument)); - - /* Actually create process. */ - svc::Handle process_handle; - R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(u32))); - - /* Set the output handle. */ - out->process_handle = process_handle; - - return ResultSuccess(); - } - - Result LoadNsoSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { + Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { /* Select read size based on compression. */ if (!is_compressed) { file_size = segment->size; @@ -574,28 +528,32 @@ namespace ams::ldr { R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso()); } - return ResultSuccess(); + R_SUCCEED(); } - Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { + Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { /* Map and read data from file. */ { - AutoCloseMap map(map_address, process_handle, nso_address, nso_size); - R_TRY(map.GetResult()); + /* Map the process memory. */ + void *mapped_memory = nullptr; + R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size)); + ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); }; + + const uintptr_t map_address = reinterpret_cast(mapped_memory); /* Load NSO segments. */ - R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); - R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); - R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); + R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); + R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); + R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); /* Clear unused space to zero. */ const size_t text_end = nso_header->text_dst_offset + nso_header->text_size; const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size; const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size; - std::memset(reinterpret_cast(map_address), 0, nso_header->text_dst_offset); + std::memset(reinterpret_cast(map_address + 0), 0, nso_header->text_dst_offset); std::memset(reinterpret_cast(map_address + text_end), 0, nso_header->ro_dst_offset - text_end); std::memset(reinterpret_cast(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end); std::memset(reinterpret_cast(map_address + rw_end), 0, nso_header->bss_size); @@ -620,16 +578,16 @@ namespace ams::ldr { const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize); const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize); if (text_size) { - R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, svc::MemoryPermission_ReadExecute)); + R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, os::MemoryPermission_ReadExecute)); } if (ro_size) { - R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, svc::MemoryPermission_Read)); + R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly)); } if (rw_size) { - R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, svc::MemoryPermission_ReadWrite)); + R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite)); } - return ResultSuccess(); + R_SUCCEED(); } Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) { @@ -640,10 +598,7 @@ namespace ams::ldr { R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read)); ON_SCOPE_EXIT { fs::CloseFile(file); }; - uintptr_t map_address; - R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->nso_size[i])); - - R_TRY(LoadAutoLoadModule(process_info->process_handle, file, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); + R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); } } @@ -651,13 +606,11 @@ namespace ams::ldr { if (argument != nullptr) { /* Write argument data into memory. */ { - uintptr_t map_address; - R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->args_size)); + void *map_address = nullptr; + R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size)); + ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); }; - AutoCloseMap map(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); - R_TRY(map.GetResult()); - - ProgramArguments *args = reinterpret_cast(map_address); + ProgramArguments *args = static_cast(map_address); std::memset(args, 0, sizeof(*args)); args->allocated_size = process_info->args_size; args->arguments_size = argument->argument_size; @@ -665,10 +618,31 @@ namespace ams::ldr { } /* Set argument region permissions. */ - R_TRY(svc::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, svc::MemoryPermission_ReadWrite)); + /* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */ + R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite)); } - return ResultSuccess(); + R_SUCCEED(); + } + + Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) { + /* Get CreateProcessParameter. */ + svc::CreateProcessParameter param; + R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit)); + + /* Decide on an NSO layout. */ + R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument)); + + /* Actually create process. */ + svc::Handle process_handle; + R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast(meta->aci_kac), meta->aci->kac_size / sizeof(u32))); + + /* Set the output handle, and ensure that if we fail after this point we clean it up. */ + out->process_handle = process_handle; + ON_RESULT_FAILURE { svc::CloseHandle(process_handle); }; + + /* Load all auto load modules. */ + R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument)); } } @@ -687,22 +661,13 @@ namespace ams::ldr { /* Validate meta. */ R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData())); - /* Load, validate NSOs. */ - R_TRY(LoadNsoHeaders(g_nso_headers, g_has_nso)); - R_TRY(ValidateNsoHeaders(g_nso_headers, g_has_nso)); + /* Load, validate NSO headers. */ + R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso)); + R_TRY(CheckAutoLoad(g_nso_headers, g_has_nso)); - /* Actually create process. */ + /* Actually create the process and load NSOs into process memory. */ ProcessInfo info; - R_TRY(CreateProcessImpl(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit)); - - /* Load NSOs into process memory. */ - { - /* Ensure we close the process handle, if we fail. */ - ON_RESULT_FAILURE { os::CloseNativeHandle(info.process_handle); }; - - /* Load all NSOs. */ - R_TRY(LoadAutoLoadModules(std::addressof(info), g_nso_headers, g_has_nso, argument)); - } + R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit)); /* Register NSOs with the RoManager. */ { @@ -759,22 +724,22 @@ namespace ams::ldr { Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) { R_UNLESS(RoManager::GetInstance().Allocate(out_id, loc, override_status), ldr::ResultMaxProcess()); - return ResultSuccess(); + R_SUCCEED(); } Result UnpinProgram(PinId id) { R_UNLESS(RoManager::GetInstance().Free(id), ldr::ResultNotPinned()); - return ResultSuccess(); + R_SUCCEED(); } Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) { R_UNLESS(RoManager::GetInstance().GetProcessModuleInfo(out_count, out, max_out_count, process_id), ldr::ResultNotPinned()); - return ResultSuccess(); + R_SUCCEED(); } Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) { R_UNLESS(RoManager::GetInstance().GetProgramLocationAndStatus(out, out_status, pin_id), ldr::ResultNotPinned()); - return ResultSuccess(); + R_SUCCEED(); } }