From 82d97283725e435211ecbb80ee03d662b21a00a0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 14:51:07 -0600 Subject: [PATCH 01/21] Loader: Save process->is_64_bit as prep for ldr:ro --- stratosphere/loader/source/ldr_process_creation.cpp | 8 +++++++- stratosphere/loader/source/ldr_registration.cpp | 3 ++- stratosphere/loader/source/ldr_registration.hpp | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index a353b645d..ddac6e006 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -174,7 +174,13 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc /* Update the list of registered processes with the new process. */ svcGetProcessId(&process_id, process_h); - Registration::set_process_id_and_tid_min(index, process_id, npdm_info.aci0->title_id); + bool is_64_bit; + if (kernelAbove200()) { + is_64_bit = (((npdm_info.header->mmu_flags >> 1) & 5) | 2) == 3; + } else { + is_64_bit = (npdm_info.header->mmu_flags & 0xE) == 0x2; + } + Registration::set_process_id_tid_min_and_is_64_bit(index, process_id, npdm_info.aci0->title_id, is_64_bit); for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { if (NsoUtils::IsNsoPresent(i)) { Registration::add_nso_info(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 509b85f85..706a752e2 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -75,7 +75,7 @@ Result Registration::get_registered_tid_sid(u64 index, Registration::TidSid *out return 0; } -void Registration::set_process_id_and_tid_min(u64 index, u64 process_id, u64 tid_min) { +void Registration::set_process_id_tid_min_and_is_64_bit(u64 index, u64 process_id, u64 tid_min, bool is_64_bit) { Registration::Process *target_process = get_process(index); if (target_process == NULL) { return; @@ -83,6 +83,7 @@ void Registration::set_process_id_and_tid_min(u64 index, u64 process_id, u64 tid target_process->process_id = process_id; target_process->title_id_min = tid_min; + target_process->is_64_bit = is_64_bit; } void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index da95cde42..771c4bdb1 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -25,6 +25,7 @@ class Registration { struct Process { bool in_use; + bool is_64_bit; u64 index; u64 process_id; u64 title_id_min; @@ -44,7 +45,7 @@ class Registration { static Result get_registered_tid_sid(u64 index, Registration::TidSid *out); static bool register_tid_sid(const TidSid *tid_sid, u64 *out_index); static bool unregister_index(u64 index); - static void set_process_id_and_tid_min(u64 index, u64 process_id, u64 tid_min); + static void set_process_id_tid_min_and_is_64_bit(u64 index, u64 process_id, u64 tid_min, bool is_64_bit); static void add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id); static Result get_nso_infos_for_process_id(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; From 1ec3eb1ace1b103d7df70e98a0f881156c4a1084 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 14:53:33 -0600 Subject: [PATCH 02/21] Loader: is_64_bit -> is_64_bit_addspace --- stratosphere/loader/source/ldr_process_creation.cpp | 8 ++++---- stratosphere/loader/source/ldr_registration.cpp | 4 ++-- stratosphere/loader/source/ldr_registration.hpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index ddac6e006..ef60ef6fe 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -174,13 +174,13 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc /* Update the list of registered processes with the new process. */ svcGetProcessId(&process_id, process_h); - bool is_64_bit; + bool is_64_bit_addspace; if (kernelAbove200()) { - is_64_bit = (((npdm_info.header->mmu_flags >> 1) & 5) | 2) == 3; + is_64_bit_addspace = (((npdm_info.header->mmu_flags >> 1) & 5) | 2) == 3; } else { - is_64_bit = (npdm_info.header->mmu_flags & 0xE) == 0x2; + is_64_bit_addspace = (npdm_info.header->mmu_flags & 0xE) == 0x2; } - Registration::set_process_id_tid_min_and_is_64_bit(index, process_id, npdm_info.aci0->title_id, is_64_bit); + Registration::set_process_id_tid_min_and_is_64_bit_addspace(index, process_id, npdm_info.aci0->title_id, is_64_bit_addspace); for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { if (NsoUtils::IsNsoPresent(i)) { Registration::add_nso_info(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 706a752e2..fb959ef1a 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -75,7 +75,7 @@ Result Registration::get_registered_tid_sid(u64 index, Registration::TidSid *out return 0; } -void Registration::set_process_id_tid_min_and_is_64_bit(u64 index, u64 process_id, u64 tid_min, bool is_64_bit) { +void Registration::set_process_id_tid_min_and_is_64_bit_addspace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace) { Registration::Process *target_process = get_process(index); if (target_process == NULL) { return; @@ -83,7 +83,7 @@ void Registration::set_process_id_tid_min_and_is_64_bit(u64 index, u64 process_i target_process->process_id = process_id; target_process->title_id_min = tid_min; - target_process->is_64_bit = is_64_bit; + target_process->is_64_bit_addspace = is_64_bit_addspace; } void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 771c4bdb1..8779bc455 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -25,7 +25,7 @@ class Registration { struct Process { bool in_use; - bool is_64_bit; + bool is_64_bit_addspace; u64 index; u64 process_id; u64 title_id_min; @@ -45,7 +45,7 @@ class Registration { static Result get_registered_tid_sid(u64 index, Registration::TidSid *out); static bool register_tid_sid(const TidSid *tid_sid, u64 *out_index); static bool unregister_index(u64 index); - static void set_process_id_tid_min_and_is_64_bit(u64 index, u64 process_id, u64 tid_min, bool is_64_bit); + static void set_process_id_tid_min_and_is_64_bit_addspace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); static void add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id); static Result get_nso_infos_for_process_id(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; From 4f09c61bfa3569511aae2b86eb30379a1cd11884 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 16:45:09 -0600 Subject: [PATCH 03/21] Loader: Push ldr:ro stub. --- stratosphere/loader/source/ldr_ro_service.cpp | 57 +++++++++++++++++++ stratosphere/loader/source/ldr_ro_service.hpp | 34 +++++++++++ 2 files changed, 91 insertions(+) create mode 100644 stratosphere/loader/source/ldr_ro_service.cpp create mode 100644 stratosphere/loader/source/ldr_ro_service.hpp diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp new file mode 100644 index 000000000..f5f7f5984 --- /dev/null +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +#include "ldr_ro_service.hpp" +#include "ldr_registration.hpp" + +Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { + Result rc = 0xF601; + + switch ((RoServiceCmd)cmd_id) { + case Ro_Cmd_LoadNro: + rc = WrapIpcCommandImpl<&RelocatableObjectsService::load_nro>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case Ro_Cmd_UnloadNro: + rc = WrapIpcCommandImpl<&RelocatableObjectsService::unload_nro>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case Ro_Cmd_LoadNrr: + rc = WrapIpcCommandImpl<&RelocatableObjectsService::load_nrr>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case Ro_Cmd_UnloadNrr: + rc = WrapIpcCommandImpl<&RelocatableObjectsService::unload_nrr>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case Ro_Cmd_Initialize: + rc = WrapIpcCommandImpl<&RelocatableObjectsService::initialize>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + default: + break; + } + return rc; +} + + +std::tuple load_nro(PidDescriptor pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + /* TODO */ + return std::make_tuple(0xF601, 0); +} + +std::tuple unload_nro(PidDescriptor pid, u64 nro_address) { + /* TODO */ + return std::make_tuple(0xF601); +} + +std::tuple load_nrr(PidDescriptor pid, u64 nrr_address, u64 nrr_size) { + /* TODO */ + return std::make_tuple(0xF601); +} + +std::tuple unload_nrr(PidDescriptor pid, u64 nrr_address) { + /* TODO */ + return std::make_tuple(0xF601); +} + +std::tuple initialize(PidDescriptor pid, CopiedHandle process_h) { + /* TODO */ + return std::make_tuple(0xF601); +} diff --git a/stratosphere/loader/source/ldr_ro_service.hpp b/stratosphere/loader/source/ldr_ro_service.hpp new file mode 100644 index 000000000..29d057ba0 --- /dev/null +++ b/stratosphere/loader/source/ldr_ro_service.hpp @@ -0,0 +1,34 @@ +#pragma once +#include + +#include +#include "ldr_registration.hpp" + +enum RoServiceCmd { + Ro_Cmd_LoadNro = 0, + Ro_Cmd_UnloadNro = 1, + Ro_Cmd_LoadNrr = 2, + Ro_Cmd_UnloadNrr = 3, + Ro_Cmd_Initialize = 4, +}; + +class RelocatableObjectsService : IServiceObject { + Handle process_handle; + u64 process_id; + bool has_initialized; + public: + RelocatableObjectsService() : process_handle(0), process_id(U64_MAX), has_initialized(false) { } + virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result handle_deferred() { + /* This service will never defer. */ + return 0; + } + + private: + /* Actual commands. */ + std::tuple load_nro(PidDescriptor pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); + std::tuple unload_nro(PidDescriptor pid, u64 nro_address); + std::tuple load_nrr(PidDescriptor pid, u64 nrr_address, u64 nrr_size); + std::tuple unload_nrr(PidDescriptor pid, u64 nrr_address); + std::tuple initialize(PidDescriptor pid, CopiedHandle process_h); +}; \ No newline at end of file From fe2f227dfc3818f17364d89876c3b434e116441b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 16:48:57 -0600 Subject: [PATCH 04/21] Loader: Implement ldr:ro->Initialize() --- stratosphere/loader/source/ldr_ro_service.cpp | 23 +++++++++++++------ stratosphere/loader/source/ldr_ro_service.hpp | 10 ++++---- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index f5f7f5984..2375a0b0b 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -31,27 +31,36 @@ Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_ } -std::tuple load_nro(PidDescriptor pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { +std::tuple RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { /* TODO */ return std::make_tuple(0xF601, 0); } -std::tuple unload_nro(PidDescriptor pid, u64 nro_address) { +std::tuple RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, u64 nro_address) { /* TODO */ return std::make_tuple(0xF601); } -std::tuple load_nrr(PidDescriptor pid, u64 nrr_address, u64 nrr_size) { +std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { /* TODO */ return std::make_tuple(0xF601); } -std::tuple unload_nrr(PidDescriptor pid, u64 nrr_address) { +std::tuple RelocatableObjectsService::unload_nrr(PidDescriptor pid_desc, u64 nrr_address) { /* TODO */ return std::make_tuple(0xF601); } -std::tuple initialize(PidDescriptor pid, CopiedHandle process_h) { - /* TODO */ - return std::make_tuple(0xF601); +std::tuple RelocatableObjectsService::initialize(PidDescriptor pid_desc, CopiedHandle process_h) { + u64 handle_pid; + Result rc = 0xAE09; + if (R_SUCCEEDED(svcGetProcessId(&handle_pid, process_h.handle)) && handle_pid == pid_desc.pid) { + if (this->has_initialized) { + svcCloseHandle(this->process_handle); + } + this->process_handle = process_h.handle; + this->has_initialized = true; + rc = 0; + } + return std::make_tuple(rc); } diff --git a/stratosphere/loader/source/ldr_ro_service.hpp b/stratosphere/loader/source/ldr_ro_service.hpp index 29d057ba0..f2a02f252 100644 --- a/stratosphere/loader/source/ldr_ro_service.hpp +++ b/stratosphere/loader/source/ldr_ro_service.hpp @@ -26,9 +26,9 @@ class RelocatableObjectsService : IServiceObject { private: /* Actual commands. */ - std::tuple load_nro(PidDescriptor pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); - std::tuple unload_nro(PidDescriptor pid, u64 nro_address); - std::tuple load_nrr(PidDescriptor pid, u64 nrr_address, u64 nrr_size); - std::tuple unload_nrr(PidDescriptor pid, u64 nrr_address); - std::tuple initialize(PidDescriptor pid, CopiedHandle process_h); + std::tuple load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); + std::tuple unload_nro(PidDescriptor pid_desc, u64 nro_address); + std::tuple load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size); + std::tuple unload_nrr(PidDescriptor pid_desc, u64 nrr_address); + std::tuple initialize(PidDescriptor pid_desc, CopiedHandle process_h); }; \ No newline at end of file From 977a51edb0d173609845c6e89b31cac10c374d2a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 16:50:43 -0600 Subject: [PATCH 05/21] Loader: Service ldr:ro on <= 2.3.0 --- stratosphere/loader/source/ldr_main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index b82521435..f3ab9b0bc 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -9,6 +9,7 @@ #include "ldr_process_manager.hpp" #include "ldr_debug_monitor.hpp" #include "ldr_shell.hpp" +#include "ldr_ro_service.hpp" extern "C" { extern u32 __start__; @@ -83,6 +84,10 @@ int main(int argc, char **argv) server_manager->add_waitable(new ServiceServer("dbg:pm", 1)); server_manager->add_waitable(new ServiceServer("dbg:shel", 3)); server_manager->add_waitable(new ServiceServer("dbg:dmnt", 2)); + if (!kernelAbove300()) { + /* On 1.0.0-2.3.0, Loader services ldr:ro instead of ro. */ + server_manager->add_waitable(new ServiceServer("dbg:ro", 0x20)); + } /* Loop forever, servicing our services. */ server_manager->process(); From 991357f3099ef8fb9fd293d9ca55def1bef353aa Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 17:03:10 -0600 Subject: [PATCH 06/21] Loader: Add NRRInfo to RegisteredProcess, refactor Registration:: --- .../loader/source/ldr_debug_monitor.cpp | 2 +- .../loader/source/ldr_process_creation.cpp | 6 ++-- .../loader/source/ldr_process_manager.cpp | 6 ++-- .../loader/source/ldr_registration.cpp | 30 ++++++++--------- .../loader/source/ldr_registration.hpp | 32 +++++++++++++------ 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/stratosphere/loader/source/ldr_debug_monitor.cpp b/stratosphere/loader/source/ldr_debug_monitor.cpp index 218e82f47..954b01168 100644 --- a/stratosphere/loader/source/ldr_debug_monitor.cpp +++ b/stratosphere/loader/source/ldr_debug_monitor.cpp @@ -41,7 +41,7 @@ std::tuple DebugMonitorService::get_nso_info(u64 pid, OutPointerWit std::fill(out.pointer, out.pointer + out.num_elements, (const Registration::NsoInfo){0}); - Result rc = Registration::get_nso_infos_for_process_id(out.pointer, out.num_elements, pid, &out_num_nsos); + Result rc = Registration::GetNsoInfosForProcessId(out.pointer, out.num_elements, pid, &out_num_nsos); return std::make_tuple(rc, out_num_nsos); } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index ef60ef6fe..c8d0fe91c 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -96,7 +96,7 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc Result rc; /* Get the process from the registration queue. */ - target_process = Registration::get_process(index); + target_process = Registration::GetProcess(index); if (target_process == NULL) { return 0x1009; } @@ -180,10 +180,10 @@ Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nc } else { is_64_bit_addspace = (npdm_info.header->mmu_flags & 0xE) == 0x2; } - Registration::set_process_id_tid_min_and_is_64_bit_addspace(index, process_id, npdm_info.aci0->title_id, is_64_bit_addspace); + Registration::SetProcessIdTidMinAndIs64BitAddressSpace(index, process_id, npdm_info.aci0->title_id, is_64_bit_addspace); for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { if (NsoUtils::IsNsoPresent(i)) { - Registration::add_nso_info(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); + Registration::AddNsoInfo(index, nso_extents.nso_addresses[i], nso_extents.nso_sizes[i], NsoUtils::GetNsoBuildId(i)); } } diff --git a/stratosphere/loader/source/ldr_process_manager.cpp b/stratosphere/loader/source/ldr_process_manager.cpp index 98ee6f0a2..8fbda0e6f 100644 --- a/stratosphere/loader/source/ldr_process_manager.cpp +++ b/stratosphere/loader/source/ldr_process_manager.cpp @@ -37,7 +37,7 @@ std::tuple ProcessManagerService::create_process(u64 flags, fprintf(stderr, "CreateProcess(%016lx, %016lx, %08x);\n", flags, index, reslimit_h.handle); - rc = Registration::get_registered_tid_sid(index, &tid_sid); + rc = Registration::GetRegisteredTidSid(index, &tid_sid); if (R_FAILED(rc)) { std::make_tuple(rc, MovedHandle{process_h}); } @@ -85,7 +85,7 @@ std::tuple ProcessManagerService::get_program_info(Registration::TidSid std::tuple ProcessManagerService::register_title(Registration::TidSid tid_sid) { u64 out_index = 0; - if (Registration::register_tid_sid(&tid_sid, &out_index)) { + if (Registration::RegisterTidSid(&tid_sid, &out_index)) { return std::make_tuple(0, out_index); } else { return std::make_tuple(0xE09, out_index); @@ -93,7 +93,7 @@ std::tuple ProcessManagerService::register_title(Registration::TidS } std::tuple ProcessManagerService::unregister_title(u64 index) { - if (Registration::unregister_index(index)) { + if (Registration::UnregisterIndex(index)) { return std::make_tuple(0); } else { return std::make_tuple(0x1009); diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index fb959ef1a..7ff8a676c 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -6,7 +6,7 @@ static Registration::List g_registration_list = {0}; static u64 g_num_registered = 1; -Registration::Process *Registration::get_free_process() { +Registration::Process *Registration::GetFreeProcess() { unsigned int i; for (i = 0; i < REGISTRATION_LIST_MAX; i++) { if (!g_registration_list.processes[i].in_use) { @@ -16,7 +16,7 @@ Registration::Process *Registration::get_free_process() { return NULL; } -Registration::Process *Registration::get_process(u64 index) { +Registration::Process *Registration::GetProcess(u64 index) { unsigned int i; for (i = 0; i < REGISTRATION_LIST_MAX && (!g_registration_list.processes[i].in_use || g_registration_list.processes[i].index != index); i++) { } @@ -26,7 +26,7 @@ Registration::Process *Registration::get_process(u64 index) { return &g_registration_list.processes[i]; } -Registration::Process *Registration::get_process_by_process_id(u64 pid) { +Registration::Process *Registration::GetProcessByProcessId(u64 pid) { unsigned int i; for (i = 0; i < REGISTRATION_LIST_MAX && (!g_registration_list.processes[i].in_use || g_registration_list.processes[i].process_id != pid); i++) { @@ -37,8 +37,8 @@ Registration::Process *Registration::get_process_by_process_id(u64 pid) { return &g_registration_list.processes[i]; } -bool Registration::register_tid_sid(const TidSid *tid_sid, u64 *out_index) { - Registration::Process *free_process = get_free_process(); +bool Registration::RegisterTidSid(const TidSid *tid_sid, u64 *out_index) { + Registration::Process *free_process = GetFreeProcess(); if (free_process == NULL) { return false; } @@ -52,8 +52,8 @@ bool Registration::register_tid_sid(const TidSid *tid_sid, u64 *out_index) { return true; } -bool Registration::unregister_index(u64 index) { - Registration::Process *target_process = get_process(index); +bool Registration::UnregisterIndex(u64 index) { + Registration::Process *target_process = GetProcess(index); if (target_process == NULL) { return false; } @@ -64,8 +64,8 @@ bool Registration::unregister_index(u64 index) { } -Result Registration::get_registered_tid_sid(u64 index, Registration::TidSid *out) { - Registration::Process *target_process = get_process(index); +Result Registration::GetRegisteredTidSid(u64 index, Registration::TidSid *out) { + Registration::Process *target_process = GetProcess(index); if (target_process == NULL) { return 0x1009; } @@ -75,8 +75,8 @@ Result Registration::get_registered_tid_sid(u64 index, Registration::TidSid *out return 0; } -void Registration::set_process_id_tid_min_and_is_64_bit_addspace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace) { - Registration::Process *target_process = get_process(index); +void Registration::SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace) { + Registration::Process *target_process = GetProcess(index); if (target_process == NULL) { return; } @@ -86,8 +86,8 @@ void Registration::set_process_id_tid_min_and_is_64_bit_addspace(u64 index, u64 target_process->is_64_bit_addspace = is_64_bit_addspace; } -void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { - Registration::Process *target_process = get_process(index); +void Registration::AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id) { + Registration::Process *target_process = GetProcess(index); if (target_process == NULL) { return; } @@ -104,8 +104,8 @@ void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const uns } -Result Registration::get_nso_infos_for_process_id(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { - Registration::Process *target_process = get_process_by_process_id(process_id); +Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { + Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { return 0x1009; } diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 8779bc455..1c1483eec 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -4,6 +4,7 @@ #define REGISTRATION_LIST_MAX (0x40) #define NSO_INFO_MAX (0x20) +#define NRR_INFO_MAX (0x40) class Registration { public: @@ -18,6 +19,18 @@ class Registration { NsoInfo info; }; + struct NrrInfo { + u64 base_address; + u64 size; + u64 code_memory_address; + u64 address_for_loader; + }; + + struct NrrInfoHolder { + bool in_use; + NrrInfo info; + }; + struct TidSid { u64 title_id; FsStorageId storage_id; @@ -31,6 +44,7 @@ class Registration { u64 title_id_min; Registration::TidSid tid_sid; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; + Registration::NrrInfoHolder nrr_infos[NRR_INFO_MAX]; u64 _0x730; }; @@ -39,13 +53,13 @@ class Registration { u64 num_processes; }; - static Registration::Process *get_free_process(); - static Registration::Process *get_process(u64 index); - static Registration::Process *get_process_by_process_id(u64 pid); - static Result get_registered_tid_sid(u64 index, Registration::TidSid *out); - static bool register_tid_sid(const TidSid *tid_sid, u64 *out_index); - static bool unregister_index(u64 index); - static void set_process_id_tid_min_and_is_64_bit_addspace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); - static void add_nso_info(u64 index, u64 base_address, u64 size, const unsigned char *build_id); - static Result get_nso_infos_for_process_id(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); + static Registration::Process *GetFreeProcess(); + static Registration::Process *GetProcess(u64 index); + static Registration::Process *GetProcessByProcessId(u64 pid); + static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out); + static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); + static bool UnregisterIndex(u64 index); + static void SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); + static void AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); + static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; From b34b9ba0e4df1cfbf979c68cb28734a539fef9ee Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 18:04:30 -0600 Subject: [PATCH 07/21] Loader: Greatly simplify mapping logic, add CodeMemory mapper. --- stratosphere/loader/source/ldr_map.cpp | 241 ++++++++++++++----------- stratosphere/loader/source/ldr_map.hpp | 19 ++ 2 files changed, 150 insertions(+), 110 deletions(-) diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index 9913c39f8..7ac554e16 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -1,6 +1,7 @@ #include #include "ldr_map.hpp" +#include "ldr_random.hpp" Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { if (kernelAbove200()) { @@ -11,38 +12,26 @@ Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) { } +Result MapUtils::MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address) { + if (kernelAbove200()) { + return MapCodeMemoryForProcessModern(process_h, base_address, size, out_code_memory_address); + } else { + return MapCodeMemoryForProcessDeprecated(process_h, is_64_bit_address_space, base_address, size, out_code_memory_address); + } +} + Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { MemoryInfo mem_info = {0}; + AddressSpaceInfo address_space = {0}; u32 page_info = 0; - u64 heap_base = 0, heap_size = 0, heap_end = 0; - u64 map_base = 0, map_size = 0, map_end = 0; - u64 addspace_base = 0, addspace_size = 0, addspace_end = 0; u64 cur_base = 0, cur_end = 0; Result rc; - if (R_FAILED((rc = svcGetInfo(&heap_base, 4, CUR_PROCESS_HANDLE, 0)))) { + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) { return rc; } - if (R_FAILED((rc = svcGetInfo(&heap_size, 5, CUR_PROCESS_HANDLE, 0)))) { - return rc; - } - if (R_FAILED((rc = svcGetInfo(&map_base, 2, CUR_PROCESS_HANDLE, 0)))) { - return rc; - } - if (R_FAILED((rc = svcGetInfo(&map_size, 3, CUR_PROCESS_HANDLE, 0)))) { - return rc; - } - if (R_FAILED((rc = svcGetInfo(&addspace_base, 12, CUR_PROCESS_HANDLE, 0)))) { - return rc; - } - if (R_FAILED((rc = svcGetInfo(&addspace_size, 13, CUR_PROCESS_HANDLE, 0)))) { - return rc; - } - heap_end = heap_base + heap_size; - map_end = map_base + map_size; - addspace_end = addspace_base + addspace_size; - cur_base = addspace_base; + cur_base = address_space.addspace_base; rc = 0xD001; cur_end = cur_base + out_size; @@ -50,98 +39,40 @@ Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { return rc; } - if (heap_size) { - if (map_size) { - while (true) { - if (cur_end - 1 < heap_base || heap_end - 1 < cur_base) { - if (cur_end - 1 < map_base || map_end - 1 < cur_base) { - if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { - /* TODO: panic. */ - } - if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { - *out = cur_base; - return 0x0; - } - if (mem_info.addr + mem_info.size <= cur_base) { - return rc; - } - cur_base = mem_info.addr + mem_info.size; - if (cur_base >= addspace_end) { - return rc; - } - } else { - if (map_end == cur_base) { - return rc; - } - cur_base = map_end; - } - } else { - if (heap_end == cur_base) { - return rc; - } - cur_base = heap_end; - } - cur_end = cur_base + out_size; - if (cur_base + out_size <= cur_base) { - return rc; - } + while (true) { + if (address_space.heap_size && (address_space.heap_base <= cur_end - 1 && cur_base <= address_space.heap_end - 1)) { + /* If we overlap the heap region, go to the end of the heap region. */ + if (cur_base == address_space.heap_end) { + return rc; } + cur_base = address_space.heap_end; + } else if (address_space.map_size && (address_space.map_base <= cur_end - 1 && cur_base <= address_space.map_end - 1)) { + /* If we overlap the map region, go to the end of the map region. */ + if (cur_base == address_space.map_end) { + return rc; + } + cur_base = address_space.map_end; } else { - while (true) { - if (cur_end - 1 < heap_base || heap_end - 1 < cur_base) { - if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { - /* TODO: panic. */ - } - if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { - *out = cur_base; - return 0x0; - } - if (mem_info.addr + mem_info.size <= cur_base) { - return rc; - } - cur_base = mem_info.addr + mem_info.size; - if (cur_base >= addspace_end) { - return rc; - } - } else { - if (heap_end == cur_base) { - return rc; - } - cur_base = heap_end; - } - cur_end = cur_base + out_size; - if (cur_base + out_size <= cur_base) { - return rc; - } + if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { + /* TODO: panic. */ + } + if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { + *out = cur_base; + return 0x0; + } + if (mem_info.addr + mem_info.size <= cur_base) { + return rc; + } + cur_base = mem_info.addr + mem_info.size; + if (cur_base >= address_space.addspace_end) { + return rc; } } - } else { - while (cur_end > cur_base) { - if (map_size && cur_end - 1 >= map_base && map_end - 1 >= cur_base) { - if (cur_base == map_end) { - return rc; - } - cur_base = map_end; - } else { - if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { - /* TODO: panic. */ - } - if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { - *out = cur_base; - return 0x0; - } - if (mem_info.addr + mem_info.size <= cur_base) { - return rc; - } - cur_base = mem_info.addr + mem_info.size; - if (cur_base >= addspace_end) { - return rc; - } - } - cur_end = cur_base + out_size; + cur_end = cur_base + out_size; + if (cur_base + out_size <= cur_base) { + return rc; } - } - return rc; + } } @@ -177,4 +108,94 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { } } return rc; +} + +Result MapUtils::MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address) { + AddressSpaceInfo address_space = {0}; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) { + return rc; + } + + if (size > address_space.addspace_size) { + return 0x6609; + } + + u64 try_address; + for (unsigned int i = 0; i < 0x200; i++) { + while (true) { + try_address = address_space.addspace_base + (RandomUtils::GetRandomU64((u64)(address_space.addspace_size - size) >> 12) << 12); + if (address_space.heap_size && (address_space.heap_base <= try_address + size - 1 && try_address <= address_space.heap_end - 1)) { + continue; + } + if (address_space.map_size && (address_space.map_base <= try_address + size - 1 && try_address <= address_space.map_end - 1)) { + continue; + } + break; + } + rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); + if (rc != 0xD401) { + break; + } + } + if (R_SUCCEEDED(rc)) { + *out_code_memory_address = try_address; + } + return rc; +} + +Result MapUtils::MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address) { + Result rc; + u64 addspace_base, addspace_size; + if (is_64_bit_address_space) { + addspace_base = 0x8000000ULL; + addspace_size = 0x78000000ULL; + } else { + addspace_base = 0x200000ULL; + addspace_size = 0x3FE0000ULL; + } + + if (size > addspace_size) { + return 0x6609; + } + + u64 try_address; + for (unsigned int i = 0; i < 0x200; i++) { + try_address = addspace_base + (RandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); + rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); + if (rc != 0xD401) { + break; + } + } + if (R_SUCCEEDED(rc)) { + *out_code_memory_address = try_address; + } + return rc; +} + +Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) { + Result rc; + if (R_FAILED((rc = svcGetInfo(&out->heap_base, 4, CUR_PROCESS_HANDLE, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->heap_size, 5, CUR_PROCESS_HANDLE, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_base, 2, CUR_PROCESS_HANDLE, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_size, 3, CUR_PROCESS_HANDLE, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_base, 12, CUR_PROCESS_HANDLE, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_size, 13, CUR_PROCESS_HANDLE, 0)))) { + return rc; + } + out->heap_end = out->heap_base + out->heap_size; + out->map_end = out->map_base + out->map_size; + out->addspace_end = out->addspace_base + out->addspace_size; + return 0; } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index bca18a47d..301b8f4ac 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -1,9 +1,28 @@ #pragma once #include +#include "ldr_registration.hpp" + class MapUtils { public: + struct AddressSpaceInfo { + u64 heap_base; + u64 heap_size; + u64 heap_end; + u64 map_base; + u64 map_size; + u64 map_end; + u64 addspace_base; + u64 addspace_size; + u64 addspace_end; + }; + static Result GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h); static Result LocateSpaceForMapDeprecated(u64 *out, u64 out_size); static Result LocateSpaceForMapModern(u64 *out, u64 out_size); static Result LocateSpaceForMap(u64 *out, u64 out_size); + + + static Result MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); + static Result MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address); + static Result MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); }; \ No newline at end of file From 1d73bd0a12687355b29fe9a852551f4bb8c16f9b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 19:13:55 -0600 Subject: [PATCH 08/21] Loader: Start work on LoadNRR, Add AutoCloseMap --- stratosphere/loader/source/ldr_map.hpp | 43 +++++++++++++++++++ stratosphere/loader/source/ldr_nso.cpp | 31 ++++--------- .../loader/source/ldr_registration.cpp | 20 +++++++++ .../loader/source/ldr_registration.hpp | 5 ++- stratosphere/loader/source/ldr_ro_service.cpp | 32 +++++++++++++- 5 files changed, 105 insertions(+), 26 deletions(-) diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 301b8f4ac..f546bf77b 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -25,4 +25,47 @@ class MapUtils { static Result MapCodeMemoryForProcessDeprecated(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); static Result MapCodeMemoryForProcessModern(Handle process_h, u64 base_address, u64 size, u64 *out_code_memory_address); static Result MapCodeMemoryForProcess(Handle process_h, bool is_64_bit_address_space, u64 base_address, u64 size, u64 *out_code_memory_address); +}; + +class AutoCloseMap { + private: + void *mapped_address; + u64 base_address; + u64 size; + Handle process_handle; + public: + AutoCloseMap() : mapped_address(0), base_address(0), size(0), process_handle(0) { }; + ~AutoCloseMap() { + Close(); + } + + void *GetMappedAddress() { + return this->mapped_address; + } + + Result Open(Handle process_h, u64 address, u64 size) { + Result rc; + u64 try_address; + if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { + return rc; + } + + if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, process_h, try_address, size)))) { + return rc; + } + + this->mapped_address = (void *)try_address; + this->process_handle = process_h; + this->base_address = address; + this->size = size; + return 0; + } + + void Close() { + if (this->mapped_address) { + if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) { + /* TODO: panic(). */ + } + } + } }; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nso.cpp b/stratosphere/loader/source/ldr_nso.cpp index 9011725c7..4e70d75b9 100644 --- a/stratosphere/loader/source/ldr_nso.cpp +++ b/stratosphere/loader/source/ldr_nso.cpp @@ -224,17 +224,13 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo Result rc = 0xA09; for (unsigned int i = 0; i < NSO_NUM_MAX; i++) { if (g_nso_present[i]) { - u64 map_addr = 0; - if (R_FAILED((rc = MapUtils::LocateSpaceForMap(&map_addr, extents->nso_sizes[i])))) { - return rc; - } - - u8 *map_base = (u8 *)map_addr; - - if (R_FAILED((rc = svcMapProcessMemory(map_base, process_h, extents->nso_addresses[i], extents->nso_sizes[i])))) { + AutoCloseMap nso_map; + if (R_FAILED((rc = nso_map.Open(process_h, extents->nso_addresses[i], extents->nso_sizes[i])))) { return rc; } + u8 *map_base = (u8 *)nso_map.GetMappedAddress(); + FILE *f_nso = OpenNso(i, title_id); if (f_nso == NULL) { /* Is there a better error to return here? */ @@ -243,7 +239,6 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo for (unsigned int seg = 0; seg < 3; seg++) { if (R_FAILED((rc = LoadNsoSegment(i, seg, f_nso, map_base, map_base + extents->nso_sizes[i])))) { fclose(f_nso); - svcUnmapProcessMemory(map_base, process_h, extents->nso_addresses[i], extents->nso_sizes[i]); return rc; } } @@ -262,9 +257,7 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo u64 bss_base = rw_base + g_nso_headers[i].segments[2].decomp_size, bss_size = g_nso_headers[i].segments[2].align_or_total_size; std::fill(map_base + bss_base, map_base + bss_base + bss_size, 0); - if (R_FAILED((rc = svcUnmapProcessMemory(map_base, process_h, extents->nso_addresses[i], extents->nso_sizes[i])))) { - return rc; - } + nso_map.Close(); for (unsigned int seg = 0; seg < 3; seg++) { u64 size = g_nso_headers[i].segments[seg].decomp_size; @@ -283,25 +276,19 @@ Result NsoUtils::LoadNsosIntoProcessMemory(Handle process_h, u64 title_id, NsoLo /* Map in arguments. */ if (args != NULL && args_size) { - u64 arg_map_addr = 0; - if (R_FAILED((rc = MapUtils::LocateSpaceForMap(&arg_map_addr, extents->args_size)))) { + AutoCloseMap args_map; + if (R_FAILED((rc = args_map.Open(process_h, extents->args_address, extents->args_size)))) { return rc; } - NsoArgument *arg_map_base = (NsoArgument *)arg_map_addr; - - if (R_FAILED((rc = svcMapProcessMemory(arg_map_base, process_h, extents->args_address, extents->args_size)))) { - return rc; - } + NsoArgument *arg_map_base = (NsoArgument *)args_map.GetMappedAddress(); arg_map_base->allocated_space = extents->args_size; arg_map_base->args_size = args_size; std::fill(arg_map_base->_0x8, arg_map_base->_0x8 + sizeof(arg_map_base->_0x8), 0); std::copy(args, args + args_size, arg_map_base->arguments); - if (R_FAILED((rc = svcUnmapProcessMemory(arg_map_base, process_h, extents->args_address, extents->args_size)))) { - return rc; - } + args_map.Close(); if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, extents->args_address, extents->args_size, 3)))) { return rc; diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 7ff8a676c..0299dbc64 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -104,6 +104,26 @@ void Registration::AddNsoInfo(u64 index, u64 base_address, u64 size, const unsig } +Result Registration::AddNrrInfo(u64 index, u64 base_address, u64 size, u64 code_memory_address, u64 loader_address) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + /* TODO: panic() */ + return 0x7009; + } + + for (unsigned int i = 0; i < NSO_INFO_MAX; i++) { + if (!target_process->nrr_infos[i].in_use) { + target_process->nrr_infos[i].info.base_address = base_address; + target_process->nrr_infos[i].info.size = size; + target_process->nrr_infos[i].info.code_memory_address = code_memory_address; + target_process->nrr_infos[i].info.loader_address = loader_address; + return 0; + } + } + return 0x7009; +} + + Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 1c1483eec..53deb2aa5 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -23,7 +23,7 @@ class Registration { u64 base_address; u64 size; u64 code_memory_address; - u64 address_for_loader; + u64 loader_address; }; struct NrrInfoHolder { @@ -45,7 +45,7 @@ class Registration { Registration::TidSid tid_sid; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; Registration::NrrInfoHolder nrr_infos[NRR_INFO_MAX]; - u64 _0x730; + void *owner_ro_service; }; struct List { @@ -61,5 +61,6 @@ class Registration { static bool UnregisterIndex(u64 index); static void SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); static void AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); + static Result AddNrrInfo(u64 index, u64 base_address, u64 size, u64 code_memory_address, u64 loader_address); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index 2375a0b0b..ff4ad97df 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -4,6 +4,7 @@ #include "ldr_ro_service.hpp" #include "ldr_registration.hpp" +#include "ldr_map.hpp" Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result rc = 0xF601; @@ -42,8 +43,34 @@ std::tuple RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, } std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - /* TODO */ - return std::make_tuple(0xF601); + Result rc; + Registration::Process *target_proc; + if (!this->has_initialized || this->process_id != pid_desc.pid) { + rc = 0xAE09; + goto LOAD_NRR_END; + } + if (nrr_address & 0xFFF) { + rc = 0xA209; + goto LOAD_NRR_END; + } + if (nrr_address + nrr_size <= nrr_address || !nrr_size || (nrr_size & 0xFFF)) { + rc = 0xA409; + goto LOAD_NRR_END; + } + + target_proc = Registration::GetProcessByProcessId(pid_desc.pid); + if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { + rc = 0xAC09; + goto LOAD_NRR_END; + } + target_proc->owner_ro_service = this; + + + /* TODO: Implement remainder of fucntion */ + rc = 0xDED09; + +LOAD_NRR_END: + return std::make_tuple(rc); } std::tuple RelocatableObjectsService::unload_nrr(PidDescriptor pid_desc, u64 nrr_address) { @@ -59,6 +86,7 @@ std::tuple RelocatableObjectsService::initialize(PidDescriptor pid_desc, svcCloseHandle(this->process_handle); } this->process_handle = process_h.handle; + this->process_id = handle_pid; this->has_initialized = true; rc = 0; } From 789afe7929043b78a972eb2fd609b231de186f42 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 19:43:26 -0600 Subject: [PATCH 09/21] Loader: fix missing reassignment in AutoCloseMap --- stratosphere/loader/source/ldr_map.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index f546bf77b..52948f371 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -66,6 +66,7 @@ class AutoCloseMap { if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) { /* TODO: panic(). */ } + this->mapped_address = NULL; } } }; \ No newline at end of file From 8524f284fd38e19a4168769bccba2e4e89feac5b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 20:27:52 -0600 Subject: [PATCH 10/21] Loader: Implement ldr:ro->LoadNRR(). NOTE: No sigchecks, at the moment. --- stratosphere/loader/source/ldr_map.hpp | 62 ++++++++++++++++++- stratosphere/loader/source/ldr_nro.cpp | 28 +++++++++ stratosphere/loader/source/ldr_nro.hpp | 33 ++++++++++ .../loader/source/ldr_registration.cpp | 9 +-- .../loader/source/ldr_registration.hpp | 18 ++---- stratosphere/loader/source/ldr_ro_service.cpp | 18 +++++- 6 files changed, 143 insertions(+), 25 deletions(-) create mode 100644 stratosphere/loader/source/ldr_nro.cpp create mode 100644 stratosphere/loader/source/ldr_nro.hpp diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 52948f371..3a0c3885f 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -1,8 +1,6 @@ #pragma once #include -#include "ldr_registration.hpp" - class MapUtils { public: struct AddressSpaceInfo { @@ -69,4 +67,64 @@ class AutoCloseMap { this->mapped_address = NULL; } } +}; + +struct MappedCodeMemory { + Handle process_handle; + u64 base_address; + u64 size; + u64 code_memory_address; + void *mapped_address; + + bool IsActive() { + return this->mapped_address != NULL; + } + + /* Utility functions. */ + Result Open(Handle process_h, bool is_64_bit_address_space, u64 address, u64 size) { + Result rc; + u64 try_address; + if (this->IsActive()) { + return 0x19009; + } + + this->process_handle = process_h; + this->base_address = address; + this->size = size; + + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(process_h, is_64_bit_address_space, address, size, &this->code_memory_address)))) { + goto CODE_MEMORY_OPEN_END; + } + + if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { + goto CODE_MEMORY_OPEN_END; + } + + if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, process_h, try_address, size)))) { + goto CODE_MEMORY_OPEN_END; + } + + this->mapped_address = (void *)try_address; + + CODE_MEMORY_OPEN_END: + if (R_FAILED(rc)) { + if (this->code_memory_address && R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { + /* TODO: panic(). */ + } + *this = (const MappedCodeMemory){0}; + } + return rc; + } + + void Close() { + if (this->IsActive()) { + if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) { + /* TODO: panic(). */ + } + if (R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { + /* TODO: panic(). */ + } + *this = (const MappedCodeMemory){0}; + } + } }; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp new file mode 100644 index 000000000..611223b16 --- /dev/null +++ b/stratosphere/loader/source/ldr_nro.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include "ldr_nro.hpp" +#include "ldr_map.hpp" +#include "ldr_random.hpp" + +Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) { + if (header->magic != MAGIC_NRR0) { + return 0x6A09; + } + if (header->nrr_size != size) { + return 0xA409; + } + + /* TODO: Check NRR signature. */ + if (false) { + return 0x6C09; + } + + if (header->title_id_min != title_id_min) { + return 0x6A09; + } + + return 0x0; +} \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nro.hpp b/stratosphere/loader/source/ldr_nro.hpp new file mode 100644 index 000000000..ef39d41ae --- /dev/null +++ b/stratosphere/loader/source/ldr_nro.hpp @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +#define MAGIC_NRO0 0x304F524E +#define MAGIC_NRR0 0x3052524E + +class NroUtils { + public: + struct NrrHeader { + u32 magic; + u32 _0x4; + u32 _0x8; + u32 _0xC; + u64 title_id_mask; + u64 title_id_pattern; + u64 _0x20; + u64 _0x28; + u8 modulus[0x100]; + u8 fixed_key_signature[0x100]; + u8 nrr_signature[0x100]; + u64 title_id_min; + u32 nrr_size; + u32 _0x33C; + u32 hash_offset; + u32 num_hashes; + u64 _0x348; + }; + + + static_assert(sizeof(NrrHeader) == 0x350, "Incorrectly defined NrrHeader!"); + static Result ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min); +}; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 0299dbc64..a952174c2 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -104,7 +104,7 @@ void Registration::AddNsoInfo(u64 index, u64 base_address, u64 size, const unsig } -Result Registration::AddNrrInfo(u64 index, u64 base_address, u64 size, u64 code_memory_address, u64 loader_address) { +Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { Registration::Process *target_process = GetProcess(index); if (target_process == NULL) { /* TODO: panic() */ @@ -112,11 +112,8 @@ Result Registration::AddNrrInfo(u64 index, u64 base_address, u64 size, u64 code_ } for (unsigned int i = 0; i < NSO_INFO_MAX; i++) { - if (!target_process->nrr_infos[i].in_use) { - target_process->nrr_infos[i].info.base_address = base_address; - target_process->nrr_infos[i].info.size = size; - target_process->nrr_infos[i].info.code_memory_address = code_memory_address; - target_process->nrr_infos[i].info.loader_address = loader_address; + if (!target_process->nrr_infos[i].IsActive()) { + target_process->nrr_infos[i] = *nrr_info; return 0; } } diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 53deb2aa5..713d56b48 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include "ldr_map.hpp" + #define REGISTRATION_LIST_MAX (0x40) #define NSO_INFO_MAX (0x20) @@ -19,18 +21,6 @@ class Registration { NsoInfo info; }; - struct NrrInfo { - u64 base_address; - u64 size; - u64 code_memory_address; - u64 loader_address; - }; - - struct NrrInfoHolder { - bool in_use; - NrrInfo info; - }; - struct TidSid { u64 title_id; FsStorageId storage_id; @@ -44,7 +34,7 @@ class Registration { u64 title_id_min; Registration::TidSid tid_sid; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; - Registration::NrrInfoHolder nrr_infos[NRR_INFO_MAX]; + MappedCodeMemory nrr_infos[NRR_INFO_MAX]; void *owner_ro_service; }; @@ -61,6 +51,6 @@ class Registration { static bool UnregisterIndex(u64 index); static void SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); static void AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); - static Result AddNrrInfo(u64 index, u64 base_address, u64 size, u64 code_memory_address, u64 loader_address); + static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index ff4ad97df..2512d94d7 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -5,6 +5,7 @@ #include "ldr_ro_service.hpp" #include "ldr_registration.hpp" #include "ldr_map.hpp" +#include "ldr_nro.hpp" Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result rc = 0xF601; @@ -44,7 +45,8 @@ std::tuple RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { Result rc; - Registration::Process *target_proc; + Registration::Process *target_proc = NULL; + MappedCodeMemory nrr_info = {0}; if (!this->has_initialized || this->process_id != pid_desc.pid) { rc = 0xAE09; goto LOAD_NRR_END; @@ -65,11 +67,21 @@ std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u } target_proc->owner_ro_service = this; + if (R_FAILED((rc = nrr_info.Open(this->process_handle, target_proc->is_64_bit_addspace, nrr_address, nrr_size)))) { + goto LOAD_NRR_END; + } - /* TODO: Implement remainder of fucntion */ - rc = 0xDED09; + rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id_min); + if (R_SUCCEEDED(rc)) { + Registration::AddNrrInfo(target_proc->index, &nrr_info); + } LOAD_NRR_END: + if (R_FAILED(rc)) { + if (nrr_info.IsActive()) { + nrr_info.Close(); + } + } return std::make_tuple(rc); } From e7aa5c246bc9d94b4dd97113449e9ed736df6f5e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 20:37:38 -0600 Subject: [PATCH 11/21] Loader: Implement ldr:ro->UnloadNrr() --- stratosphere/loader/source/ldr_registration.cpp | 17 ++++++++++++++++- stratosphere/loader/source/ldr_registration.hpp | 1 + stratosphere/loader/source/ldr_ro_service.cpp | 17 +++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index a952174c2..c7246d5cd 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -111,7 +111,7 @@ Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { return 0x7009; } - for (unsigned int i = 0; i < NSO_INFO_MAX; i++) { + for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { if (!target_process->nrr_infos[i].IsActive()) { target_process->nrr_infos[i] = *nrr_info; return 0; @@ -120,6 +120,21 @@ Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { return 0x7009; } +Result Registration::RemoveNrrInfo(u64 index, u64 base_address) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + /* Despite the fact that this should really be a panic condition, Nintendo returns 0x1009 in this case. */ + return 0x1009; + } + + for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].base_address == base_address) { + target_process->nrr_infos[i].Close(); + return 0; + } + } + return 0xAA09; +} Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 713d56b48..8492248f0 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -52,5 +52,6 @@ class Registration { static void SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); static void AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); + static Result RemoveNrrInfo(u64 index, u64 base_address); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index 2512d94d7..d32c102d0 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -86,8 +86,21 @@ LOAD_NRR_END: } std::tuple RelocatableObjectsService::unload_nrr(PidDescriptor pid_desc, u64 nrr_address) { - /* TODO */ - return std::make_tuple(0xF601); + Registration::Process *target_proc = NULL; + if (!this->has_initialized || this->process_id != pid_desc.pid) { + return 0xAE09; + } + if (nrr_address & 0xFFF) { + return 0xA209; + } + + target_proc = Registration::GetProcessByProcessId(pid_desc.pid); + if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { + return 0xAC09; + } + target_proc->owner_ro_service = this; + + return Registration::RemoveNrrInfo(target_proc->index, nrr_address); } std::tuple RelocatableObjectsService::initialize(PidDescriptor pid_desc, CopiedHandle process_h) { From 2e7b6de195419ffd4284e9357b65d246da7d09a2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 20:50:27 -0600 Subject: [PATCH 12/21] Loader: Automatically unload NRRs on service close. --- .../loader/source/ldr_registration.cpp | 44 +++++++++++++------ .../loader/source/ldr_registration.hpp | 2 + stratosphere/loader/source/ldr_ro_service.hpp | 6 +++ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index c7246d5cd..7df25314d 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -7,8 +7,7 @@ static Registration::List g_registration_list = {0}; static u64 g_num_registered = 1; Registration::Process *Registration::GetFreeProcess() { - unsigned int i; - for (i = 0; i < REGISTRATION_LIST_MAX; i++) { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { if (!g_registration_list.processes[i].in_use) { return &g_registration_list.processes[i]; } @@ -17,24 +16,30 @@ Registration::Process *Registration::GetFreeProcess() { } Registration::Process *Registration::GetProcess(u64 index) { - unsigned int i; - for (i = 0; i < REGISTRATION_LIST_MAX && (!g_registration_list.processes[i].in_use || g_registration_list.processes[i].index != index); i++) { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { + if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].index == index) { + return &g_registration_list.processes[i]; + } } - if (i >= REGISTRATION_LIST_MAX) { - return NULL; - } - return &g_registration_list.processes[i]; + return NULL; } Registration::Process *Registration::GetProcessByProcessId(u64 pid) { - unsigned int i; - for (i = 0; i < REGISTRATION_LIST_MAX && (!g_registration_list.processes[i].in_use || g_registration_list.processes[i].process_id != pid); i++) { - + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { + if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].process_id == pid) { + return &g_registration_list.processes[i]; + } } - if (i >= REGISTRATION_LIST_MAX) { - return NULL; + return NULL; +} + +Registration::Process *Registration::GetProcessByRoService(void *service) { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { + if (g_registration_list.processes[i].in_use && g_registration_list.processes[i].owner_ro_service == service) { + return &g_registration_list.processes[i]; + } } - return &g_registration_list.processes[i]; + return NULL; } bool Registration::RegisterTidSid(const TidSid *tid_sid, u64 *out_index) { @@ -103,6 +108,17 @@ void Registration::AddNsoInfo(u64 index, u64 base_address, u64 size, const unsig } } +void Registration::CloseRoService(void *service, Handle process_h) { + Registration::Process *target_process = GetProcessByRoService(service); + if (target_process == NULL) { + return; + } + for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].process_handle == process_h) { + target_process->nrr_infos[i].Close(); + } + } +} Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { Registration::Process *target_process = GetProcess(index); diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 8492248f0..f29d9e7b3 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -46,11 +46,13 @@ class Registration { static Registration::Process *GetFreeProcess(); static Registration::Process *GetProcess(u64 index); static Registration::Process *GetProcessByProcessId(u64 pid); + static Registration::Process *GetProcessByRoService(void *service); static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out); static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); static bool UnregisterIndex(u64 index); static void SetProcessIdTidMinAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid_min, bool is_64_bit_addspace); static void AddNsoInfo(u64 index, u64 base_address, u64 size, const unsigned char *build_id); + static void CloseRoService(void *service, Handle process_h); static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); static Result RemoveNrrInfo(u64 index, u64 base_address); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); diff --git a/stratosphere/loader/source/ldr_ro_service.hpp b/stratosphere/loader/source/ldr_ro_service.hpp index f2a02f252..f47d9849b 100644 --- a/stratosphere/loader/source/ldr_ro_service.hpp +++ b/stratosphere/loader/source/ldr_ro_service.hpp @@ -18,6 +18,12 @@ class RelocatableObjectsService : IServiceObject { bool has_initialized; public: RelocatableObjectsService() : process_handle(0), process_id(U64_MAX), has_initialized(false) { } + ~RelocatableObjectsService() { + Registration::CloseRoService(this, this->process_handle); + if (this->has_initialized) { + svcCloseHandle(this->process_handle); + } + } virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); virtual Result handle_deferred() { /* This service will never defer. */ From e43c6df98664193f3325543955fdf0354aede2c0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 26 Apr 2018 20:51:12 -0600 Subject: [PATCH 13/21] Loader: fix missing NULL assignment --- stratosphere/loader/source/ldr_registration.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 7df25314d..900499ef2 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -118,6 +118,7 @@ void Registration::CloseRoService(void *service, Handle process_h) { target_process->nrr_infos[i].Close(); } } + target_process->owner_ro_service = NULL; } Result Registration::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { From 10171313dfceb7d466db1e96cd30a4e1e0b5e67b Mon Sep 17 00:00:00 2001 From: Kurt Date: Thu, 26 Apr 2018 20:45:11 -0700 Subject: [PATCH 14/21] Update README.md (#70) --- thermosphere/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thermosphere/README.md b/thermosphere/README.md index 6b4e9df6d..cb8f6d5df 100644 --- a/thermosphere/README.md +++ b/thermosphere/README.md @@ -1,6 +1,6 @@ -Thermoshère +Thermosphère ===== ![License](https://img.shields.io/badge/License-GPLv2-blue.svg) -Thermoshère is a hypervisor for the Nintendo Switch. +Thermosphère is a hypervisor for the Nintendo Switch. From 772e41971da571bd66e756f2debebbcd3d5b099f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 27 Apr 2018 03:17:07 -0600 Subject: [PATCH 15/21] Loader: Add ldr:ro->LoadNro() --- stratosphere/loader/source/ldr_map.hpp | 73 ++++++++++----- stratosphere/loader/source/ldr_nro.cpp | 88 +++++++++++++++++++ stratosphere/loader/source/ldr_nro.hpp | 23 +++++ .../loader/source/ldr_registration.cpp | 70 +++++++++++++++ .../loader/source/ldr_registration.hpp | 19 ++++ stratosphere/loader/source/ldr_ro_service.cpp | 40 ++++++++- 6 files changed, 288 insertions(+), 25 deletions(-) diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 3a0c3885f..fb8fcc2fc 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -77,13 +77,16 @@ struct MappedCodeMemory { void *mapped_address; bool IsActive() { + return this->code_memory_address != 0; + } + + bool IsMapped() { return this->mapped_address != NULL; } /* Utility functions. */ Result Open(Handle process_h, bool is_64_bit_address_space, u64 address, u64 size) { Result rc; - u64 try_address; if (this->IsActive()) { return 0x19009; } @@ -91,40 +94,64 @@ struct MappedCodeMemory { this->process_handle = process_h; this->base_address = address; this->size = size; - - if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(process_h, is_64_bit_address_space, address, size, &this->code_memory_address)))) { - goto CODE_MEMORY_OPEN_END; - } - - if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { - goto CODE_MEMORY_OPEN_END; + + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(this->process_handle, is_64_bit_address_space, this->base_address, this->size, &this->code_memory_address)))) { + Close(); + } + return rc; + } + + Result OpenAtAddress(Handle process_h, u64 address, u64 size, u64 target_code_memory_address) { + Result rc; + if (this->IsActive()) { + return 0x19009; } + this->process_handle = process_h; + this->base_address = address; + this->size = size; - if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, process_h, try_address, size)))) { - goto CODE_MEMORY_OPEN_END; - } - - this->mapped_address = (void *)try_address; - - CODE_MEMORY_OPEN_END: - if (R_FAILED(rc)) { - if (this->code_memory_address && R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { - /* TODO: panic(). */ - } - *this = (const MappedCodeMemory){0}; + if (R_SUCCEEDED((rc = svcMapProcessCodeMemory(this->process_handle, target_code_memory_address, this->base_address, this->size)))) { + this->code_memory_address = target_code_memory_address; + } else { + Close(); } return rc; } - void Close() { - if (this->IsActive()) { + Result Map() { + Result rc; + u64 try_address; + if (this->IsMapped()) { + return 0x19009; + } + if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { + return rc; + } + + if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, this->process_handle, try_address, size)))) { + return rc; + } + + this->mapped_address = (void *)try_address; + return rc; + } + + void Unmap() { + if (this->IsMapped()) { if (R_FAILED(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size))) { /* TODO: panic(). */ } + } + this->mapped_address = NULL; + } + + void Close() { + Unmap(); + if (this->IsActive()) { if (R_FAILED(svcUnmapProcessCodeMemory(this->process_handle, this->code_memory_address, this->base_address, this->size))) { /* TODO: panic(). */ } - *this = (const MappedCodeMemory){0}; } + *this = (const MappedCodeMemory){0}; } }; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp index 611223b16..3f95c7a50 100644 --- a/stratosphere/loader/source/ldr_nro.cpp +++ b/stratosphere/loader/source/ldr_nro.cpp @@ -4,6 +4,7 @@ #include #include #include "ldr_nro.hpp" +#include "ldr_registration.hpp" #include "ldr_map.hpp" #include "ldr_random.hpp" @@ -24,5 +25,92 @@ Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min return 0x6A09; } + return 0x0; +} + +Result NroUtils::LoadNro(Registration::Process *target_proc, Handle process_h, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size, u64 *out_address) { + NroHeader *nro; + MappedCodeMemory mcm_nro; + MappedCodeMemory mcm_bss; + unsigned int i; + Result rc; + u8 nro_hash[0x20]; + /* Ensure there is an available NRO slot. */ + for (i = 0; i < NRO_INFO_MAX; i++) { + if (!target_proc->nro_infos[i].in_use) { + break; + } + } + if (i >= NRO_INFO_MAX) { + return 0x6E09; + } + for (i = 0; i < 0x200; i++) { + if (R_SUCCEEDED(mcm_nro.Open(process_h, target_proc->is_64_bit_addspace, nro_heap_address, nro_heap_size))) { + if (R_SUCCEEDED(mcm_bss.OpenAtAddress(process_h, bss_heap_address, bss_heap_size, nro_heap_address + nro_heap_size))) { + break; + } else { + mcm_nro.Close(); + } + } + } + if (i >= 0x200) { + return 0x6609; + } + if (R_FAILED((rc = mcm_nro.Map()))) { + goto LOAD_NRO_END; + } + + nro = (NroHeader *)mcm_nro.mapped_address; + if (nro->magic != MAGIC_NRO0) { + rc = 0x6809; + goto LOAD_NRO_END; + } + if (nro->nro_size != nro_heap_size || nro->bss_size != bss_heap_size) { + rc = 0x6809; + goto LOAD_NRO_END; + } + if ((nro->text_size & 0xFFF) || (nro->ro_size & 0xFFF) || (nro->rw_size & 0xFFF) || (nro->bss_size & 0xFFF)) { + rc = 0x6809; + goto LOAD_NRO_END; + } + if (nro->text_offset != 0 || nro->text_offset + nro->text_size != nro->ro_offset || nro->ro_offset + nro->ro_size != nro->rw_offset || nro->rw_offset + nro->rw_size != nro->nro_size) { + rc = 0x6809; + goto LOAD_NRO_END; + } + + picosha2::hash256((u8 *)nro, (u8 *)nro + nro->nro_size, nro_hash, nro_hash + sizeof(nro_hash)); + + if (!Registration::IsNroHashPresent(target_proc->index, nro_hash)) { + rc = 0x6C09; + goto LOAD_NRO_END; + } + + if (Registration::IsNroAlreadyLoaded(target_proc->index, nro_hash)) { + rc = 0x7209; + goto LOAD_NRO_END; + } + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address, nro->text_size, 5)))) { + goto LOAD_NRO_END; + } + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro->ro_offset, nro->ro_size, 1)))) { + goto LOAD_NRO_END; + } + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro->rw_offset, nro->rw_size + nro->bss_size, 3)))) { + goto LOAD_NRO_END; + } + + Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro->text_size, nro->ro_size, nro->rw_size, nro->build_id); + mcm_nro.Unmap(); + mcm_bss.Unmap(); + rc = 0x0; + +LOAD_NRO_END: + if (R_FAILED(rc)) { + mcm_nro.Close(); + mcm_bss.Close(); + } return 0x0; } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_nro.hpp b/stratosphere/loader/source/ldr_nro.hpp index ef39d41ae..497278fad 100644 --- a/stratosphere/loader/source/ldr_nro.hpp +++ b/stratosphere/loader/source/ldr_nro.hpp @@ -2,6 +2,7 @@ #include #include +#include "ldr_registration.hpp" #define MAGIC_NRO0 0x304F524E #define MAGIC_NRR0 0x3052524E @@ -27,7 +28,29 @@ class NroUtils { u64 _0x348; }; + struct NroHeader { + u32 entrypoint_insn; + u32 mod_offset; + u64 padding; + u32 magic; + u32 _0x14; + u32 nro_size; + u32 _0x1C; + u32 text_offset; + u32 text_size; + u32 ro_offset; + u32 ro_size; + u32 rw_offset; + u32 rw_size; + u32 bss_size; + u32 _0x3C; + unsigned char build_id[0x20]; + u8 _0x60[0x20]; + }; + static_assert(sizeof(NrrHeader) == 0x350, "Incorrectly defined NrrHeader!"); + static_assert(sizeof(NroHeader) == 0x80, "Incorrectly defined NroHeader!"); static Result ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min); + static Result LoadNro(Registration::Process *target_proc, Handle process_h, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size, u64 *out_address); }; \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 900499ef2..2f9578fb9 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -1,7 +1,9 @@ #include #include #include +#include #include "ldr_registration.hpp" +#include "ldr_nro.hpp" static Registration::List g_registration_list = {0}; static u64 g_num_registered = 1; @@ -153,6 +155,74 @@ Result Registration::RemoveNrrInfo(u64 index, u64 base_address) { return 0xAA09; } + +bool Registration::IsNroHashPresent(u64 index, u8 *nro_hash) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + /* TODO: panic */ + return false; + } + + for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + if (target_process->nrr_infos[i].IsActive()) { + NroUtils::NrrHeader *nrr = (NroUtils::NrrHeader *)target_process->nrr_infos[i].mapped_address; + /* Binary search. */ + int low = 0, high = (int)(nrr->num_hashes - 1); + while (low <= high) { + int mid = (low + high) / 2; + u8 *hash_in_nrr = (u8 *)nrr + nrr->hash_offset + 0x20 * mid; + int ret = std::memcmp(hash_in_nrr, nro_hash, 0x20); + if (ret == 0) { + return true; + } else if (ret > 0) { + high = mid - 1; + } else { + low = mid + 1; + } + } + } + } + return false; +} + +bool Registration::IsNroAlreadyLoaded(u64 index, u8 *build_id) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + /* TODO: panic */ + return true; + } + + for (unsigned int i = 0; i < NRO_INFO_MAX; i++) { + if (target_process->nro_infos[i].in_use && std::memcmp(target_process->nro_infos[i].build_id, build_id, 0x20) == 0) { + return true; + } + } + return false; +} + +void Registration::AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + /* TODO: panic */ + return; + } + + for (unsigned int i = 0; i < NRO_INFO_MAX; i++) { + if (!target_process->nro_infos[i].in_use) { + target_process->nro_infos[i].base_address = nro->code_memory_address; + target_process->nro_infos[i].nro_heap_address = nro->base_address; + target_process->nro_infos[i].nro_heap_size = nro->size; + target_process->nro_infos[i].bss_heap_address = bss->base_address; + target_process->nro_infos[i].bss_heap_size = bss->size; + target_process->nro_infos[i].text_size = text_size; + target_process->nro_infos[i].ro_size = ro_size; + target_process->nro_infos[i].rw_size = rw_size; + std::copy(build_id, build_id + sizeof(target_process->nro_infos[i].build_id), target_process->nro_infos[i].build_id); + target_process->nro_infos[i].in_use = true; + } + } +} + Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index f29d9e7b3..dc79995ec 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -7,6 +7,7 @@ #define NSO_INFO_MAX (0x20) #define NRR_INFO_MAX (0x40) +#define NRO_INFO_MAX (0x40) class Registration { public: @@ -21,6 +22,20 @@ class Registration { NsoInfo info; }; + struct NroInfo { + bool in_use; + u64 base_address; + u64 total_mapped_size; + u64 nro_heap_address; + u64 nro_heap_size; + u64 bss_heap_address; + u64 bss_heap_size; + u64 text_size; + u64 ro_size; + u64 rw_size; + unsigned char build_id[0x20]; + }; + struct TidSid { u64 title_id; FsStorageId storage_id; @@ -34,6 +49,7 @@ class Registration { u64 title_id_min; Registration::TidSid tid_sid; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; + Registration::NroInfo nro_infos[NRO_INFO_MAX]; MappedCodeMemory nrr_infos[NRR_INFO_MAX]; void *owner_ro_service; }; @@ -55,5 +71,8 @@ class Registration { static void CloseRoService(void *service, Handle process_h); static Result AddNrrInfo(u64 index, MappedCodeMemory *nrr_info); static Result RemoveNrrInfo(u64 index, u64 base_address); + static bool IsNroHashPresent(u64 index, u8 *nro_hash); + static bool IsNroAlreadyLoaded(u64 index, u8 *build_id); + static void AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index d32c102d0..1e6b23206 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -34,8 +34,40 @@ Result RelocatableObjectsService::dispatch(IpcParsedCommand &r, IpcCommand &out_ std::tuple RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - /* TODO */ - return std::make_tuple(0xF601, 0); + Result rc; + u64 out_address = 0; + Registration::Process *target_proc = NULL; + if (!this->has_initialized || this->process_id != pid_desc.pid) { + rc = 0xAE09; + goto LOAD_NRO_END; + } + if (nro_address & 0xFFF) { + rc = 0xA209; + goto LOAD_NRO_END; + } + if (nro_address + nro_size <= nro_address || !nro_size || (nro_size & 0xFFF)) { + rc = 0xA409; + goto LOAD_NRO_END; + } + if (bss_size && bss_address + bss_size <= bss_address) { + rc = 0xA409; + goto LOAD_NRO_END; + } + /* Ensure no overflow for combined sizes. */ + if (U64_MAX - nro_size < bss_size) { + rc = 0xA409; + goto LOAD_NRO_END; + } + target_proc = Registration::GetProcessByProcessId(pid_desc.pid); + if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { + rc = 0xAC09; + goto LOAD_NRO_END; + } + target_proc->owner_ro_service = this; + + rc = NroUtils::LoadNro(target_proc, this->process_handle, nro_address, nro_size, bss_address, bss_size, &out_address); +LOAD_NRO_END: + return std::make_tuple(rc, out_address); } std::tuple RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, u64 nro_address) { @@ -71,6 +103,10 @@ std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u goto LOAD_NRR_END; } + if (R_FAILED((rc = nrr_info.Map()))) { + goto LOAD_NRR_END; + } + rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id_min); if (R_SUCCEEDED(rc)) { Registration::AddNrrInfo(target_proc->index, &nrr_info); From 4e1a29f618d93447d57030b85dc45f538402a57d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 27 Apr 2018 03:33:44 -0600 Subject: [PATCH 16/21] Loader: Finish ldr:ro --- stratosphere/loader/source/ldr_nro.cpp | 1 + .../loader/source/ldr_registration.cpp | 23 +++++++++++++++++++ .../loader/source/ldr_registration.hpp | 1 + stratosphere/loader/source/ldr_ro_service.cpp | 17 ++++++++++++-- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp index 3f95c7a50..120587fd6 100644 --- a/stratosphere/loader/source/ldr_nro.cpp +++ b/stratosphere/loader/source/ldr_nro.cpp @@ -103,6 +103,7 @@ Result NroUtils::LoadNro(Registration::Process *target_proc, Handle process_h, u } Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro->text_size, nro->ro_size, nro->rw_size, nro->build_id); + *out_address = mcm_nro.code_memory_address; mcm_nro.Unmap(); mcm_bss.Unmap(); rc = 0x0; diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 2f9578fb9..ab0fce037 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -223,6 +223,29 @@ void Registration::AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeM } } +Result Registration::RemoveNroInfo(u64 index, Handle process_h, u64 nro_heap_address) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + return 0xA809; + } + + for (unsigned int i = 0; i < NRR_INFO_MAX; i++) { + if (target_process->nro_infos[i].in_use && target_process->nro_infos[i].nro_heap_address == nro_heap_address) { + NroInfo *info = &target_process->nro_infos[i]; + Result rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size + info->rw_size, info->bss_heap_address, info->bss_heap_size); + if (R_SUCCEEDED(rc)) { + rc = svcUnmapProcessCodeMemory(process_h, info->base_address + info->text_size + info->ro_size, nro_heap_address + info->text_size + info->ro_size, info->rw_size); + if (R_SUCCEEDED(rc)) { + rc = svcUnmapProcessCodeMemory(process_h, info->base_address, nro_heap_address, info->text_size + info->ro_size); + } + } + target_process->nro_infos[i] = (const NroInfo ){0}; + return rc; + } + } + return 0xA809; +} + Result Registration::GetNsoInfosForProcessId(Registration::NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index dc79995ec..4df3bd05d 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -74,5 +74,6 @@ class Registration { static bool IsNroHashPresent(u64 index, u8 *nro_hash); static bool IsNroAlreadyLoaded(u64 index, u8 *build_id); static void AddNroToProcess(u64 index, MappedCodeMemory *nro, MappedCodeMemory *bss, u32 text_size, u32 ro_size, u32 rw_size, u8 *build_id); + static Result RemoveNroInfo(u64 index, Handle process_h, u64 base_address); static Result GetNsoInfosForProcessId(NsoInfo *out, u32 max_out, u64 process_id, u32 *num_written); }; diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp index 1e6b23206..020db98c1 100644 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -71,8 +71,21 @@ LOAD_NRO_END: } std::tuple RelocatableObjectsService::unload_nro(PidDescriptor pid_desc, u64 nro_address) { - /* TODO */ - return std::make_tuple(0xF601); + Registration::Process *target_proc = NULL; + if (!this->has_initialized || this->process_id != pid_desc.pid) { + return 0xAE09; + } + if (nro_address & 0xFFF) { + return 0xA209; + } + + target_proc = Registration::GetProcessByProcessId(pid_desc.pid); + if (target_proc == NULL || (target_proc->owner_ro_service != NULL && (RelocatableObjectsService *)(target_proc->owner_ro_service) != this)) { + return 0xAC09; + } + target_proc->owner_ro_service = this; + + return Registration::RemoveNroInfo(target_proc->index, this->process_handle, nro_address); } std::tuple RelocatableObjectsService::load_nrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { From 0d25f342c6e6f6814ceea8fd7869ea64ec888dd4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 27 Apr 2018 03:56:06 -0600 Subject: [PATCH 17/21] Add banner to README --- README.md | 2 ++ img/banner.png | Bin 0 -> 13849 bytes img/banner_light.png | Bin 0 -> 14121 bytes 3 files changed, 2 insertions(+) create mode 100644 img/banner.png create mode 100644 img/banner_light.png diff --git a/README.md b/README.md index 400b84b9b..ec8e7140d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Atmosphère-NX ===== +![Banner](img/banner.png?raw=true) + ![License](https://img.shields.io/badge/License-GPLv2-blue.svg) Atmosphère is a work-in-progress customized firmware for the Nintendo Switch. diff --git a/img/banner.png b/img/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..c056934152b84f781c2484b1bfa40d7b399180ee GIT binary patch literal 13849 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 literal 0 HcmV?d00001 diff --git a/img/banner_light.png b/img/banner_light.png new file mode 100644 index 0000000000000000000000000000000000000000..e7de7448380523a7402d9e98eefa6d4ac85bfeee GIT binary patch literal 14121 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 Date: Fri, 27 Apr 2018 03:56:48 -0600 Subject: [PATCH 18/21] Update banner placement in README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ec8e7140d..839374deb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -Atmosphère-NX -===== ![Banner](img/banner.png?raw=true) +===== ![License](https://img.shields.io/badge/License-GPLv2-blue.svg) From 52730d218826f361fa1785a388943c7b2338703b Mon Sep 17 00:00:00 2001 From: hexkyz Date: Mon, 30 Apr 2018 17:48:03 +0100 Subject: [PATCH 19/21] Adding credits section --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 839374deb..e106cd567 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,17 @@ ![License](https://img.shields.io/badge/License-GPLv2-blue.svg) Atmosphère is a work-in-progress customized firmware for the Nintendo Switch. + +Credits +===== + +Atmosphère is currently being developed and maintained by __SciresM__, __TuxSH__, __ktemkin__ and __hexkyz__.
+In no particular order, we credit the following for their invaluable contributions: + +* __switchbrew__ for the [libnx](https://github.com/switchbrew/libnx) project and the extensive [documentation, research and tool development](http://switchbrew.org) pertaining to the Nintendo Switch. +* __devkitPro__ for the [devkitA64](https://devkitpro.org/) toolchain and libnx support. +* __ReSwitched Team__ for additional [documentation, research and tool development](https://reswitched.tech/) pertaining to the Nintendo Switch. +* __naehrwert__ for the [hekate](https://github.com/nwert/hekate) project and it's hwinit code base. +* __hedgeberg__ for research and hardware testing. +* __lioncash__ for code cleanup and general improvements. +* _All those who actively contribute to the Atmosphère repository._ From 30f975a558d03e83b1020bca0f577fdd3d997d2a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 30 Apr 2018 22:27:07 -0600 Subject: [PATCH 20/21] Stratosphere: Fix remaining bugs in sm, which now works as a KIP1 on hardware --- .../source/waitablemanager.cpp | 17 +++---- stratosphere/sm/source/sm_main.cpp | 2 +- stratosphere/sm/source/sm_manager_service.cpp | 1 - stratosphere/sm/source/sm_registration.cpp | 48 +++++++------------ stratosphere/sm/source/sm_registration.hpp | 2 +- stratosphere/sm/source/sm_user_service.cpp | 5 +- 6 files changed, 30 insertions(+), 45 deletions(-) diff --git a/stratosphere/libstratosphere/source/waitablemanager.cpp b/stratosphere/libstratosphere/source/waitablemanager.cpp index 32db56cfc..bc80e27e4 100644 --- a/stratosphere/libstratosphere/source/waitablemanager.cpp +++ b/stratosphere/libstratosphere/source/waitablemanager.cpp @@ -40,14 +40,8 @@ void WaitableManager::process() { handles.resize(signalables.size()); std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); }); - unsigned int num_not_deferred = 0; - for (auto & signalable : signalables) { - if (!signalable->get_deferred()) { - num_not_deferred++; - } - } - rc = svcWaitSynchronization(&handle_index, handles.data(), num_not_deferred, this->timeout); + rc = svcWaitSynchronization(&handle_index, handles.data(), signalables.size(), this->timeout); if (R_SUCCEEDED(rc)) { /* Handle a signaled waitable. */ /* TODO: What timeout should be passed here? */ @@ -82,12 +76,19 @@ void WaitableManager::process() { delete signalables[handle_index]; } + /* If relevant, remove from signalables. */ + signalables.erase(std::remove(signalables.begin(), signalables.end(), signalables[handle_index]), signalables.end()); + for (int i = 0; i < handle_index; i++) { signalables[i]->update_priority(); } } /* Do deferred callback for each waitable. */ - std::for_each(signalables.begin() + num_not_deferred, signalables.end(), [](IWaitable *w) { w->handle_deferred(); }); + for (auto & waitable : signalables) { + if (waitable->get_deferred()) { + waitable->handle_deferred(); + } + } } } \ No newline at end of file diff --git a/stratosphere/sm/source/sm_main.cpp b/stratosphere/sm/source/sm_main.cpp index 6a20fa4f1..21c526565 100644 --- a/stratosphere/sm/source/sm_main.cpp +++ b/stratosphere/sm/source/sm_main.cpp @@ -48,7 +48,7 @@ void __appExit(void) { int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); - + /* TODO: What's a good timeout value to use here? */ WaitableManager *server_manager = new WaitableManager(U64_MAX); diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index d21236040..49247ec5c 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -4,7 +4,6 @@ Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result rc = 0xF601; - switch ((ManagerServiceCmd)cmd_id) { case Manager_Cmd_RegisterProcess: rc = WrapIpcCommandImpl<&ManagerService::register_process>(this, r, out_c, pointer_buffer, pointer_buffer_size); diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index 400fffa9d..a6483cbe9 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -6,6 +6,15 @@ static Registration::Process g_process_list[REGISTRATION_LIST_MAX_PROCESS] = {0}; static Registration::Service g_service_list[REGISTRATION_LIST_MAX_SERVICE] = {0}; +u64 GetServiceNameLength(u64 service) { + u64 service_name_len = 0; + while (service & 0xFF) { + service_name_len++; + service >>= 8; + } + return service_name_len; +} + /* Utilities. */ Registration::Process *Registration::GetProcessForPid(u64 pid) { for (unsigned int i = 0; i < REGISTRATION_LIST_MAX_PROCESS; i++) { @@ -162,13 +171,10 @@ Result Registration::GetServiceForPid(u64 pid, u64 service, Handle *out) { return 0xC15; } - u64 service_name_len = 0; - while ((service >> (8 * service_name_len)) & 0xFF) { - service_name_len++; - } + u64 service_name_len = GetServiceNameLength(service); /* If the service has bytes after a null terminator, that's no good. */ - if ((service >> (8 * service_name_len))) { + if (service_name_len != 8 && (service >> (8 * service_name_len))) { return 0xC15; } @@ -191,13 +197,10 @@ Result Registration::RegisterServiceForPid(u64 pid, u64 service, u64 max_session return 0xC15; } - u64 service_name_len = 0; - while ((service >> (8 * service_name_len)) & 0xFF) { - service_name_len++; - } + u64 service_name_len = GetServiceNameLength(service); /* If the service has bytes after a null terminator, that's no good. */ - if ((service >> (8 * service_name_len))) { + if (service_name_len != 8 && (service >> (8 * service_name_len))) { return 0xC15; } @@ -240,13 +243,10 @@ Result Registration::RegisterServiceForSelf(u64 service, u64 max_sessions, bool return rc; } - u64 service_name_len = 0; - while ((service >> (8 * service_name_len)) & 0xFF) { - service_name_len++; - } + u64 service_name_len = GetServiceNameLength(service); /* If the service has bytes after a null terminator, that's no good. */ - if ((service >> (8 * service_name_len))) { + if (service_name_len != 8 && (service >> (8 * service_name_len))) { return 0xC15; } @@ -276,27 +276,13 @@ Result Registration::UnregisterServiceForPid(u64 pid, u64 service) { return 0xC15; } - u64 service_name_len = 0; - while ((service >> (8 * service_name_len)) & 0xFF) { - service_name_len++; - } + u64 service_name_len = GetServiceNameLength(service); /* If the service has bytes after a null terminator, that's no good. */ - if ((service >> (8 * service_name_len))) { + if (service_name_len != 8 && (service >> (8 * service_name_len))) { return 0xC15; } - if (pid >= REGISTRATION_PID_BUILTIN_MAX) { - Registration::Process *proc = GetProcessForPid(pid); - if (proc == NULL) { - return 0x415; - } - - if (!IsValidForSac(proc->sac, proc->sac_size, service, true)) { - return 0x1015; - } - } - Registration::Service *target_service = GetService(service); if (target_service == NULL) { return 0xE15; diff --git a/stratosphere/sm/source/sm_registration.hpp b/stratosphere/sm/source/sm_registration.hpp index 2a19be857..7ac0f0f89 100644 --- a/stratosphere/sm/source/sm_registration.hpp +++ b/stratosphere/sm/source/sm_registration.hpp @@ -4,7 +4,7 @@ #define REGISTRATION_LIST_MAX_PROCESS (0x40) #define REGISTRATION_LIST_MAX_SERVICE (0x100) #define REGISTRATION_MAX_SAC_SIZE (0x200) -#define REGISTRATION_PID_BUILTIN_MAX 0x7 +#define REGISTRATION_PID_BUILTIN_MAX 0x50 class Registration { public: diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index f57f45858..f05070c3d 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -4,8 +4,7 @@ #include "sm_registration.hpp" Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { - Result rc = 0xF601; - + Result rc = 0xF601; switch ((UserServiceCmd)cmd_id) { case User_Cmd_Initialize: rc = WrapIpcCommandImpl<&UserService::initialize>(this, r, out_c, pointer_buffer, pointer_buffer_size); @@ -27,7 +26,7 @@ Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, Result UserService::handle_deferred() { /* If we're deferred, GetService failed. */ - return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service); + return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service);; } From 6a7afc5ce886e0d5b01fd0538e02ba715282af4b Mon Sep 17 00:00:00 2001 From: hexkyz Date: Tue, 1 May 2018 17:17:34 +0100 Subject: [PATCH 21/21] Fix typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e106cd567..c8564ba65 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ In no particular order, we credit the following for their invaluable contributio * __switchbrew__ for the [libnx](https://github.com/switchbrew/libnx) project and the extensive [documentation, research and tool development](http://switchbrew.org) pertaining to the Nintendo Switch. * __devkitPro__ for the [devkitA64](https://devkitpro.org/) toolchain and libnx support. * __ReSwitched Team__ for additional [documentation, research and tool development](https://reswitched.tech/) pertaining to the Nintendo Switch. -* __naehrwert__ for the [hekate](https://github.com/nwert/hekate) project and it's hwinit code base. +* __naehrwert__ for the [hekate](https://github.com/nwert/hekate) project and its hwinit code base. * __hedgeberg__ for research and hardware testing. * __lioncash__ for code cleanup and general improvements. * _All those who actively contribute to the Atmosphère repository._