diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 5c53aa250..01ed21f68 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -180,7 +180,7 @@ int main(int argc, char **argv) server_manager->AddWaitable(new ServiceServer("pm:shell", 3)); server_manager->AddWaitable(new ServiceServer("pm:dmnt", 2)); server_manager->AddWaitable(new ServiceServer("pm:bm", 6)); - server_manager->AddWaitable(new ServiceServer("pm:info", 1)); + server_manager->AddWaitable(new ServiceServer("pm:info", 2)); /* Loop forever, servicing our services. */ server_manager->Process(); diff --git a/stratosphere/ro/ro.json b/stratosphere/ro/ro.json index 8b4efa28c..57d4d3bf3 100644 --- a/stratosphere/ro/ro.json +++ b/stratosphere/ro/ro.json @@ -14,7 +14,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv"], + "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv", "pm:info"], "service_host": ["ldr:ro", "ro:dmnt", "ro:1"], "kernel_capabilities": [{ "type": "kernel_flags", diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 1a3787677..cd5f9e807 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -25,6 +25,7 @@ #include "ro_debug_monitor.hpp" #include "ro_service.hpp" +#include "ro_registration.hpp" extern "C" { extern u32 __start__; @@ -77,16 +78,18 @@ void __appInit(void) { if (R_FAILED(rc)) { std::abort(); } - - rc = splInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } rc = fsInitialize(); if (R_FAILED(rc)) { std::abort(); } + + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } rc = fsdevMountSdmc(); if (R_FAILED(rc)) { @@ -99,23 +102,29 @@ void __appInit(void) { void __appExit(void) { fsdevUnmountAll(); fsExit(); - splExit(); + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + pminfoExit(); + } setsysExit(); smExit(); } /* Helpers to create RO objects. */ -static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; -static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForOthers); }; +static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoModuleType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoModuleType_ForOthers); }; int main(int argc, char **argv) { + /* Initialize. */ + Registration::Initialize(); + /* Static server manager. */ static auto s_server_manager = WaitableManager(1); /* Create services. */ s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); - s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + /* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */ + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 2)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); } diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index fe2726e76..03a8c7941 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -21,9 +21,30 @@ #include "ro_registration.hpp" -/* Declare process contexts as static to this function. */ +/* Declare process contexts as global array. */ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; +static bool g_is_development_hardware, g_is_development_function_enabled; + +void Registration::Initialize() { + if (R_FAILED(splInitialize())) { + std::abort(); + } + ON_SCOPE_EXIT { splExit(); }; + + if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { + std::abort(); + } + + { + u64 out_val = 0; + if (R_FAILED(splGetConfig(SplConfigItem_IsDebugMode, &out_val))) { + std::abort(); + } + g_is_development_function_enabled = out_val != 0; + } +} + Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { /* Check if a process context already exists. */ for (size_t i = 0; i < Registration::MaxSessions; i++) { @@ -56,6 +77,7 @@ void Registration::UnregisterProcess(RoProcessContext *context) { } } } + std::memset(context, 0, sizeof(*context)); } Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { @@ -85,6 +107,83 @@ Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_ return ResultSuccess; } +Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type) { + /* Validate address/size. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if (nrr_size == 0 || (nrr_size & 0xFFF) || !(nrr_address < nrr_address + nrr_size)) { + return ResultRoInvalidSize; + } + + /* Check we have space for a new NRR. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoTooManyNrr; + } + + NrrInfo *nrr_info = &context->nrr_infos[slot]; + + /* Map. */ + NrrHeader *header = nullptr; + u64 mapped_code_address = 0; + Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size); + if (R_FAILED(rc)) { + return rc; + } + + /* Set NRR info. */ + nrr_info->header = header; + nrr_info->nrr_heap_address = nrr_address; + nrr_info->nrr_heap_size = nrr_size; + nrr_info->mapped_code_address = mapped_code_address; + context->nrr_in_use[slot] = true; + + /* TODO. */ + return ResultSuccess; +} + +Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { + /* Validate address. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + + /* Check the NRR is loaded. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + continue; + } + + if (context->nrr_infos[slot].nrr_heap_address == nrr_address) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoNotRegistered; + } + + /* Unmap. */ + const NrrInfo nrr_info = context->nrr_infos[slot]; + { + /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ + context->nrr_in_use[slot] = false; + std::memset(&context->nrr_infos[slot], 0, sizeof(context->nrr_infos[slot])); + } + return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); +} + +Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { Result rc = svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size); if (R_FAILED(rc)) { diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index b869d7e4d..bc1057c33 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -19,9 +19,15 @@ #include +enum RoModuleType : u32 { + RoModuleType_ForSelf = 0, + RoModuleType_ForOthers = 1, +}; + class Registration { public: - static constexpr size_t MaxSessions = 0x8; + /* NOTE: 2 ldr:ro, 2 ro:1. Nintendo only actually supports 2 total, but we'll be a little more generous. */ + static constexpr size_t MaxSessions = 0x4; static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; public: @@ -98,11 +104,17 @@ class Registration { u64 process_id; bool in_use; }; + private: + static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size); + static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); public: + static void Initialize(); + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); static void UnregisterProcess(RoProcessContext *context); + + static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type); + static Result UnloadNrr(RoProcessContext *context, u64 nrr_address); static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); - - static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); }; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index 8dc2bd504..d384fe282 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -28,6 +28,34 @@ RelocatableObjectsService::~RelocatableObjectsService() { } } +bool RelocatableObjectsService::IsProcessIdValid(u64 process_id) { + if (!this->IsInitialized()) { + return false; + } + + return this->context->process_id == process_id; +} + +u64 RelocatableObjectsService::GetTitleId(Handle process_handle) { + u64 title_id = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + /* 3.0.0+: Use svcGetInfo. */ + if (R_FAILED(svcGetInfo(&title_id, 18, process_handle, 0))) { + std::abort(); + } + } else { + /* 1.0.0-2.3.0: We're not inside loader, so ask pm. */ + u64 process_id = 0; + if (R_FAILED(svcGetProcessId(&process_id, process_handle))) { + std::abort(); + } + if (R_FAILED(pminfoGetTitleId(&title_id, process_id))) { + std::abort(); + } + } + return title_id; +} + Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { /* TODO */ return ResultKernelConnectionClosed; @@ -39,21 +67,35 @@ Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_addr } Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(this->context->process_handle), nrr_address, nrr_size, RoModuleType_ForSelf, true); } Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNrr(this->context, nrr_address); } Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { - /* TODO */ - return ResultKernelConnectionClosed; + /* Validate the input pid/process handle. */ + u64 handle_pid = 0; + if (R_FAILED(svcGetProcessId(&handle_pid, process_h.handle)) || handle_pid != pid_desc.pid) { + return ResultRoInvalidProcess; + } + + return Registration::RegisterProcess(&this->context, process_h.handle, pid_desc.pid); } Result RelocatableObjectsService::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(process_h.handle), nrr_address, nrr_size, this->type, this->type == RoModuleType_ForOthers); } \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp index 2d68f84bd..6354d0805 100644 --- a/stratosphere/ro/source/ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -30,16 +30,12 @@ enum RoServiceCmd { Ro_Cmd_LoadNrrEx = 10, }; -enum RoServiceType : u32 { - RoServiceType_ForSelf = 0, - RoServiceType_ForOthers = 1, -}; - class RelocatableObjectsService final : public IServiceObject { - Registration::RoProcessContext *context = nullptr; - RoServiceType type; + private: + Registration::RoProcessContext *context = nullptr; + RoModuleType type; public: - explicit RelocatableObjectsService(RoServiceType t) : type(t) { + explicit RelocatableObjectsService(RoModuleType t) : type(t) { /* ... */ } virtual ~RelocatableObjectsService() override; @@ -47,6 +43,8 @@ class RelocatableObjectsService final : public IServiceObject { bool IsInitialized() const { return this->context != nullptr; } + bool IsProcessIdValid(u64 process_id); + static u64 GetTitleId(Handle process_handle); private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);