diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp index fb26fa66b..ef6897c60 100644 --- a/stratosphere/ro/source/ro_debug_monitor.cpp +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -15,12 +15,15 @@ */ #include -#include -#include #include +#include + #include "ro_debug_monitor.hpp" +#include "ro_registration.hpp" Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { - /* TODO: Implement. */ - return ResultKernelConnectionClosed; + if (out_infos.num_elements > INT_MAX) { + return ResultRoInvalidSize; + } + return Registration::GetProcessModuleInfo(count.GetPointer(), out_infos.buffer, out_infos.num_elements, pid); } \ No newline at end of file diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 4bd9b4893..1a3787677 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -77,7 +77,7 @@ void __appInit(void) { if (R_FAILED(rc)) { std::abort(); } - + rc = splInitialize(); if (R_FAILED(rc)) { std::abort(); @@ -114,13 +114,10 @@ int main(int argc, char **argv) static auto s_server_manager = WaitableManager(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); - { - - s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { - s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); - } + s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); } /* Loop forever, servicing our services. */ diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp new file mode 100644 index 000000000..fe2726e76 --- /dev/null +++ b/stratosphere/ro/source/ro_registration.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "ro_registration.hpp" + +/* Declare process contexts as static to this function. */ +static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; + +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++) { + if (g_process_contexts[i].process_id == process_id) { + return ResultRoInvalidSession; + } + } + + /* Find a free process context. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (!g_process_contexts[i].in_use) { + g_process_contexts[i].process_id = process_id; + g_process_contexts[i].process_handle = process_handle; + g_process_contexts[i].in_use = true; + *out_context = &g_process_contexts[i]; + return ResultSuccess; + } + } + + /* Failure to find a free context is actually an abort condition. */ + /* TODO: Should this return an unofficial error code? */ + std::abort(); +} + +void Registration::UnregisterProcess(RoProcessContext *context) { + if (context->process_handle != INVALID_HANDLE) { + for (size_t i = 0; i < Registration::MaxNrrInfos; i++) { + if (context->nrr_in_use[i]) { + UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); + } + } + } +} + +Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { + size_t count = 0; + for (size_t sess = 0; sess < Registration::MaxSessions; sess++) { + if (g_process_contexts[sess].process_id == process_id) { + /* For convenience, helper. */ + const RoProcessContext *context = &g_process_contexts[sess]; + + for (size_t i = 0; i < Registration::MaxNroInfos && count < max_out_count; i++) { + if (!context->nro_in_use[i]) { + continue; + } + + /* Just copy out the info. */ + LoaderModuleInfo *out_info = &out_infos[count++]; + memcpy(out_info->build_id, &context->nro_infos[i].module_id, sizeof(context->nro_infos[i].module_id)); + out_info->base_address = context->nro_infos[i].base_address; + out_info->size = context->nro_infos[i].nro_heap_size + context->nro_infos[i].bss_heap_size; + } + + break; + } + } + + *out_count = static_cast(count); + return ResultSuccess; +} + +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)) { + return rc; + } + + return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size); +} diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp new file mode 100644 index 000000000..b869d7e4d --- /dev/null +++ b/stratosphere/ro/source/ro_registration.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include + +class Registration { + public: + static constexpr size_t MaxSessions = 0x8; + static constexpr size_t MaxNrrInfos = 0x40; + static constexpr size_t MaxNroInfos = 0x40; + 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; + u8 nrr_type; /* 7.0.0+ */ + u8 _0x33D[3]; + u32 hash_offset; + u32 num_hashes; + u64 _0x348; + }; + static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); + + 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(NroHeader) == 0x80, "NroHeader definition!"); + + struct ModuleId { + u8 build_id[0x20]; + }; + static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); + + struct NroInfo { + u64 base_address; + u64 nro_heap_address; + u64 nro_heap_size; + u64 bss_heap_address; + u64 bss_heap_size; + u64 code_size; + u64 rw_size; + ModuleId module_id; + }; + struct NrrInfo { + NrrHeader *header; + u64 nrr_heap_address; + u64 nrr_heap_size; + u64 mapped_code_address; + }; + struct RoProcessContext { + bool nro_in_use[MaxNroInfos]; + bool nrr_in_use[MaxNrrInfos]; + NroInfo nro_infos[MaxNroInfos]; + NrrInfo nrr_infos[MaxNrrInfos]; + Handle process_handle; + u64 process_id; + bool in_use; + }; + public: + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); + static void UnregisterProcess(RoProcessContext *context); + + 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 0ecbf1551..8dc2bd504 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -20,9 +20,12 @@ #include #include "ro_service.hpp" +#include "ro_registration.hpp" RelocatableObjectsService::~RelocatableObjectsService() { - /* TODO */ + if (this->IsInitialized()) { + Registration::UnregisterProcess(this->context); + } } Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp index 8066c86e8..2d68f84bd 100644 --- a/stratosphere/ro/source/ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -19,6 +19,8 @@ #include +#include "ro_registration.hpp" + enum RoServiceCmd { Ro_Cmd_LoadNro = 0, Ro_Cmd_UnloadNro = 1, @@ -34,16 +36,17 @@ enum RoServiceType : u32 { }; class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; + Registration::RoProcessContext *context = nullptr; RoServiceType type; public: explicit RelocatableObjectsService(RoServiceType t) : type(t) { /* ... */ } virtual ~RelocatableObjectsService() override; - + private: + bool IsInitialized() const { + return this->context != nullptr; + } private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);