diff --git a/README.md b/README.md index a839167a0..5c39694db 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -Atmosphère-NX + +![Banner](img/banner.png?raw=true) ===== ![License](https://img.shields.io/badge/License-GPLv2-blue.svg) @@ -30,3 +31,17 @@ Atmosphère will want to run a customized Secure Monitor. ### Fusée (First-stage Loader) - In Progress Atmosphère will need a loader -- this will be responsible for loading and validating stage 2 (custom TrustZone) plus package2 (Kernel/FIRM sysmodules), and patching them as needed. (This should implement all functionality normally in Package1loader/NX Bootloader). +======= +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 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. diff --git a/img/banner.png b/img/banner.png new file mode 100644 index 000000000..c05693415 Binary files /dev/null and b/img/banner.png differ diff --git a/img/banner_light.png b/img/banner_light.png new file mode 100644 index 000000000..e7de74483 Binary files /dev/null and b/img/banner_light.png differ 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/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_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(); 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..fb8fcc2fc 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -3,7 +3,155 @@ 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); +}; + +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(). */ + } + 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->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; + if (this->IsActive()) { + return 0x19009; + } + + this->process_handle = process_h; + this->base_address = address; + this->size = size; + + 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_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; + } + + 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}; + } }; \ 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..120587fd6 --- /dev/null +++ b/stratosphere/loader/source/ldr_nro.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include "ldr_nro.hpp" +#include "ldr_registration.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; +} + +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); + *out_address = mcm_nro.code_memory_address; + 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 new file mode 100644 index 000000000..497278fad --- /dev/null +++ b/stratosphere/loader/source/ldr_nro.hpp @@ -0,0 +1,56 @@ +#pragma once +#include +#include + +#include "ldr_registration.hpp" +#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; + }; + + 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_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_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index a353b645d..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; } @@ -174,10 +174,16 @@ 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_addspace; + if (kernelAbove200()) { + is_64_bit_addspace = (((npdm_info.header->mmu_flags >> 1) & 5) | 2) == 3; + } else { + is_64_bit_addspace = (npdm_info.header->mmu_flags & 0xE) == 0x2; + } + 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 509b85f85..ab0fce037 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -1,14 +1,15 @@ #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; -Registration::Process *Registration::get_free_process() { - unsigned int i; - for (i = 0; i < REGISTRATION_LIST_MAX; i++) { +Registration::Process *Registration::GetFreeProcess() { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX; i++) { if (!g_registration_list.processes[i].in_use) { return &g_registration_list.processes[i]; } @@ -16,29 +17,35 @@ Registration::Process *Registration::get_free_process() { return NULL; } -Registration::Process *Registration::get_process(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++) { +Registration::Process *Registration::GetProcess(u64 index) { + 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::get_process_by_process_id(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++) { - +Registration::Process *Registration::GetProcessByProcessId(u64 pid) { + 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 &g_registration_list.processes[i]; + return NULL; } -bool Registration::register_tid_sid(const TidSid *tid_sid, u64 *out_index) { - Registration::Process *free_process = get_free_process(); +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 NULL; +} + +bool Registration::RegisterTidSid(const TidSid *tid_sid, u64 *out_index) { + Registration::Process *free_process = GetFreeProcess(); if (free_process == NULL) { return false; } @@ -52,8 +59,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 +71,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,18 +82,19 @@ 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) { - 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; } target_process->process_id = process_id; target_process->title_id_min = tid_min; + 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; } @@ -102,9 +110,144 @@ void Registration::add_nso_info(u64 index, u64 base_address, u64 size, const uns } } +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(); + } + } + target_process->owner_ro_service = NULL; +} -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::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { + Registration::Process *target_process = GetProcess(index); + if (target_process == NULL) { + /* TODO: panic() */ + return 0x7009; + } + + 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; + } + } + 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; +} + + +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::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) { return 0x1009; } diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index da95cde42..4df3bd05d 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -1,9 +1,13 @@ #pragma once #include +#include "ldr_map.hpp" + #define REGISTRATION_LIST_MAX (0x40) #define NSO_INFO_MAX (0x20) +#define NRR_INFO_MAX (0x40) +#define NRO_INFO_MAX (0x40) class Registration { public: @@ -18,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; @@ -25,12 +43,15 @@ class Registration { struct Process { bool in_use; + bool is_64_bit_addspace; u64 index; u64 process_id; u64 title_id_min; Registration::TidSid tid_sid; Registration::NsoInfoHolder nso_infos[NSO_INFO_MAX]; - u64 _0x730; + Registration::NroInfo nro_infos[NRO_INFO_MAX]; + MappedCodeMemory nrr_infos[NRR_INFO_MAX]; + void *owner_ro_service; }; struct List { @@ -38,13 +59,21 @@ 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_and_tid_min(u64 index, u64 process_id, u64 tid_min); - 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 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 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 new file mode 100644 index 000000000..020db98c1 --- /dev/null +++ b/stratosphere/loader/source/ldr_ro_service.cpp @@ -0,0 +1,168 @@ +#include +#include +#include + +#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; + + 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 RelocatableObjectsService::load_nro(PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + 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) { + 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) { + Result rc; + 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; + } + 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; + + if (R_FAILED((rc = nrr_info.Open(this->process_handle, target_proc->is_64_bit_addspace, nrr_address, nrr_size)))) { + 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); + } + +LOAD_NRR_END: + if (R_FAILED(rc)) { + if (nrr_info.IsActive()) { + nrr_info.Close(); + } + } + return std::make_tuple(rc); +} + +std::tuple RelocatableObjectsService::unload_nrr(PidDescriptor pid_desc, u64 nrr_address) { + 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) { + 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->process_id = handle_pid; + 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 new file mode 100644 index 000000000..f47d9849b --- /dev/null +++ b/stratosphere/loader/source/ldr_ro_service.hpp @@ -0,0 +1,40 @@ +#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) { } + ~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. */ + return 0; + } + + private: + /* Actual commands. */ + 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 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);; } 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.