diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere
index f575d0644..1d81da123 160000
--- a/stratosphere/libstratosphere
+++ b/stratosphere/libstratosphere
@@ -1 +1 @@
-Subproject commit f575d0644635582d83d484e7fc9691f6c17630b1
+Subproject commit 1d81da1230728993922126f756f6499b24be3eda
diff --git a/stratosphere/ro/Makefile b/stratosphere/ro/Makefile
index e43faaab2..17c54ea6f 100644
--- a/stratosphere/ro/Makefile
+++ b/stratosphere/ro/Makefile
@@ -26,7 +26,7 @@ endif
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
-SOURCES := source
+SOURCES := source source/impl
DATA := data
INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src
diff --git a/stratosphere/ro/source/impl/ro_nro_utils.cpp b/stratosphere/ro/source/impl/ro_nro_utils.cpp
new file mode 100644
index 000000000..acb4b67bd
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_nro_utils.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 "ro_nro_utils.hpp"
+
+namespace sts::ro::impl {
+
+ namespace {
+
+ constexpr size_t MaxMapRetries = 0x200;
+
+ }
+
+ Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
+ map::MappedCodeMemory nro_mcm(ResultRoInternalError);
+ map::MappedCodeMemory bss_mcm(ResultRoInternalError);
+ u64 base_address;
+
+ /* Map the NRO, and map the BSS immediately after it. */
+ size_t i;
+ for (i = 0; i < MaxMapRetries; i++) {
+ map::MappedCodeMemory tmp_nro_mcm(ResultRoInternalError);
+ R_TRY(map::MapCodeMemoryInProcess(tmp_nro_mcm, process_handle, nro_heap_address, nro_heap_size));
+ base_address = tmp_nro_mcm.GetDstAddress();
+
+ if (bss_heap_size > 0) {
+ map::MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size);
+ R_TRY_CATCH(tmp_bss_mcm.GetResult()) {
+ R_CATCH(ResultKernelInvalidMemoryState) {
+ continue;
+ }
+ } R_END_TRY_CATCH;
+
+ if (!map::CanAddGuardRegionsInProcess(process_handle, base_address, nro_heap_size + bss_heap_size)) {
+ continue;
+ }
+
+ bss_mcm = std::move(tmp_bss_mcm);
+ } else {
+ if (!map::CanAddGuardRegionsInProcess(process_handle, base_address, nro_heap_size)) {
+ continue;
+ }
+ }
+ nro_mcm = std::move(tmp_nro_mcm);
+ break;
+ }
+ if (i == MaxMapRetries) {
+ return ResultRoInsufficientAddressSpace;
+ }
+
+ /* Invalidation here actually prevents them from unmapping at scope exit. */
+ nro_mcm.Invalidate();
+ bss_mcm.Invalidate();
+
+ *out_base_address = base_address;
+ return ResultSuccess;
+ }
+
+ Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) {
+ const u64 rx_offset = 0;
+ const u64 ro_offset = rx_offset + rx_size;
+ const u64 rw_offset = ro_offset + ro_size;
+
+ R_TRY(svcSetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, Perm_Rx));
+ R_TRY(svcSetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, Perm_R ));
+ R_TRY(svcSetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, Perm_Rw));
+
+ return ResultSuccess;
+ }
+
+ Result UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) {
+ /* First, unmap bss. */
+ if (bss_heap_size > 0) {
+ R_TRY(svcUnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size));
+ }
+
+ /* Next, unmap .rwdata */
+ if (rw_size > 0) {
+ R_TRY(svcUnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size));
+ }
+
+ /* Finally, unmap .text + .rodata. */
+ R_TRY(svcUnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size));
+
+ return ResultSuccess;
+ }
+
+}
diff --git a/stratosphere/ro/source/impl/ro_nro_utils.hpp b/stratosphere/ro/source/impl/ro_nro_utils.hpp
new file mode 100644
index 000000000..d0087a25e
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_nro_utils.hpp
@@ -0,0 +1,29 @@
+/*
+ * 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
+#include
+
+namespace sts::ro::impl {
+
+ /* Utilities for working with NROs. */
+ Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
+ Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
+ Result UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size);
+
+}
\ No newline at end of file
diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.cpp b/stratosphere/ro/source/impl/ro_nrr_utils.cpp
new file mode 100644
index 000000000..9e07241cc
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_nrr_utils.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "ro_nrr_utils.hpp"
+#include "ro_service_impl.hpp"
+
+namespace sts::ro::impl {
+
+ namespace {
+
+ /* Helper functions. */
+ Result ValidateNrrSignature(const NrrHeader *header) {
+ /* TODO: Implement RSA-2048 PSS..... */
+
+ /* TODO: Check PSS fixed-key signature. */
+ if (false) {
+ return ResultRoNotAuthorized;
+ }
+
+ /* Check TitleID pattern is valid. */
+ if (!header->IsTitleIdValid()) {
+ return ResultRoNotAuthorized;
+ }
+
+ /* TODO: Check PSS signature over hashes. */
+ if (false) {
+ return ResultRoNotAuthorized;
+ }
+
+ return ResultSuccess;
+ }
+
+ Result ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, ModuleType expected_type, bool enforce_type) {
+ /* Check magic. */
+ if (!header->IsMagicValid()) {
+ return ResultRoInvalidNrr;
+ }
+
+ /* Check size. */
+ if (header->GetSize() != size) {
+ return ResultRoInvalidSize;
+ }
+
+ /* Only perform checks if we must. */
+ const bool ease_nro_restriction = ShouldEaseNroRestriction();
+ if (!ease_nro_restriction) {
+ /* Check signature. */
+ R_TRY(ValidateNrrSignature(header));
+
+ /* Check title id. */
+ if (title_id != header->GetTitleId()) {
+ return ResultRoInvalidNrr;
+ }
+
+ /* Check type. */
+ if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700 && enforce_type) {
+ if (expected_type != header->GetType()) {
+ return ResultRoInvalidNrrType;
+ }
+ }
+ }
+
+ return ResultSuccess;
+ }
+
+ }
+
+ /* Utilities for working with NRRs. */
+ Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type) {
+ map::MappedCodeMemory nrr_mcm(ResultRoInternalError);
+
+ /* First, map the NRR. */
+ R_TRY(map::MapCodeMemoryInProcess(nrr_mcm, process_handle, nrr_heap_address, nrr_heap_size));
+
+ const u64 code_address = nrr_mcm.GetDstAddress();
+ uintptr_t map_address;
+ if (R_FAILED(map::LocateMappableSpace(&map_address, nrr_heap_size))) {
+ return ResultRoInsufficientAddressSpace;
+ }
+
+ /* Nintendo...does not check the return value of this map. We will check, instead of aborting if it fails. */
+ map::AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size);
+ if (!nrr_map.IsSuccess()) {
+ return nrr_map.GetResult();
+ }
+
+ NrrHeader *nrr_header = reinterpret_cast(map_address);
+ R_TRY(ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type));
+
+ /* Invalidation here actually prevents them from unmapping at scope exit. */
+ nrr_map.Invalidate();
+ nrr_mcm.Invalidate();
+
+ *out_header = nrr_header;
+ *out_mapped_code_address = code_address;
+ return ResultSuccess;
+ }
+
+ Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) {
+ R_TRY(svcUnmapProcessMemory(reinterpret_cast(const_cast(header)), process_handle, mapped_code_address, nrr_heap_size));
+ R_TRY(svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size));
+ return ResultSuccess;
+ }
+
+}
diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.hpp b/stratosphere/ro/source/impl/ro_nrr_utils.hpp
new file mode 100644
index 000000000..5235223a0
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_nrr_utils.hpp
@@ -0,0 +1,28 @@
+/*
+ * 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
+#include
+
+namespace sts::ro::impl {
+
+ /* Utilities for working with NRRs. */
+ Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type);
+ 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/impl/ro_patcher.cpp b/stratosphere/ro/source/impl/ro_patcher.cpp
new file mode 100644
index 000000000..ce4db30d4
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_patcher.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "ro_patcher.hpp"
+
+namespace sts::ro::impl {
+
+ namespace {
+
+ constexpr const char *NroPatchesDirectory = "nro_patches";
+
+ /* NRO patches want to prevent modification of header, */
+ /* but don't want to adjust offset relative to mapped location. */
+ constexpr size_t NroPatchesProtectedSize = sizeof(NroHeader);
+ constexpr size_t NroPatchesProtectedOffset = 0;
+
+ }
+
+ /* Apply IPS patches. */
+ void LocateAndApplyIpsPatchesToModule(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size) {
+ sts::patcher::LocateAndApplyIpsPatchesToModule(NroPatchesDirectory, NroPatchesProtectedSize, NroPatchesProtectedOffset, module_id, mapped_nro, mapped_size);
+ }
+
+}
\ No newline at end of file
diff --git a/stratosphere/ro/source/ro_patcher.hpp b/stratosphere/ro/source/impl/ro_patcher.hpp
similarity index 77%
rename from stratosphere/ro/source/ro_patcher.hpp
rename to stratosphere/ro/source/impl/ro_patcher.hpp
index ed88c7223..041023b45 100644
--- a/stratosphere/ro/source/ro_patcher.hpp
+++ b/stratosphere/ro/source/impl/ro_patcher.hpp
@@ -16,11 +16,11 @@
#pragma once
#include
-#include
+#include
-#include "ro_types.hpp"
+namespace sts::ro::impl {
-class PatchUtils {
- public:
- static void ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size);
-};
\ No newline at end of file
+ /* Apply IPS patches. */
+ void LocateAndApplyIpsPatchesToModule(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size);
+
+}
\ No newline at end of file
diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp
new file mode 100644
index 000000000..b023abd76
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_service_impl.cpp
@@ -0,0 +1,537 @@
+/*
+ * 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
+
+#include "ro_nrr_utils.hpp"
+#include "ro_nro_utils.hpp"
+#include "ro_patcher.hpp"
+#include "ro_service_impl.hpp"
+
+namespace sts::ro::impl {
+
+ namespace {
+
+ /* Convenience definitions. */
+ constexpr size_t MaxSessions = 0x4;
+ constexpr size_t MaxNrrInfos = 0x40;
+ constexpr size_t MaxNroInfos = 0x40;
+
+ /* Types. */
+ struct Sha256Hash {
+ u8 hash[SHA256_HASH_SIZE];
+
+ bool operator==(const Sha256Hash &o) const {
+ return std::memcmp(this, &o, sizeof(*this)) == 0;
+ }
+ bool operator!=(const Sha256Hash &o) const {
+ return std::memcmp(this, &o, sizeof(*this)) != 0;
+ }
+ bool operator<(const Sha256Hash &o) const {
+ return std::memcmp(this, &o, sizeof(*this)) < 0;
+ }
+ bool operator>(const Sha256Hash &o) const {
+ return std::memcmp(this, &o, sizeof(*this)) > 0;
+ }
+ };
+ static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash 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;
+ bool in_use;
+ };
+
+ struct NrrInfo {
+ const NrrHeader *header;
+ u64 nrr_heap_address;
+ u64 nrr_heap_size;
+ u64 mapped_code_address;
+ bool in_use;
+ };
+
+ struct ProcessContext {
+ NroInfo nro_infos[MaxNroInfos];
+ NrrInfo nrr_infos[MaxNrrInfos];
+ Handle process_handle;
+ u64 process_id;
+ bool in_use;
+
+ u64 GetTitleId(Handle other_process_h) const {
+ /* Automatically select a handle, allowing for override. */
+ Handle process_h = this->process_handle;
+ if (other_process_h != INVALID_HANDLE) {
+ process_h = other_process_h;
+ }
+
+ u64 title_id = 0;
+ if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
+ /* 3.0.0+: Use svcGetInfo. */
+ R_ASSERT(svcGetInfo(&title_id, InfoType_TitleId, process_h, 0));
+ } else {
+ /* 1.0.0-2.3.0: We're not inside loader, so ask pm. */
+ u64 process_id = 0;
+ R_ASSERT(svcGetProcessId(&process_id, process_h));
+ R_ASSERT(pminfoGetTitleId(&title_id, process_id));
+ }
+ return title_id;
+ }
+
+ Result GetNrrInfoByAddress(NrrInfo **out, u64 nrr_heap_address) {
+ for (size_t i = 0; i < MaxNrrInfos; i++) {
+ if (this->nrr_infos[i].in_use && this->nrr_infos[i].nrr_heap_address == nrr_heap_address) {
+ if (out != nullptr) {
+ *out = &this->nrr_infos[i];
+ }
+ return ResultSuccess;
+ }
+ }
+ return ResultRoNotRegistered;
+ }
+
+ Result GetFreeNrrInfo(NrrInfo **out) {
+ for (size_t i = 0; i < MaxNrrInfos; i++) {
+ if (!this->nrr_infos[i].in_use) {
+ if (out != nullptr) {
+ *out = &this->nrr_infos[i];
+ }
+ return ResultSuccess;
+ }
+ }
+ return ResultRoTooManyNrr;
+ }
+
+ Result GetNroInfoByAddress(NroInfo **out, u64 nro_address) {
+ for (size_t i = 0; i < MaxNroInfos; i++) {
+ if (this->nro_infos[i].in_use && this->nro_infos[i].base_address == nro_address) {
+ if (out != nullptr) {
+ *out = &this->nro_infos[i];
+ }
+ return ResultSuccess;
+ }
+ }
+ return ResultRoNotLoaded;
+ }
+
+ Result GetNroInfoByModuleId(NroInfo **out, const ModuleId *module_id) {
+ for (size_t i = 0; i < MaxNroInfos; i++) {
+ if (this->nro_infos[i].in_use && std::memcmp(&this->nro_infos[i].module_id, module_id, sizeof(*module_id)) == 0) {
+ if (out != nullptr) {
+ *out = &this->nro_infos[i];
+ }
+ return ResultSuccess;
+ }
+ }
+ return ResultRoNotLoaded;
+ }
+
+ Result GetFreeNroInfo(NroInfo **out) {
+ for (size_t i = 0; i < MaxNroInfos; i++) {
+ if (!this->nro_infos[i].in_use) {
+ if (out != nullptr) {
+ *out = &this->nro_infos[i];
+ }
+ return ResultSuccess;
+ }
+ }
+ return ResultRoTooManyNro;
+ }
+
+ Result ValidateHasNroHash(const NroHeader *nro_header) const {
+ /* Calculate hash. */
+ Sha256Hash hash;
+ sha256CalculateHash(&hash, nro_header, nro_header->GetSize());
+
+ for (size_t i = 0; i < MaxNrrInfos; i++) {
+ if (this->nrr_infos[i].in_use) {
+ const NrrHeader *nrr_header = this->nrr_infos[i].header;
+ const Sha256Hash *nro_hashes = reinterpret_cast(nrr_header->GetHashes());
+ if (std::binary_search(nro_hashes, nro_hashes + nrr_header->GetNumHashes(), hash)) {
+ return ResultSuccess;
+ }
+ }
+ }
+
+ return ResultRoNotAuthorized;
+ }
+
+ Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
+ /* Find space to map the NRO. */
+ uintptr_t map_address;
+ if (R_FAILED(map::LocateMappableSpace(&map_address, expected_nro_size))) {
+ return ResultRoInsufficientAddressSpace;
+ }
+
+ /* Actually map the NRO. */
+ map::AutoCloseMap nro_map(map_address, this->process_handle, base_address, expected_nro_size);
+ if (!nro_map.IsSuccess()) {
+ return nro_map.GetResult();
+ }
+
+ /* Validate header. */
+ const NroHeader *header = reinterpret_cast(map_address);
+ if (!header->IsMagicValid()) {
+ return ResultRoInvalidNro;
+ }
+
+ const u64 nro_size = header->GetSize();
+ const u64 text_ofs = header->GetTextOffset();
+ const u64 text_size = header->GetTextSize();
+ const u64 ro_ofs = header->GetRoOffset();
+ const u64 ro_size = header->GetRoSize();
+ const u64 rw_ofs = header->GetRwOffset();
+ const u64 rw_size = header->GetRwSize();
+ const u64 bss_size = header->GetBssSize();
+ if (nro_size != expected_nro_size || bss_size != expected_bss_size) {
+ return ResultRoInvalidNro;
+ }
+ if ((text_size & 0xFFF) || (ro_size & 0xFFF) || (rw_size & 0xFFF) || (bss_size & 0xFFF)) {
+ return ResultRoInvalidNro;
+ }
+ if (text_ofs > ro_ofs || ro_ofs > rw_ofs) {
+ return ResultRoInvalidNro;
+ }
+ if (text_ofs != 0 || text_ofs + text_size != ro_ofs || ro_ofs + ro_size != rw_ofs || rw_ofs + rw_size != nro_size) {
+ return ResultRoInvalidNro;
+ }
+
+ /* Verify NRO hash. */
+ R_TRY(this->ValidateHasNroHash(header));
+
+ /* Check if NRO has already been loaded. */
+ const ModuleId *module_id = header->GetModuleId();
+ if (R_SUCCEEDED(this->GetNroInfoByModuleId(nullptr, module_id))) {
+ return ResultRoAlreadyLoaded;
+ }
+
+ /* Apply patches to NRO. */
+ LocateAndApplyIpsPatchesToModule(module_id, reinterpret_cast(map_address), nro_size);
+
+ /* Copy to output. */
+ *out_module_id = *module_id;
+ *out_rx_size = text_size;
+ *out_ro_size = ro_size;
+ *out_rw_size = rw_size;
+ return ResultSuccess;
+ }
+ };
+
+ /* Globals. */
+ ProcessContext g_process_contexts[MaxSessions] = {};
+ bool g_is_development_hardware = false;
+ bool g_is_development_function_enabled = false;
+
+ /* Context Helpers. */
+ ProcessContext *GetContextById(size_t context_id) {
+ if (context_id == InvalidContextId) {
+ return nullptr;
+ } else if (context_id < MaxSessions) {
+ return &g_process_contexts[context_id];
+ } else {
+ std::abort();
+ }
+ }
+
+ ProcessContext *GetContextByProcessId(u64 process_id) {
+ for (size_t i = 0; i < MaxSessions; i++) {
+ if (g_process_contexts[i].process_id == process_id) {
+ return &g_process_contexts[i];
+ }
+ }
+ return nullptr;
+ }
+
+ size_t AllocateContext(Handle process_handle, u64 process_id) {
+ /* Find a free process context. */
+ for (size_t i = 0; i < MaxSessions; i++) {
+ ProcessContext *context = &g_process_contexts[i];
+ if (!context->in_use) {
+ std::memset(context, 0, sizeof(*context));
+ context->process_id = process_id;
+ context->process_handle = process_handle;
+ context->in_use = true;
+ return i;
+ }
+ }
+
+ /* Failure to find a free context is actually an abort condition. */
+ std::abort();
+ }
+
+ void FreeContext(size_t context_id) {
+ ProcessContext *context = GetContextById(context_id);
+ if (context != nullptr) {
+ if (context->process_handle != INVALID_HANDLE) {
+ for (size_t i = 0; i < MaxNrrInfos; i++) {
+ if (context->nrr_infos[i].in_use) {
+ 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);
+ }
+ }
+ svcCloseHandle(context->process_handle);
+ }
+ std::memset(context, 0, sizeof(*context));
+ context->in_use = false;
+ }
+ }
+
+ }
+
+ /* Access utilities. */
+ void SetDevelopmentHardware(bool is_development_hardware) {
+ g_is_development_hardware = is_development_hardware;
+ }
+
+ void SetDevelopmentFunctionEnabled(bool is_development_function_enabled) {
+ g_is_development_function_enabled = is_development_function_enabled;
+ }
+
+ bool IsDevelopmentHardware() {
+ return g_is_development_hardware;
+ }
+
+ bool IsDevelopmentFunctionEnabled() {
+ return g_is_development_function_enabled;
+ }
+
+ bool ShouldEaseNroRestriction() {
+ /* Retrieve whether we should ease restrictions from set:sys. */
+ bool should_ease = false;
+ if (R_FAILED(setsysGetSettingsItemValue("ro", "ease_nro_restriction", &should_ease, sizeof(should_ease)))) {
+ return false;
+ }
+
+ /* Nintendo only allows easing restriction on dev, we will allow on production, as well. */
+ /* should_ease &= IsDevelopmentFunctionEnabled(); */
+ return should_ease;
+ }
+
+ /* Context utilities. */
+ Result RegisterProcess(size_t *out_context_id, Handle process_handle, u64 process_id) {
+ /* Validate process handle. */
+ {
+ u64 handle_pid = 0;
+
+ /* Validate handle is a valid process handle. */
+ if (R_FAILED(svcGetProcessId(&handle_pid, process_handle))) {
+ return ResultRoInvalidProcess;
+ }
+
+ /* Validate process id. */
+ if (handle_pid != process_id) {
+ return ResultRoInvalidProcess;
+ }
+ }
+
+ /* Check if a process context already exists. */
+ if (GetContextByProcessId(process_id) != nullptr) {
+ return ResultRoInvalidSession;
+ }
+
+ *out_context_id = AllocateContext(process_handle, process_id);
+ return ResultSuccess;
+ }
+
+ Result ValidateProcess(size_t context_id, u64 process_id) {
+ const ProcessContext *ctx = GetContextById(context_id);
+ if (ctx == nullptr || ctx->process_id != process_id) {
+ return ResultRoInvalidProcess;
+ }
+ return ResultSuccess;
+ }
+
+ void UnregisterProcess(size_t context_id) {
+ FreeContext(context_id);
+ }
+
+ /* Service implementations. */
+ Result LoadNrr(size_t context_id, Handle process_h, u64 nrr_address, u64 nrr_size, ModuleType expected_type, bool enforce_type) {
+ /* Get context. */
+ ProcessContext *context = GetContextById(context_id);
+ if (context == nullptr) {
+ std::abort();
+ }
+
+ /* Get title id. */
+ const u64 title_id = context->GetTitleId(process_h);
+
+ /* 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. */
+ NrrInfo *nrr_info = nullptr;
+ R_TRY(context->GetFreeNrrInfo(&nrr_info));
+
+ /* Map. */
+ NrrHeader *header = nullptr;
+ u64 mapped_code_address = 0;
+ R_TRY(MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size, expected_type, enforce_type));
+
+ /* Set NRR info. */
+ nrr_info->in_use = true;
+ 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;
+
+ return ResultSuccess;
+ }
+
+ Result UnloadNrr(size_t context_id, u64 nrr_address) {
+ /* Get context. */
+ ProcessContext *context = GetContextById(context_id);
+ if (context == nullptr) {
+ std::abort();
+ }
+
+ /* Validate address. */
+ if (nrr_address & 0xFFF) {
+ return ResultRoInvalidAddress;
+ }
+
+ /* Check the NRR is loaded. */
+ NrrInfo *nrr_info = nullptr;
+ R_TRY(context->GetNrrInfoByAddress(&nrr_info, nrr_address));
+
+ /* Unmap. */
+ const NrrInfo nrr_backup = *nrr_info;
+ {
+ /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */
+ nrr_info->in_use = false;
+ std::memset(nrr_info, 0, sizeof(*nrr_info));
+ }
+ return UnmapNrr(context->process_handle, nrr_backup.header, nrr_backup.nrr_heap_address, nrr_backup.nrr_heap_size, nrr_backup.mapped_code_address);
+ }
+
+ Result LoadNro(u64 *out_address, size_t context_id, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
+ /* Get context. */
+ ProcessContext *context = GetContextById(context_id);
+ if (context == nullptr) {
+ std::abort();
+ }
+
+ /* Validate address/size. */
+ if (nro_address & 0xFFF) {
+ return ResultRoInvalidAddress;
+ }
+ if (nro_size == 0 || (nro_size & 0xFFF) || !(nro_address < nro_address + nro_size)) {
+ return ResultRoInvalidSize;
+ }
+ if (bss_address & 0xFFF) {
+ return ResultRoInvalidAddress;
+ }
+ if ((bss_size & 0xFFF) || (bss_size > 0 && !(bss_address < bss_address + bss_size))) {
+ return ResultRoInvalidSize;
+ }
+
+ const u64 total_size = nro_size + bss_size;
+ if (total_size < nro_size || total_size < bss_size) {
+ return ResultRoInvalidSize;
+ }
+
+ /* Check we have space for a new NRO. */
+ NroInfo *nro_info = nullptr;
+ R_TRY(context->GetFreeNroInfo(&nro_info));
+ nro_info->nro_heap_address = nro_address;
+ nro_info->nro_heap_size = nro_size;
+ nro_info->bss_heap_address = bss_address;
+ nro_info->bss_heap_size = bss_size;
+
+ /* Map the NRO. */
+ R_TRY(MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size));
+
+ /* Validate the NRO (parsing region extents). */
+ u64 rx_size, ro_size, rw_size;
+ R_TRY_CLEANUP(context->ValidateNro(&nro_info->module_id, &rx_size, &ro_size, &rw_size, nro_info->base_address, nro_size, bss_size), {
+ UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0);
+ });
+
+ /* Set NRO perms. */
+ R_TRY_CLEANUP(SetNroPerms(context->process_handle, nro_info->base_address, rx_size, ro_size, rw_size + bss_size), {
+ UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size);
+ });
+
+ nro_info->code_size = rx_size + ro_size;
+ nro_info->rw_size = rw_size;
+ nro_info->in_use = true;
+ *out_address = nro_info->base_address;
+ return ResultSuccess;
+ }
+
+ Result UnloadNro(size_t context_id, u64 nro_address) {
+ /* Get context. */
+ ProcessContext *context = GetContextById(context_id);
+ if (context == nullptr) {
+ std::abort();
+ }
+
+ /* Validate address. */
+ if (nro_address & 0xFFF) {
+ return ResultRoInvalidAddress;
+ }
+
+ /* Check the NRO is loaded. */
+ NroInfo *nro_info = nullptr;
+ R_TRY(context->GetNroInfoByAddress(&nro_info, nro_address));
+
+ /* Unmap. */
+ const NroInfo nro_backup = *nro_info;
+ {
+ /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */
+ nro_info->in_use = false;
+ std::memset(nro_info, 0, sizeof(*nro_info));
+ }
+ return UnmapNro(context->process_handle, nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.bss_heap_address, nro_backup.bss_heap_size, nro_backup.code_size, nro_backup.rw_size);
+ }
+
+ /* Debug service implementations. */
+ Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) {
+ size_t count = 0;
+ const ProcessContext *context = GetContextByProcessId(process_id);
+ if (context != nullptr) {
+ for (size_t i = 0; i < MaxNroInfos && count < max_out_count; i++) {
+ const NroInfo *nro_info = &context->nro_infos[i];
+ if (!nro_info->in_use) {
+ continue;
+ }
+
+ /* Just copy out the info. */
+ LoaderModuleInfo *out_info = &out_infos[count++];
+ memcpy(out_info->build_id, &nro_info->module_id, sizeof(nro_info->module_id));
+ out_info->base_address = nro_info->base_address;
+ out_info->size = nro_info->nro_heap_size + nro_info->bss_heap_size;
+ }
+ }
+
+ *out_count = static_cast(count);
+ return ResultSuccess;
+ }
+
+}
diff --git a/stratosphere/ro/source/impl/ro_service_impl.hpp b/stratosphere/ro/source/impl/ro_service_impl.hpp
new file mode 100644
index 000000000..07a707080
--- /dev/null
+++ b/stratosphere/ro/source/impl/ro_service_impl.hpp
@@ -0,0 +1,48 @@
+/*
+ * 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
+#include
+
+namespace sts::ro::impl {
+
+ /* Definitions. */
+ constexpr size_t InvalidContextId = static_cast(-1);
+
+ /* Access utilities. */
+ void SetDevelopmentHardware(bool is_development_hardware);
+ void SetDevelopmentFunctionEnabled(bool is_development_function_enabled);
+ bool IsDevelopmentHardware();
+ bool IsDevelopmentFunctionEnabled();
+ bool ShouldEaseNroRestriction();
+
+ /* Context utilities. */
+ Result RegisterProcess(size_t *out_context_id, Handle process_handle, u64 process_id);
+ Result ValidateProcess(size_t context_id, u64 process_id);
+ void UnregisterProcess(size_t context_id);
+
+ /* Service implementations. */
+ Result LoadNrr(size_t context_id, Handle process_h, u64 nrr_address, u64 nrr_size, ModuleType expected_type, bool enforce_type);
+ Result UnloadNrr(size_t context_id, u64 nrr_address);
+ Result LoadNro(u64 *out_address, size_t context_id, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
+ Result UnloadNro(size_t context_id, u64 nro_address);
+
+ /* Debug service implementations. */
+ Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id);
+
+}
diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp
index c0eb68697..213f65630 100644
--- a/stratosphere/ro/source/ro_debug_monitor.cpp
+++ b/stratosphere/ro/source/ro_debug_monitor.cpp
@@ -14,16 +14,20 @@
* along with this program. If not, see .
*/
+#include
#include
#include
-#include
#include "ro_debug_monitor.hpp"
-#include "ro_registration.hpp"
+#include "impl/ro_service_impl.hpp"
-Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) {
- if (out_infos.num_elements > INT_MAX) {
- return ResultRoInvalidSize;
+namespace sts::ro {
+
+ Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) {
+ if (out_infos.num_elements > INT_MAX) {
+ return ResultRoInvalidSize;
+ }
+ return impl::GetProcessModuleInfo(count.GetPointer(), out_infos.buffer, out_infos.num_elements, pid);
}
- 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_debug_monitor.hpp b/stratosphere/ro/source/ro_debug_monitor.hpp
index d8273e2df..b675f83d9 100644
--- a/stratosphere/ro/source/ro_debug_monitor.hpp
+++ b/stratosphere/ro/source/ro_debug_monitor.hpp
@@ -18,16 +18,20 @@
#include
#include
-enum DebugMonitorServiceCmd {
- Dmnt_Cmd_GetProcessModuleInfo = 0
-};
+namespace sts::ro {
-class DebugMonitorService final : public IServiceObject {
- private:
- /* Actual commands. */
- Result GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid);
- public:
- DEFINE_SERVICE_DISPATCH_TABLE {
- MakeServiceCommandMeta(),
- };
-};
+ class DebugMonitorService final : public IServiceObject {
+ protected:
+ enum class CommandId {
+ GetProcessModuleInfo = 0,
+ };
+ private:
+ /* Actual commands. */
+ Result GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid);
+ public:
+ DEFINE_SERVICE_DISPATCH_TABLE {
+ MakeServiceCommandMeta(),
+ };
+ };
+
+}
diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp
index 486c0e4de..aa1a055d4 100644
--- a/stratosphere/ro/source/ro_main.cpp
+++ b/stratosphere/ro/source/ro_main.cpp
@@ -22,10 +22,11 @@
#include
#include
#include
+#include
+#include
#include "ro_debug_monitor.hpp"
#include "ro_service.hpp"
-#include "ro_registration.hpp"
extern "C" {
extern u32 __start__;
@@ -85,24 +86,34 @@ void __appExit(void) {
setsysExit();
}
+using namespace sts;
+
/* Helpers to create RO objects. */
-static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoModuleType_ForSelf); };
-static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoModuleType_ForOthers); };
+static const auto MakeRoServiceForSelf = []() { return std::make_shared(ro::ModuleType::ForSelf); };
+static const auto MakeRoServiceForOthers = []() { return std::make_shared(ro::ModuleType::ForOthers); };
int main(int argc, char **argv)
{
- /* Initialize. */
- Registration::Initialize();
+ /* Initialize Debug config. */
+ {
+ DoWithSmSession([]() {
+ R_ASSERT(splInitialize());
+ });
+ ON_SCOPE_EXIT { splExit(); };
+
+ ro::SetDevelopmentHardware(spl::IsDevelopmentHardware());
+ ro::SetDevelopmentFunctionEnabled(spl::IsDevelopmentFunctionEnabled());
+ }
/* 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("ro:dmnt", 2));
/* 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));
+ s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 2));
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) {
- s_server_manager.AddWaitable(new ServiceServer("ro:1", 2));
+ s_server_manager.AddWaitable(new ServiceServer("ro:1", 2));
}
/* Loop forever, servicing our services. */
diff --git a/stratosphere/ro/source/ro_map.cpp b/stratosphere/ro/source/ro_map.cpp
deleted file mode 100644
index 9dec810c3..000000000
--- a/stratosphere/ro/source/ro_map.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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 "ro_map.hpp"
-
-bool MapUtils::CanAddGuardRegions(Handle process_handle, u64 address, u64 size) {
- MemoryInfo mem_info;
- u32 page_info;
-
- /* Nintendo doesn't validate SVC return values at all. */
- /* TODO: Should we allow these to fail? */
- R_ASSERT(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address - 1));
- if (mem_info.type == MemType_Unmapped && address - GuardRegionSize >= mem_info.addr) {
- R_ASSERT(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address + size));
- return mem_info.type == MemType_Unmapped && address + size + GuardRegionSize <= mem_info.addr + mem_info.size;
- }
-
- return false;
-}
-
-Result MapUtils::LocateSpaceForMap(u64 *out, u64 out_size) {
- if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
- return LocateSpaceForMapModern(out, out_size);
- } else {
- return LocateSpaceForMapDeprecated(out, out_size);
- }
-}
-
-Result MapUtils::MapCodeMemoryForProcess(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size) {
- if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
- return MapCodeMemoryForProcessModern(out_mcm, process_handle, base_address, size);
- } else {
- if (R_FAILED(MapCodeMemoryForProcessDeprecated(out_mcm, process_handle, true, base_address, size))) {
- R_TRY(MapCodeMemoryForProcessDeprecated(out_mcm, process_handle, false, base_address, size));
- }
- return ResultSuccess;
- }
-}
-
-Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) {
- MemoryInfo mem_info = {};
- AddressSpaceInfo address_space = {};
- u32 page_info = 0;
- u64 cur_base = 0, cur_end = 0;
-
- R_TRY(GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE));
-
- cur_base = address_space.addspace_base;
-
- cur_end = cur_base + out_size;
- if (cur_end <= cur_base) {
- return ResultKernelOutOfMemory;
- }
-
- 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 ResultKernelOutOfMemory;
- }
- 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 ResultKernelOutOfMemory;
- }
- cur_base = address_space.map_end;
- } else {
- R_ASSERT(svcQueryMemory(&mem_info, &page_info, cur_base));
- if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) {
- *out = cur_base;
- return ResultSuccess;
- }
- if (mem_info.addr + mem_info.size <= cur_base) {
- return ResultKernelOutOfMemory;
- }
- cur_base = mem_info.addr + mem_info.size;
- if (cur_base >= address_space.addspace_end) {
- return ResultKernelOutOfMemory;
- }
- }
- cur_end = cur_base + out_size;
- if (cur_base + out_size <= cur_base) {
- return ResultKernelOutOfMemory;
- }
- }
-}
-
-
-Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) {
- MemoryInfo mem_info = {};
- u32 page_info = 0;
-
- u64 cur_base = 0x8000000ULL;
- do {
- R_TRY(svcQueryMemory(&mem_info, &page_info, cur_base));
-
- if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) {
- *out = cur_base;
- return ResultSuccess;
- }
-
- const u64 mem_end = mem_info.addr + mem_info.size;
- if (mem_info.type == 0x10 || mem_end < cur_base || (mem_end >> 31)) {
- return ResultKernelOutOfMemory;
- }
-
- cur_base = mem_end;
- } while (true);
-}
-
-Result MapUtils::MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size) {
- AddressSpaceInfo address_space = {};
-
- R_TRY(GetAddressSpaceInfo(&address_space, process_handle));
-
- if (size > address_space.addspace_size) {
- return ResultRoInsufficientAddressSpace;
- }
-
- u64 try_address;
- for (unsigned int i = 0; i < LocateRetryCount; i++) {
- while (true) {
- try_address = address_space.addspace_base + (StratosphereRandomUtils::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;
- }
- MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size);
-
- R_TRY_CATCH(tmp_mcm.GetResult()) {
- R_CATCH(ResultKernelInvalidMemoryState) {
- continue;
- }
- } R_END_TRY_CATCH;
-
- if (!CanAddGuardRegions(process_handle, try_address, size)) {
- continue;
- }
-
- /* We're done searching. */
- out_mcm = std::move(tmp_mcm);
- return ResultSuccess;
- }
-
- return ResultRoInsufficientAddressSpace;
-}
-
-Result MapUtils::MapCodeMemoryForProcessDeprecated(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size) {
- u64 addspace_base, addspace_size;
- if (is_64_bit) {
- addspace_base = 0x8000000ULL;
- addspace_size = 0x78000000ULL;
- } else {
- addspace_base = 0x200000ULL;
- addspace_size = 0x3FE0000ULL;
- }
-
- if (size > addspace_size) {
- return ResultRoInsufficientAddressSpace;
- }
-
- u64 try_address;
- for (unsigned int i = 0; i < LocateRetryCount; i++) {
- try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12);
-
- MappedCodeMemory tmp_mcm(process_handle, try_address, base_address, size);
-
- R_TRY_CATCH(tmp_mcm.GetResult()) {
- R_CATCH(ResultKernelInvalidMemoryState) {
- continue;
- }
- } R_END_TRY_CATCH;
-
- if (!CanAddGuardRegions(process_handle, try_address, size)) {
- continue;
- }
-
- /* We're done searching. */
- out_mcm = std::move(tmp_mcm);
- return ResultSuccess;
- }
-
- return ResultRoInsufficientAddressSpace;
-}
-
-Result MapUtils::GetAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h) {
- R_TRY(svcGetInfo(&out->heap_base, 4, process_h, 0));
- R_TRY(svcGetInfo(&out->heap_size, 5, process_h, 0));
- R_TRY(svcGetInfo(&out->map_base, 2, process_h, 0));
- R_TRY(svcGetInfo(&out->map_size, 3, process_h, 0));
- R_TRY(svcGetInfo(&out->addspace_base, 12, process_h, 0));
- R_TRY(svcGetInfo(&out->addspace_size, 13, process_h, 0));
-
- 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 ResultSuccess;
-}
diff --git a/stratosphere/ro/source/ro_map.hpp b/stratosphere/ro/source/ro_map.hpp
deleted file mode 100644
index d03e72342..000000000
--- a/stratosphere/ro/source/ro_map.hpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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 MappedCodeMemory {
- private:
- Handle process_handle = INVALID_HANDLE;
- Result result = ResultRoInternalError;
- u64 dst_address = 0;
- u64 src_address = 0;
- u64 size = 0;
- public:
- MappedCodeMemory() : process_handle(INVALID_HANDLE), result(ResultRoInternalError), dst_address(0), src_address(0), size(0) {
- /* ... */
- }
-
- MappedCodeMemory(Handle p_h, u64 dst, u64 src, u64 sz) : process_handle(p_h), dst_address(dst), src_address(src), size(sz) {
- this->result = svcMapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size);
- }
-
- ~MappedCodeMemory() {
- if (this->process_handle != INVALID_HANDLE && this->size > 0 && R_SUCCEEDED(this->result)) {
- R_ASSERT((this->result = svcUnmapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size)));
- }
- }
-
- u64 GetDstAddress() const {
- return this->dst_address;
- }
-
- Result GetResult() const {
- return this->result;
- }
-
- bool IsSuccess() const {
- return R_SUCCEEDED(this->result);
- }
-
- void Invalidate() {
- this->process_handle = INVALID_HANDLE;
- }
-
- MappedCodeMemory &operator=(MappedCodeMemory &&o) {
- this->process_handle = o.process_handle;
- this->result = o.result;
- this->dst_address = o.dst_address;
- this->src_address = o.src_address;
- this->size = o.size;
- o.Invalidate();
- return *this;
- }
-};
-
-class AutoCloseMap {
- private:
- Handle process_handle;
- Result result;
- void *mapped_address;
- u64 base_address;
- u64 size;
- public:
- AutoCloseMap(void *mp, Handle p_h, u64 ba, u64 sz) : process_handle(p_h), mapped_address(mp), base_address(ba), size(sz) {
- this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size);
- }
- AutoCloseMap(u64 mp, Handle p_h, u64 ba, u64 sz) : process_handle(p_h), mapped_address(reinterpret_cast(mp)), base_address(ba), size(sz) {
- this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size);
- }
-
- ~AutoCloseMap() {
- if (this->process_handle != INVALID_HANDLE && R_SUCCEEDED(this->result)) {
- R_ASSERT((this->result = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size)));
- }
- }
-
- Result GetResult() const {
- return this->result;
- }
-
- bool IsSuccess() const {
- return R_SUCCEEDED(this->result);
- }
-
- void Invalidate() {
- this->process_handle = INVALID_HANDLE;
- }
-};
-
-
-class MapUtils {
- public:
- static constexpr size_t GuardRegionSize = 0x4000;
- static constexpr size_t LocateRetryCount = 0x200;
- 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;
- };
- private:
- 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 MapCodeMemoryForProcessDeprecated(MappedCodeMemory &out_mcm, Handle process_handle, bool is_64_bit, u64 base_address, u64 size);
- static Result MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size);
- public:
- static Result LocateSpaceForMap(u64 *out, u64 out_size);
- static Result MapCodeMemoryForProcess(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size);
- static bool CanAddGuardRegions(Handle process_handle, u64 address, u64 size);
-};
\ No newline at end of file
diff --git a/stratosphere/ro/source/ro_nrr.cpp b/stratosphere/ro/source/ro_nrr.cpp
deleted file mode 100644
index 69f83e797..000000000
--- a/stratosphere/ro/source/ro_nrr.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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_nrr.hpp"
-#include "ro_registration.hpp"
-
-Result NrrUtils::ValidateNrrSignature(const NrrHeader *header) {
- /* TODO: Implement RSA-2048 PSS..... */
-
- /* TODO: Check PSS fixed-key signature. */
- if (false) {
- return ResultRoNotAuthorized;
- }
-
- /* Check TitleID pattern is valid. */
- if ((header->title_id & header->title_id_mask) != header->title_id_pattern) {
- return ResultRoNotAuthorized;
- }
-
- /* TODO: Check PSS signature over hashes. */
- if (false) {
- return ResultRoNotAuthorized;
- }
-
- return ResultSuccess;
-}
-
-Result NrrUtils::ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, RoModuleType expected_type, bool enforce_type) {
- if (header->magic != MagicNrr0) {
- return ResultRoInvalidNrr;
- }
- if (header->nrr_size != size) {
- return ResultRoInvalidSize;
- }
-
- bool ease_nro_restriction = Registration::ShouldEaseNroRestriction();
-
- /* Check signature. */
- if (!ease_nro_restriction) {
- R_TRY(ValidateNrrSignature(header));
- }
-
- /* Check title id. */
- if (title_id != header->title_id) {
- if (!ease_nro_restriction) {
- return ResultRoInvalidNrr;
- }
- }
-
- /* Check type. */
- if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) {
- if (!enforce_type || expected_type != static_cast(header->nrr_type)) {
- if (!ease_nro_restriction) {
- return ResultRoInvalidNrrType;
- }
- }
- }
-
- return ResultSuccess;
-}
\ No newline at end of file
diff --git a/stratosphere/ro/source/ro_nrr.hpp b/stratosphere/ro/source/ro_nrr.hpp
deleted file mode 100644
index d49ab6374..000000000
--- a/stratosphere/ro/source/ro_nrr.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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
-
-enum RoModuleType : u32 {
- RoModuleType_ForSelf = 0,
- RoModuleType_ForOthers = 1,
-};
-
-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;
- 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!");
-
-class NrrUtils {
- public:
- static constexpr u32 MagicNrr0 = 0x3052524E;
- private:
- static Result ValidateNrrSignature(const NrrHeader *header);
- public:
- static Result ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, RoModuleType expected_type, bool enforce_type);
-};
\ No newline at end of file
diff --git a/stratosphere/ro/source/ro_patcher.cpp b/stratosphere/ro/source/ro_patcher.cpp
deleted file mode 100644
index 0ac52c98c..000000000
--- a/stratosphere/ro/source/ro_patcher.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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
-#include
-
-#include
-#include "ro_patcher.hpp"
-#include "ro_registration.hpp"
-
-/* IPS Patching adapted from Luma3DS (https://github.com/AuroraWright/Luma3DS/blob/master/sysmodules/loader/source/patcher.c) */
-
-#define IPS_MAGIC "PATCH"
-#define IPS_TAIL "EOF"
-
-#define IPS32_MAGIC "IPS32"
-#define IPS32_TAIL "EEOF"
-
-static inline u8 HexNybbleToU8(const char nybble) {
- if ('0' <= nybble && nybble <= '9') {
- return nybble - '0';
- } else if ('a' <= nybble && nybble <= 'f') {
- return nybble - 'a' + 0xa;
- } else {
- return nybble - 'A' + 0xA;
- }
-}
-
-static bool MatchesBuildId(const char *name, size_t name_len, const u8 *build_id) {
- /* Validate name is hex build id. */
- for (unsigned int i = 0; i < name_len - 4; i++) {
- if (isxdigit(name[i]) == 0) {
- return false;
- }
- }
-
- /* Read build id from name. */
- u8 build_id_from_name[0x20] = {0};
- for (unsigned int name_ofs = 0, id_ofs = 0; name_ofs < name_len - 4; id_ofs++) {
- build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]) << 4;
- build_id_from_name[id_ofs] |= HexNybbleToU8(name[name_ofs++]);
- }
-
- return memcmp(build_id, build_id_from_name, sizeof(build_id_from_name)) == 0;
-}
-
-static void ApplyIpsPatch(u8 *mapped_nro, size_t mapped_size, bool is_ips32, FILE *f_ips) {
- u8 buffer[4];
- while (fread(buffer, is_ips32 ? 4 : 3, 1, f_ips) == 1) {
- if (is_ips32 && memcmp(buffer, IPS32_TAIL, 4) == 0) {
- break;
- } else if (!is_ips32 && memcmp(buffer, IPS_TAIL, 3) == 0) {
- break;
- }
-
- /* Offset of patch. */
- u32 patch_offset;
- if (is_ips32) {
- patch_offset = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
- } else {
- patch_offset = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]);
- }
-
- /* Size of patch. */
- if (fread(buffer, 2, 1, f_ips) != 1) {
- break;
- }
- u32 patch_size = (buffer[0] << 8) | (buffer[1]);
-
- /* Check for RLE encoding. */
- if (patch_size == 0) {
- /* Size of RLE. */
- if (fread(buffer, 2, 1, f_ips) != 1) {
- break;
- }
-
- u32 rle_size = (buffer[0] << 8) | (buffer[1]);
-
- /* Value for RLE. */
- if (fread(buffer, 1, 1, f_ips) != 1) {
- break;
- }
-
- if (patch_offset < sizeof(Registration::NroHeader)) {
- if (patch_offset + rle_size > sizeof(Registration::NroHeader)) {
- u32 diff = sizeof(Registration::NroHeader) - patch_offset;
- patch_offset += diff;
- rle_size -= diff;
- goto IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS;
- }
- } else {
- IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS:
- if (patch_offset + rle_size > mapped_size) {
- rle_size = mapped_size - patch_offset;
- }
- memset(mapped_nro + patch_offset, buffer[0], rle_size);
- }
- } else {
- if (patch_offset < sizeof(Registration::NroHeader)) {
- if (patch_offset + patch_size > sizeof(Registration::NroHeader)) {
- u32 diff = sizeof(Registration::NroHeader) - patch_offset;
- patch_offset += diff;
- patch_size -= diff;
- fseek(f_ips, diff, SEEK_CUR);
- goto IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS;
- } else {
- fseek(f_ips, patch_size, SEEK_CUR);
- }
- } else {
- IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS:
- u32 read_size = patch_size;
- if (patch_offset + read_size > mapped_size) {
- read_size = mapped_size - patch_offset;
- }
- if (fread(mapped_nro + patch_offset, read_size, 1, f_ips) != 1) {
- break;
- }
- if (patch_size > read_size) {
- fseek(f_ips, patch_size - read_size, SEEK_CUR);
- }
- }
- }
- }
-}
-
-void PatchUtils::ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size) {
- /* Inspect all patches from /atmosphere/nro_patches/<*>/<*>.ips */
- char path[FS_MAX_PATH+1] = {0};
- snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches");
- DIR *patches_dir = opendir(path);
- struct dirent *pdir_ent;
- if (patches_dir != NULL) {
- /* Iterate over the patches directory to find patch subdirectories. */
- while ((pdir_ent = readdir(patches_dir)) != NULL) {
- if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) {
- continue;
- }
- snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches/%s", pdir_ent->d_name);
- DIR *patch_dir = opendir(path);
- struct dirent *ent;
- if (patch_dir != NULL) {
- /* Iterate over the patch subdirectory to find .ips patches. */
- while ((ent = readdir(patch_dir)) != NULL) {
- if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
- continue;
- }
- size_t name_len = strlen(ent->d_name);
- if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && MatchesBuildId(ent->d_name, name_len, module_id->build_id)) {
- snprintf(path, sizeof(path) - 1, "sdmc:/atmosphere/nro_patches/%s/%s", pdir_ent->d_name, ent->d_name);
- FILE *f_ips = fopen(path, "rb");
- if (f_ips != NULL) {
- u8 header[5];
- if (fread(header, 5, 1, f_ips) == 1) {
- if (memcmp(header, IPS_MAGIC, 5) == 0) {
- ApplyIpsPatch(mapped_nro, mapped_size, false, f_ips);
- } else if (memcmp(header, IPS32_MAGIC, 5) == 0) {
- ApplyIpsPatch(mapped_nro, mapped_size, true, f_ips);
- }
- }
- fclose(f_ips);
- }
- }
- }
- closedir(patch_dir);
- }
- }
- closedir(patches_dir);
- }
-}
\ No newline at end of file
diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp
deleted file mode 100644
index 7ecff50b3..000000000
--- a/stratosphere/ro/source/ro_registration.cpp
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * 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"
-#include "ro_map.hpp"
-#include "ro_nrr.hpp"
-#include "ro_patcher.hpp"
-
-/* 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() {
- DoWithSmSession([&]() {
- R_ASSERT(splInitialize());
- });
- ON_SCOPE_EXIT { splExit(); };
-
- R_ASSERT(splIsDevelopment(&g_is_development_hardware));
-
- {
- u64 out_val = 0;
- R_ASSERT(splGetConfig(SplConfigItem_IsDebugMode, &out_val));
- g_is_development_function_enabled = out_val != 0;
- }
-}
-
-bool Registration::ShouldEaseNroRestriction() {
- bool should_ease = false;
-
- if (R_FAILED(setsysGetSettingsItemValue("ro", "ease_nro_restriction", &should_ease, sizeof(should_ease)))) {
- return false;
- }
-
- /* Nintendo only allows easing restriction on dev, we will allow on production, as well. */
- /* should_ease &= g_is_development_function_enabled; */
- return should_ease;
-}
-
-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) {
- std::memset(&g_process_contexts[i], 0, sizeof(g_process_contexts[i]));
- 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);
- }
- }
- svcCloseHandle(context->process_handle);
- }
- std::memset(context, 0, sizeof(*context));
-}
-
-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::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;
- R_TRY(MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size, expected_type, enforce_type));
-
- /* 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::LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
- /* Validate address/size. */
- if (nro_address & 0xFFF) {
- return ResultRoInvalidAddress;
- }
- if (nro_size == 0 || (nro_size & 0xFFF) || !(nro_address < nro_address + nro_size)) {
- return ResultRoInvalidSize;
- }
- if (bss_address & 0xFFF) {
- return ResultRoInvalidAddress;
- }
- if ((bss_size & 0xFFF) || (bss_size > 0 && !(bss_address < bss_address + bss_size))) {
- return ResultRoInvalidSize;
- }
-
- const u64 total_size = nro_size + bss_size;
- if (total_size < nro_size || total_size < bss_size) {
- return ResultRoInvalidSize;
- }
-
- /* Check we have space for a new NRO. */
- size_t slot = 0;
- for (slot = 0; slot < Registration::MaxNroInfos; slot++) {
- if (!context->nro_in_use[slot]) {
- break;
- }
- }
- if (slot == Registration::MaxNroInfos) {
- return ResultRoTooManyNro;
- }
-
- NroInfo *nro_info = &context->nro_infos[slot];
- nro_info->nro_heap_address = nro_address;
- nro_info->nro_heap_size = nro_size;
- nro_info->bss_heap_address = bss_address;
- nro_info->bss_heap_size = bss_size;
-
- /* Map the NRO. */
- R_TRY(MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size));
-
- /* Validate the NRO (parsing region extents). */
- u64 rx_size, ro_size, rw_size;
- R_TRY_CLEANUP(ValidateNro(&nro_info->module_id, &rx_size, &ro_size, &rw_size, context, nro_info->base_address, nro_size, bss_size), {
- UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0);
- });
-
- /* Set NRO perms. */
- R_TRY_CLEANUP(SetNroPerms(context->process_handle, nro_info->base_address, rx_size, ro_size, rw_size + bss_size), {
- UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size);
- });
-
- nro_info->code_size = rx_size + ro_size;
- nro_info->rw_size = rw_size;
- context->nro_in_use[slot] = true;
- *out_address = nro_info->base_address;
- return ResultSuccess;
-}
-
-bool Registration::IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash) {
- for (size_t i = 0; i < Registration::MaxNrrInfos; i++) {
- if (context->nrr_in_use[i]) {
- const Sha256Hash *nro_hashes = reinterpret_cast(reinterpret_cast(context->nrr_infos[i].header) + context->nrr_infos[i].header->hash_offset);
- if (std::binary_search(nro_hashes, nro_hashes + context->nrr_infos[i].header->num_hashes, *hash)) {
- return true;
- }
- }
- }
- return false;
-}
-
-Result Registration::ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size) {
- /* Find space to map the NRO. */
- u64 map_address;
- if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nro_size))) {
- return ResultRoInsufficientAddressSpace;
- }
-
- /* Actually map the NRO. */
- AutoCloseMap nro_map(map_address, context->process_handle, base_address, nro_size);
- if (!nro_map.IsSuccess()) {
- return nro_map.GetResult();
- }
-
- /* Validate header. */
- const Registration::NroHeader *header = reinterpret_cast(map_address);
- if (header->magic != MagicNro0) {
- return ResultRoInvalidNro;
- }
- if (header->nro_size != nro_size || header->bss_size != bss_size) {
- return ResultRoInvalidNro;
- }
- if ((header->text_size & 0xFFF) || (header->ro_size & 0xFFF) || (header->rw_size & 0xFFF) || (header->bss_size & 0xFFF)) {
- return ResultRoInvalidNro;
- }
- if (header->text_offset > header->ro_offset || header->ro_offset > header->rw_offset) {
- return ResultRoInvalidNro;
- }
- if (header->text_offset != 0 || header->text_offset + header->text_size != header->ro_offset || header->ro_offset + header->ro_size != header->rw_offset || header->rw_offset + header->rw_size != header->nro_size) {
- return ResultRoInvalidNro;
- }
-
- /* Verify NRO hash. */
- {
- Sha256Hash hash;
- sha256CalculateHash(&hash, header, nro_size);
- if (!IsNroHashPresent(context, &hash)) {
- return ResultRoNotAuthorized;
- }
- }
-
- ModuleId module_id;
- std::memcpy(&module_id, header->build_id, sizeof(module_id));
-
- /* Check if NRO has already been loaded. */
- for (size_t i = 0; i < Registration::MaxNroInfos; i++) {
- if (context->nro_in_use[i]) {
- if (std::memcmp(&context->nro_infos[i].module_id, &module_id, sizeof(module_id)) == 0) {
- return ResultRoAlreadyLoaded;
- }
- }
- }
-
- /* Apply patches to NRO. */
- PatchUtils::ApplyPatches(&module_id, reinterpret_cast(map_address), nro_size);
-
- *out_module_id = module_id;
- *out_rx_size = header->text_size;
- *out_ro_size = header->ro_size;
- *out_rw_size = header->rw_size;
- return ResultSuccess;
-}
-
-Result Registration::SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) {
- const u64 rx_offset = 0;
- const u64 ro_offset = rx_offset + rx_size;
- const u64 rw_offset = ro_offset + ro_size;
-
- R_TRY(svcSetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, Perm_Rx));
- R_TRY(svcSetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, Perm_R ));
- R_TRY(svcSetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, Perm_Rw));
-
- return ResultSuccess;
-}
-
-Result Registration::UnloadNro(RoProcessContext *context, u64 nro_address) {
- /* Validate address. */
- if (nro_address & 0xFFF) {
- return ResultRoInvalidAddress;
- }
-
- /* Check the NRO is loaded. */
- size_t slot = 0;
- for (slot = 0; slot < Registration::MaxNroInfos; slot++) {
- if (!context->nro_in_use[slot]) {
- continue;
- }
-
- if (context->nro_infos[slot].base_address == nro_address) {
- break;
- }
- }
- if (slot == Registration::MaxNroInfos) {
- return ResultRoNotLoaded;
- }
-
- /* Unmap. */
- const NroInfo nro_info = context->nro_infos[slot];
- {
- /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */
- context->nro_in_use[slot] = false;
- std::memset(&context->nro_infos[slot], 0, sizeof(context->nro_infos[slot]));
- }
- return UnmapNro(context->process_handle, nro_info.base_address, nro_info.nro_heap_address, nro_info.bss_heap_address, nro_info.bss_heap_size, nro_info.code_size, nro_info.rw_size);
-}
-
-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, RoModuleType expected_type, bool enforce_type) {
- MappedCodeMemory nrr_mcm;
-
- /* First, map the NRR. */
- R_TRY(MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, nrr_heap_address, nrr_heap_size));
-
- const u64 code_address = nrr_mcm.GetDstAddress();
- u64 map_address;
- if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nrr_heap_size))) {
- return ResultRoInsufficientAddressSpace;
- }
-
- /* Nintendo...does not check the return value of this map. We will check, instead of aborting if it fails. */
- AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size);
- if (!nrr_map.IsSuccess()) {
- return nrr_map.GetResult();
- }
-
- NrrHeader *nrr_header = reinterpret_cast(map_address);
- R_TRY(NrrUtils::ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type));
-
- /* Invalidation here actually prevents them from unmapping at scope exit. */
- nrr_map.Invalidate();
- nrr_mcm.Invalidate();
-
- *out_header = nrr_header;
- *out_mapped_code_address = code_address;
- return ResultSuccess;
-}
-
-Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) {
- R_TRY(svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size));
- return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size);
-}
-
-Result Registration::MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
- MappedCodeMemory nro_mcm;
- MappedCodeMemory bss_mcm;
- u64 base_address;
-
- /* Map the NRO, and map the BSS immediately after it. */
- size_t i = 0;
- for (i = 0; i < MapUtils::LocateRetryCount; i++) {
- MappedCodeMemory tmp_nro_mcm;
- R_TRY(MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, nro_heap_address, nro_heap_size));
- base_address = tmp_nro_mcm.GetDstAddress();
-
- if (bss_heap_size > 0) {
- MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size);
- R_TRY_CATCH(tmp_bss_mcm.GetResult()) {
- R_CATCH(ResultKernelInvalidMemoryState) {
- continue;
- }
- } R_END_TRY_CATCH;
-
- if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size + bss_heap_size)) {
- continue;
- }
-
- bss_mcm = std::move(tmp_bss_mcm);
- } else {
- if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size)) {
- continue;
- }
- }
- nro_mcm = std::move(tmp_nro_mcm);
- break;
- }
- if (i == MapUtils::LocateRetryCount) {
- return ResultRoInsufficientAddressSpace;
- }
-
- /* Invalidation here actually prevents them from unmapping at scope exit. */
- nro_mcm.Invalidate();
- bss_mcm.Invalidate();
-
- *out_base_address = base_address;
- return ResultSuccess;
-}
-
-Result Registration::UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) {
- /* First, unmap bss. */
- if (bss_heap_size > 0) {
- R_TRY(svcUnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size));
- }
-
- /* Next, unmap .rwdata */
- if (rw_size > 0) {
- R_TRY(svcUnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size));
- }
-
- /* Finally, unmap .text + .rodata. */
- R_TRY(svcUnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size));
-
- return ResultSuccess;
-}
diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp
deleted file mode 100644
index f416534f1..000000000
--- a/stratosphere/ro/source/ro_registration.hpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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
-
-#include "ro_nrr.hpp"
-#include "ro_types.hpp"
-
-class Registration {
- public:
- /* 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;
-
- static constexpr u32 MagicNro0 = 0x304F524E;
- public:
- 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 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;
- };
- 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, RoModuleType expected_type, bool enforce_type);
- static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address);
- static bool IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash);
-
- static Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
- static Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size);
- static Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
- static Result UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size);
- public:
- static void Initialize();
- static bool ShouldEaseNroRestriction();
-
- 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 LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
- static Result UnloadNro(RoProcessContext *context, u64 nro_address);
-
- static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id);
-};
\ No newline at end of file
diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp
index d03562f3e..4bfa845ec 100644
--- a/stratosphere/ro/source/ro_service.cpp
+++ b/stratosphere/ro/source/ro_service.cpp
@@ -20,83 +20,53 @@
#include
#include "ro_service.hpp"
-#include "ro_registration.hpp"
+#include "impl/ro_service_impl.hpp"
-RelocatableObjectsService::~RelocatableObjectsService() {
- if (this->IsInitialized()) {
- Registration::UnregisterProcess(this->context);
- this->context = nullptr;
+namespace sts::ro {
+
+ void SetDevelopmentHardware(bool is_development_hardware) {
+ impl::SetDevelopmentHardware(is_development_hardware);
}
+
+ void SetDevelopmentFunctionEnabled(bool is_development_function_enabled) {
+ impl::SetDevelopmentFunctionEnabled(is_development_function_enabled);
+ }
+
+ Service::Service(ModuleType t) : context_id(impl::InvalidContextId), type(t) {
+ /* ... */
+ }
+
+ Service::~Service() {
+ impl::UnregisterProcess(this->context_id);
+ }
+
+ Result Service::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
+ R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
+ return impl::LoadNro(load_address.GetPointer(), this->context_id, nro_address, nro_size, bss_address, bss_size);
+ }
+
+ Result Service::UnloadNro(PidDescriptor pid_desc, u64 nro_address) {
+ R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
+ return impl::UnloadNro(this->context_id, nro_address);
+ }
+
+ Result Service::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) {
+ R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
+ return impl::LoadNrr(this->context_id, INVALID_HANDLE, nrr_address, nrr_size, ModuleType::ForSelf, true);
+ }
+
+ Result Service::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) {
+ R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
+ return impl::UnloadNrr(this->context_id, nrr_address);
+ }
+
+ Result Service::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) {
+ return impl::RegisterProcess(&this->context_id, process_h.handle, pid_desc.pid);
+ }
+
+ Result Service::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) {
+ R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
+ return impl::LoadNrr(this->context_id, process_h.handle, nrr_address, nrr_size, this->type, this->type == ModuleType::ForOthers);
+ }
+
}
-
-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. */
- R_ASSERT(svcGetInfo(&title_id, 18, process_handle, 0));
- } else {
- /* 1.0.0-2.3.0: We're not inside loader, so ask pm. */
- u64 process_id = 0;
- R_ASSERT(svcGetProcessId(&process_id, process_handle));
- R_ASSERT(pminfoGetTitleId(&title_id, process_id));
- }
- return title_id;
-}
-
-Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
- if (!this->IsProcessIdValid(pid_desc.pid)) {
- return ResultRoInvalidProcess;
- }
-
- return Registration::LoadNro(load_address.GetPointer(), this->context, nro_address, nro_size, bss_address, bss_size);
-}
-
-Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) {
- if (!this->IsProcessIdValid(pid_desc.pid)) {
- return ResultRoInvalidProcess;
- }
-
- return Registration::UnloadNro(this->context, nro_address);
-}
-
-Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) {
- 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) {
- if (!this->IsProcessIdValid(pid_desc.pid)) {
- return ResultRoInvalidProcess;
- }
-
- return Registration::UnloadNrr(this->context, nrr_address);
-}
-
-Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) {
- /* 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) {
- 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 aa693011c..5e2914610 100644
--- a/stratosphere/ro/source/ro_service.hpp
+++ b/stratosphere/ro/source/ro_service.hpp
@@ -18,48 +18,48 @@
#include
#include
+#include
-#include "ro_registration.hpp"
+namespace sts::ro {
-enum RoServiceCmd {
- Ro_Cmd_LoadNro = 0,
- Ro_Cmd_UnloadNro = 1,
- Ro_Cmd_LoadNrr = 2,
- Ro_Cmd_UnloadNrr = 3,
- Ro_Cmd_Initialize = 4,
- Ro_Cmd_LoadNrrEx = 10,
-};
+ /* Access utilities. */
+ void SetDevelopmentHardware(bool is_development_hardware);
+ void SetDevelopmentFunctionEnabled(bool is_development_function_enabled);
-class RelocatableObjectsService final : public IServiceObject {
- private:
- Registration::RoProcessContext *context = nullptr;
- RoModuleType type;
- public:
- explicit RelocatableObjectsService(RoModuleType t) : type(t) {
- /* ... */
- }
- virtual ~RelocatableObjectsService() override;
- private:
- 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);
- Result UnloadNro(PidDescriptor pid_desc, u64 nro_address);
- Result LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size);
- Result UnloadNrr(PidDescriptor pid_desc, u64 nrr_address);
- Result Initialize(PidDescriptor pid_desc, CopiedHandle process_h);
- Result LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h);
- public:
- DEFINE_SERVICE_DISPATCH_TABLE {
- MakeServiceCommandMeta(),
- MakeServiceCommandMeta(),
- MakeServiceCommandMeta(),
- MakeServiceCommandMeta(),
- MakeServiceCommandMeta(),
- MakeServiceCommandMeta(),
- };
-};
+ class Service final : public IServiceObject {
+ protected:
+ enum class CommandId {
+ LoadNro = 0,
+ UnloadNro = 1,
+ LoadNrr = 2,
+ UnloadNrr = 3,
+ Initialize = 4,
+ LoadNrrEx = 10,
+ };
+ private:
+ size_t context_id;
+ ModuleType type;
+ public:
+ explicit Service(ModuleType t);
+ virtual ~Service() override;
+ private:
+ /* Actual commands. */
+ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
+ Result UnloadNro(PidDescriptor pid_desc, u64 nro_address);
+ Result LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size);
+ Result UnloadNrr(PidDescriptor pid_desc, u64 nrr_address);
+ Result Initialize(PidDescriptor pid_desc, CopiedHandle process_h);
+ Result LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h);
+ public:
+ DEFINE_SERVICE_DISPATCH_TABLE {
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ };
+
+ };
+
+}
diff --git a/stratosphere/ro/source/ro_types.hpp b/stratosphere/ro/source/ro_types.hpp
deleted file mode 100644
index b6473adb1..000000000
--- a/stratosphere/ro/source/ro_types.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
-
-struct ModuleId {
- u8 build_id[0x20];
-};
-static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!");
-
-struct Sha256Hash {
- u8 hash[0x20];
-
- bool operator==(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) == 0;
- }
- bool operator!=(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) != 0;
- }
- bool operator<(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) < 0;
- }
- bool operator>(const Sha256Hash &o) const {
- return std::memcmp(this, &o, sizeof(*this)) > 0;
- }
-};
-static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!");