From 6004b7479e13450f662d7eaa80a4ead677954589 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 17:43:56 -0700 Subject: [PATCH 01/13] ro: add ro sysmodule skeleton --- Makefile | 2 + stratosphere/Makefile | 2 +- stratosphere/ro/Makefile | 166 +++++++++++++++++++++++++++++ stratosphere/ro/ro.json | 94 ++++++++++++++++ stratosphere/ro/source/ro_main.cpp | 117 ++++++++++++++++++++ 5 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 stratosphere/ro/Makefile create mode 100644 stratosphere/ro/ro.json create mode 100644 stratosphere/ro/source/ro_main.cpp diff --git a/Makefile b/Makefile index e38f766b9..384f76d63 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ dist: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 @@ -69,6 +70,7 @@ dist: all cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp + cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 7bbb09435..76ce0d97a 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt +MODULES := loader pm sm boot ams_mitm eclct.stub ro creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) diff --git a/stratosphere/ro/Makefile b/stratosphere/ro/Makefile new file mode 100644 index 000000000..2d3bf2862 --- /dev/null +++ b/stratosphere/ro/Makefile @@ -0,0 +1,166 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +AMSBRANCH := $(shell git symbolic-ref --short HEAD) +AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) + +ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) + AMSREV := $(AMSREV)-dirty +endif + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../../common/include +EXEFS_SRC := exefs_src + +DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" -DINI_MAX_LINE=768 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lstratosphere -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/ro/ro.json b/stratosphere/ro/ro.json new file mode 100644 index 000000000..8b4efa28c --- /dev/null +++ b/stratosphere/ro/ro.json @@ -0,0 +1,94 @@ +{ + "name": "ro", + "title_id": "0x0100000000000037", + "title_id_range_min": "0x0100000000000037", + "title_id_range_max": "0x0100000000000037", + "main_thread_stack_size": "0x00005000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 1, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv"], + "service_host": ["ldr:ro", "ro:dmnt", "ro:1"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 59, + "lowest_thread_priority": 28, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcSetProcessMemoryPermission": "0x73", + "svcMapProcessMemory": "0x74", + "svcUnmapProcessMemory": "0x75", + "svcQueryProcessMemory": "0x76", + "svcMapProcessCodeMemory": "0x77", + "svcUnmapProcessCodeMemory": "0x78", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp new file mode 100644 index 000000000..0844a1de3 --- /dev/null +++ b/stratosphere/ro/source/ro_main.cpp @@ -0,0 +1,117 @@ +/* + * 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 + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x30000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[0x1000]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + u64 __stratosphere_title_id = TitleId_Ro; + void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + StratosphereCrashHandler(ctx); +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + Result rc; + + SetFirmwareVersionForLibnx(); + + rc = smInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = setsysInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = splInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) { + std::abort(); + } + + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); +} + +void __appExit(void) { + fsdevUnmountAll(); + fsExit(); + splExit(); + setsysExit(); + smExit(); +} + +int main(int argc, char **argv) +{ + /* Static server manager. */ + static auto g_server_manager = WaitableManager(1); + + /* TODO: Create services. */ + + /* Loop forever, servicing our services. */ + g_server_manager.Process(); + + /* Cleanup */ + return 0; +} + From 4ac8f2745bdc5439139d9575b080c0b09d9215d5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:13:32 -0700 Subject: [PATCH 02/13] ro: skeleton ro:dmnt --- stratosphere/ro/source/ro_debug_monitor.cpp | 26 ++++++++++++++++ stratosphere/ro/source/ro_debug_monitor.hpp | 33 +++++++++++++++++++++ stratosphere/ro/source/ro_main.cpp | 7 +++-- 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 stratosphere/ro/source/ro_debug_monitor.cpp create mode 100644 stratosphere/ro/source/ro_debug_monitor.hpp diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp new file mode 100644 index 000000000..fb26fa66b --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -0,0 +1,26 @@ +/* + * 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_debug_monitor.hpp" + +Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { + /* TODO: Implement. */ + return ResultKernelConnectionClosed; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_debug_monitor.hpp b/stratosphere/ro/source/ro_debug_monitor.hpp new file mode 100644 index 000000000..d8273e2df --- /dev/null +++ b/stratosphere/ro/source/ro_debug_monitor.hpp @@ -0,0 +1,33 @@ +/* + * 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 DebugMonitorServiceCmd { + Dmnt_Cmd_GetProcessModuleInfo = 0 +}; + +class DebugMonitorService final : public IServiceObject { + 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 0844a1de3..4c7264ff0 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -23,6 +23,8 @@ #include #include +#include "ro_debug_monitor.hpp" + extern "C" { extern u32 __start__; @@ -104,12 +106,13 @@ void __appExit(void) { int main(int argc, char **argv) { /* Static server manager. */ - static auto g_server_manager = WaitableManager(1); + static auto s_server_manager = WaitableManager(1); /* TODO: Create services. */ + s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); /* Loop forever, servicing our services. */ - g_server_manager.Process(); + s_server_manager.Process(); /* Cleanup */ return 0; From 79c52e2b9144620df9bb0e986ebbab5ba79095e1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:37:01 -0700 Subject: [PATCH 03/13] ro: skeleton ldr:ro --- stratosphere/ro/source/ro_main.cpp | 14 +++++- stratosphere/ro/source/ro_service.cpp | 56 +++++++++++++++++++++++ stratosphere/ro/source/ro_service.hpp | 64 +++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 stratosphere/ro/source/ro_service.cpp create mode 100644 stratosphere/ro/source/ro_service.hpp diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 4c7264ff0..cff2733f4 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -24,6 +24,7 @@ #include #include "ro_debug_monitor.hpp" +#include "ro_service.hpp" extern "C" { extern u32 __start__; @@ -103,13 +104,24 @@ void __appExit(void) { smExit(); } +/* Helpers to create RO objects. */ +static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForSelf); }; + int main(int argc, char **argv) { /* Static server manager. */ static auto s_server_manager = WaitableManager(1); - /* TODO: Create services. */ + /* Create services. */ s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); + { + + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); + } + } /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp new file mode 100644 index 000000000..0ecbf1551 --- /dev/null +++ b/stratosphere/ro/source/ro_service.cpp @@ -0,0 +1,56 @@ +/* + * 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_service.hpp" + +RelocatableObjectsService::~RelocatableObjectsService() { + /* TODO */ +} + +Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result RelocatableObjectsService::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) { + /* TODO */ + return ResultKernelConnectionClosed; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp new file mode 100644 index 000000000..8066c86e8 --- /dev/null +++ b/stratosphere/ro/source/ro_service.hpp @@ -0,0 +1,64 @@ +/* + * 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 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, +}; + +enum RoServiceType : u32 { + RoServiceType_ForSelf = 0, + RoServiceType_ForOthers = 1, +}; + +class RelocatableObjectsService final : public IServiceObject { + Handle process_handle = 0; + u64 process_id = U64_MAX; + bool has_initialized = false; + RoServiceType type; + public: + explicit RelocatableObjectsService(RoServiceType t) : type(t) { + /* ... */ + } + virtual ~RelocatableObjectsService() 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(), + }; +}; From e04fcfff6bc5f08b6cf3a8f14a9ae9c46d41a2ee Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 20 Apr 2019 18:37:50 -0700 Subject: [PATCH 04/13] ro: fix typo --- stratosphere/ro/source/ro_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index cff2733f4..4bd9b4893 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -106,7 +106,7 @@ void __appExit(void) { /* Helpers to create RO objects. */ static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; -static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForOthers); }; int main(int argc, char **argv) { From d69fc060f4878e551512429b5085b86a26dcb2da Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 02:09:08 -0700 Subject: [PATCH 05/13] ro: Implement ro:dmnt --- stratosphere/ro/source/ro_debug_monitor.cpp | 11 +- stratosphere/ro/source/ro_main.cpp | 13 +-- stratosphere/ro/source/ro_registration.cpp | 95 +++++++++++++++++ stratosphere/ro/source/ro_registration.hpp | 108 ++++++++++++++++++++ stratosphere/ro/source/ro_service.cpp | 5 +- stratosphere/ro/source/ro_service.hpp | 11 +- 6 files changed, 226 insertions(+), 17 deletions(-) create mode 100644 stratosphere/ro/source/ro_registration.cpp create mode 100644 stratosphere/ro/source/ro_registration.hpp diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp index fb26fa66b..ef6897c60 100644 --- a/stratosphere/ro/source/ro_debug_monitor.cpp +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -15,12 +15,15 @@ */ #include -#include -#include #include +#include + #include "ro_debug_monitor.hpp" +#include "ro_registration.hpp" Result DebugMonitorService::GetProcessModuleInfo(Out count, OutBuffer out_infos, u64 pid) { - /* TODO: Implement. */ - return ResultKernelConnectionClosed; + if (out_infos.num_elements > INT_MAX) { + return ResultRoInvalidSize; + } + return Registration::GetProcessModuleInfo(count.GetPointer(), out_infos.buffer, out_infos.num_elements, pid); } \ No newline at end of file diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 4bd9b4893..1a3787677 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -77,7 +77,7 @@ void __appInit(void) { if (R_FAILED(rc)) { std::abort(); } - + rc = splInitialize(); if (R_FAILED(rc)) { std::abort(); @@ -114,13 +114,10 @@ int main(int argc, char **argv) static auto s_server_manager = WaitableManager(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 1)); - { - - s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { - s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); - } + s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { + s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); } /* Loop forever, servicing our services. */ diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp new file mode 100644 index 000000000..fe2726e76 --- /dev/null +++ b/stratosphere/ro/source/ro_registration.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "ro_registration.hpp" + +/* Declare process contexts as static to this function. */ +static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; + +Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { + /* Check if a process context already exists. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (g_process_contexts[i].process_id == process_id) { + return ResultRoInvalidSession; + } + } + + /* Find a free process context. */ + for (size_t i = 0; i < Registration::MaxSessions; i++) { + if (!g_process_contexts[i].in_use) { + g_process_contexts[i].process_id = process_id; + g_process_contexts[i].process_handle = process_handle; + g_process_contexts[i].in_use = true; + *out_context = &g_process_contexts[i]; + return ResultSuccess; + } + } + + /* Failure to find a free context is actually an abort condition. */ + /* TODO: Should this return an unofficial error code? */ + std::abort(); +} + +void Registration::UnregisterProcess(RoProcessContext *context) { + if (context->process_handle != INVALID_HANDLE) { + for (size_t i = 0; i < Registration::MaxNrrInfos; i++) { + if (context->nrr_in_use[i]) { + UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address); + } + } + } +} + +Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { + size_t count = 0; + for (size_t sess = 0; sess < Registration::MaxSessions; sess++) { + if (g_process_contexts[sess].process_id == process_id) { + /* For convenience, helper. */ + const RoProcessContext *context = &g_process_contexts[sess]; + + for (size_t i = 0; i < Registration::MaxNroInfos && count < max_out_count; i++) { + if (!context->nro_in_use[i]) { + continue; + } + + /* Just copy out the info. */ + LoaderModuleInfo *out_info = &out_infos[count++]; + memcpy(out_info->build_id, &context->nro_infos[i].module_id, sizeof(context->nro_infos[i].module_id)); + out_info->base_address = context->nro_infos[i].base_address; + out_info->size = context->nro_infos[i].nro_heap_size + context->nro_infos[i].bss_heap_size; + } + + break; + } + } + + *out_count = static_cast(count); + return ResultSuccess; +} + +Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { + Result rc = svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size); + if (R_FAILED(rc)) { + return rc; + } + + return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size); +} diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp new file mode 100644 index 000000000..b869d7e4d --- /dev/null +++ b/stratosphere/ro/source/ro_registration.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#include + +class Registration { + public: + static constexpr size_t MaxSessions = 0x8; + static constexpr size_t MaxNrrInfos = 0x40; + static constexpr size_t MaxNroInfos = 0x40; + public: + struct NrrHeader { + u32 magic; + u32 _0x4; + u32 _0x8; + u32 _0xC; + u64 title_id_mask; + u64 title_id_pattern; + u64 _0x20; + u64 _0x28; + u8 modulus[0x100]; + u8 fixed_key_signature[0x100]; + u8 nrr_signature[0x100]; + u64 title_id_min; + u32 nrr_size; + u8 nrr_type; /* 7.0.0+ */ + u8 _0x33D[3]; + u32 hash_offset; + u32 num_hashes; + u64 _0x348; + }; + static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); + + struct NroHeader { + u32 entrypoint_insn; + u32 mod_offset; + u64 padding; + u32 magic; + u32 _0x14; + u32 nro_size; + u32 _0x1C; + u32 text_offset; + u32 text_size; + u32 ro_offset; + u32 ro_size; + u32 rw_offset; + u32 rw_size; + u32 bss_size; + u32 _0x3C; + unsigned char build_id[0x20]; + u8 _0x60[0x20]; + }; + static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!"); + + struct ModuleId { + u8 build_id[0x20]; + }; + static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!"); + + struct NroInfo { + u64 base_address; + u64 nro_heap_address; + u64 nro_heap_size; + u64 bss_heap_address; + u64 bss_heap_size; + u64 code_size; + u64 rw_size; + ModuleId module_id; + }; + struct NrrInfo { + NrrHeader *header; + u64 nrr_heap_address; + u64 nrr_heap_size; + u64 mapped_code_address; + }; + struct RoProcessContext { + bool nro_in_use[MaxNroInfos]; + bool nrr_in_use[MaxNrrInfos]; + NroInfo nro_infos[MaxNroInfos]; + NrrInfo nrr_infos[MaxNrrInfos]; + Handle process_handle; + u64 process_id; + bool in_use; + }; + public: + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); + static void UnregisterProcess(RoProcessContext *context); + + static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); + + static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index 0ecbf1551..8dc2bd504 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -20,9 +20,12 @@ #include #include "ro_service.hpp" +#include "ro_registration.hpp" RelocatableObjectsService::~RelocatableObjectsService() { - /* TODO */ + if (this->IsInitialized()) { + Registration::UnregisterProcess(this->context); + } } Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp index 8066c86e8..2d68f84bd 100644 --- a/stratosphere/ro/source/ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -19,6 +19,8 @@ #include +#include "ro_registration.hpp" + enum RoServiceCmd { Ro_Cmd_LoadNro = 0, Ro_Cmd_UnloadNro = 1, @@ -34,16 +36,17 @@ enum RoServiceType : u32 { }; class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; + Registration::RoProcessContext *context = nullptr; RoServiceType type; public: explicit RelocatableObjectsService(RoServiceType t) : type(t) { /* ... */ } virtual ~RelocatableObjectsService() override; - + private: + bool IsInitialized() const { + return this->context != nullptr; + } private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); From cb88fdfd62f79ef4f8e5addabf8566fa2f5b70f2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 03:39:29 -0700 Subject: [PATCH 06/13] ro: implement UnloadNrr, half of LoadNrr/LoadNrrEx --- stratosphere/pm/source/pm_main.cpp | 2 +- stratosphere/ro/ro.json | 2 +- stratosphere/ro/source/ro_main.cpp | 27 ++++-- stratosphere/ro/source/ro_registration.cpp | 101 ++++++++++++++++++++- stratosphere/ro/source/ro_registration.hpp | 18 +++- stratosphere/ro/source/ro_service.cpp | 58 ++++++++++-- stratosphere/ro/source/ro_service.hpp | 14 ++- 7 files changed, 191 insertions(+), 31 deletions(-) diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 5c53aa250..01ed21f68 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -180,7 +180,7 @@ int main(int argc, char **argv) server_manager->AddWaitable(new ServiceServer("pm:shell", 3)); server_manager->AddWaitable(new ServiceServer("pm:dmnt", 2)); server_manager->AddWaitable(new ServiceServer("pm:bm", 6)); - server_manager->AddWaitable(new ServiceServer("pm:info", 1)); + server_manager->AddWaitable(new ServiceServer("pm:info", 2)); /* Loop forever, servicing our services. */ server_manager->Process(); diff --git a/stratosphere/ro/ro.json b/stratosphere/ro/ro.json index 8b4efa28c..57d4d3bf3 100644 --- a/stratosphere/ro/ro.json +++ b/stratosphere/ro/ro.json @@ -14,7 +14,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv"], + "service_access": ["fatal:u", "spl:", "set:sys", "fsp-srv", "pm:info"], "service_host": ["ldr:ro", "ro:dmnt", "ro:1"], "kernel_capabilities": [{ "type": "kernel_flags", diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 1a3787677..cd5f9e807 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -25,6 +25,7 @@ #include "ro_debug_monitor.hpp" #include "ro_service.hpp" +#include "ro_registration.hpp" extern "C" { extern u32 __start__; @@ -77,16 +78,18 @@ void __appInit(void) { if (R_FAILED(rc)) { std::abort(); } - - rc = splInitialize(); - if (R_FAILED(rc)) { - std::abort(); - } rc = fsInitialize(); if (R_FAILED(rc)) { std::abort(); } + + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + std::abort(); + } + } rc = fsdevMountSdmc(); if (R_FAILED(rc)) { @@ -99,23 +102,29 @@ void __appInit(void) { void __appExit(void) { fsdevUnmountAll(); fsExit(); - splExit(); + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + pminfoExit(); + } setsysExit(); smExit(); } /* Helpers to create RO objects. */ -static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoServiceType_ForSelf); }; -static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoServiceType_ForOthers); }; +static const auto MakeRoServiceForSelf = []() { return std::make_shared(RoModuleType_ForSelf); }; +static const auto MakeRoServiceForOthers = []() { return std::make_shared(RoModuleType_ForOthers); }; int main(int argc, char **argv) { + /* Initialize. */ + Registration::Initialize(); + /* Static server manager. */ static auto s_server_manager = WaitableManager(1); /* Create services. */ s_server_manager.AddWaitable(new ServiceServer("ro:dmnt", 2)); - s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 32)); + /* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */ + s_server_manager.AddWaitable(new ServiceServer("ldr:ro", 2)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { s_server_manager.AddWaitable(new ServiceServer("ro:1", 2)); } diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index fe2726e76..03a8c7941 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -21,9 +21,30 @@ #include "ro_registration.hpp" -/* Declare process contexts as static to this function. */ +/* Declare process contexts as global array. */ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; +static bool g_is_development_hardware, g_is_development_function_enabled; + +void Registration::Initialize() { + if (R_FAILED(splInitialize())) { + std::abort(); + } + ON_SCOPE_EXIT { splExit(); }; + + if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { + std::abort(); + } + + { + u64 out_val = 0; + if (R_FAILED(splGetConfig(SplConfigItem_IsDebugMode, &out_val))) { + std::abort(); + } + g_is_development_function_enabled = out_val != 0; + } +} + Result Registration::RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id) { /* Check if a process context already exists. */ for (size_t i = 0; i < Registration::MaxSessions; i++) { @@ -56,6 +77,7 @@ void Registration::UnregisterProcess(RoProcessContext *context) { } } } + std::memset(context, 0, sizeof(*context)); } Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) { @@ -85,6 +107,83 @@ Result Registration::GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_ return ResultSuccess; } +Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type) { + /* Validate address/size. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + if (nrr_size == 0 || (nrr_size & 0xFFF) || !(nrr_address < nrr_address + nrr_size)) { + return ResultRoInvalidSize; + } + + /* Check we have space for a new NRR. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoTooManyNrr; + } + + NrrInfo *nrr_info = &context->nrr_infos[slot]; + + /* Map. */ + NrrHeader *header = nullptr; + u64 mapped_code_address = 0; + Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size); + if (R_FAILED(rc)) { + return rc; + } + + /* Set NRR info. */ + nrr_info->header = header; + nrr_info->nrr_heap_address = nrr_address; + nrr_info->nrr_heap_size = nrr_size; + nrr_info->mapped_code_address = mapped_code_address; + context->nrr_in_use[slot] = true; + + /* TODO. */ + return ResultSuccess; +} + +Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { + /* Validate address. */ + if (nrr_address & 0xFFF) { + return ResultRoInvalidAddress; + } + + /* Check the NRR is loaded. */ + size_t slot = 0; + for (slot = 0; slot < Registration::MaxNrrInfos; slot++) { + if (!context->nrr_in_use[slot]) { + continue; + } + + if (context->nrr_infos[slot].nrr_heap_address == nrr_address) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + return ResultRoNotRegistered; + } + + /* Unmap. */ + const NrrInfo nrr_info = context->nrr_infos[slot]; + { + /* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */ + context->nrr_in_use[slot] = false; + std::memset(&context->nrr_infos[slot], 0, sizeof(context->nrr_infos[slot])); + } + return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); +} + +Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) { Result rc = svcUnmapProcessMemory((void *)header, process_handle, mapped_code_address, nrr_heap_size); if (R_FAILED(rc)) { diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index b869d7e4d..bc1057c33 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -19,9 +19,15 @@ #include +enum RoModuleType : u32 { + RoModuleType_ForSelf = 0, + RoModuleType_ForOthers = 1, +}; + class Registration { public: - static constexpr size_t MaxSessions = 0x8; + /* NOTE: 2 ldr:ro, 2 ro:1. Nintendo only actually supports 2 total, but we'll be a little more generous. */ + static constexpr size_t MaxSessions = 0x4; static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; public: @@ -98,11 +104,17 @@ class Registration { u64 process_id; bool in_use; }; + private: + static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size); + static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); public: + static void Initialize(); + static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); static void UnregisterProcess(RoProcessContext *context); + + static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type); + static Result UnloadNrr(RoProcessContext *context, u64 nrr_address); static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id); - - static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); }; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index 8dc2bd504..d384fe282 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -28,6 +28,34 @@ RelocatableObjectsService::~RelocatableObjectsService() { } } +bool RelocatableObjectsService::IsProcessIdValid(u64 process_id) { + if (!this->IsInitialized()) { + return false; + } + + return this->context->process_id == process_id; +} + +u64 RelocatableObjectsService::GetTitleId(Handle process_handle) { + u64 title_id = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + /* 3.0.0+: Use svcGetInfo. */ + if (R_FAILED(svcGetInfo(&title_id, 18, process_handle, 0))) { + std::abort(); + } + } else { + /* 1.0.0-2.3.0: We're not inside loader, so ask pm. */ + u64 process_id = 0; + if (R_FAILED(svcGetProcessId(&process_id, process_handle))) { + std::abort(); + } + if (R_FAILED(pminfoGetTitleId(&title_id, process_id))) { + std::abort(); + } + } + return title_id; +} + Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { /* TODO */ return ResultKernelConnectionClosed; @@ -39,21 +67,35 @@ Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_addr } Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(this->context->process_handle), nrr_address, nrr_size, RoModuleType_ForSelf, true); } Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::UnloadNrr(this->context, nrr_address); } Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { - /* TODO */ - return ResultKernelConnectionClosed; + /* Validate the input pid/process handle. */ + u64 handle_pid = 0; + if (R_FAILED(svcGetProcessId(&handle_pid, process_h.handle)) || handle_pid != pid_desc.pid) { + return ResultRoInvalidProcess; + } + + return Registration::RegisterProcess(&this->context, process_h.handle, pid_desc.pid); } Result RelocatableObjectsService::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) { - /* TODO */ - return ResultKernelConnectionClosed; + if (!this->IsProcessIdValid(pid_desc.pid)) { + return ResultRoInvalidProcess; + } + + return Registration::LoadNrr(this->context, GetTitleId(process_h.handle), nrr_address, nrr_size, this->type, this->type == RoModuleType_ForOthers); } \ No newline at end of file diff --git a/stratosphere/ro/source/ro_service.hpp b/stratosphere/ro/source/ro_service.hpp index 2d68f84bd..6354d0805 100644 --- a/stratosphere/ro/source/ro_service.hpp +++ b/stratosphere/ro/source/ro_service.hpp @@ -30,16 +30,12 @@ enum RoServiceCmd { Ro_Cmd_LoadNrrEx = 10, }; -enum RoServiceType : u32 { - RoServiceType_ForSelf = 0, - RoServiceType_ForOthers = 1, -}; - class RelocatableObjectsService final : public IServiceObject { - Registration::RoProcessContext *context = nullptr; - RoServiceType type; + private: + Registration::RoProcessContext *context = nullptr; + RoModuleType type; public: - explicit RelocatableObjectsService(RoServiceType t) : type(t) { + explicit RelocatableObjectsService(RoModuleType t) : type(t) { /* ... */ } virtual ~RelocatableObjectsService() override; @@ -47,6 +43,8 @@ class RelocatableObjectsService final : public IServiceObject { bool IsInitialized() const { return this->context != nullptr; } + bool IsProcessIdValid(u64 process_id); + static u64 GetTitleId(Handle process_handle); private: /* Actual commands. */ Result LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); From 4ba6d8b24cb24e866dddd64e247f5042d6530955 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 05:28:57 -0700 Subject: [PATCH 07/13] ro: implement rest of LoadNrr/LoadNrrEx --- stratosphere/loader/source/ldr_map.cpp | 4 +- stratosphere/ro/source/ro_map.cpp | 257 +++++++++++++++++++++ stratosphere/ro/source/ro_map.hpp | 136 +++++++++++ stratosphere/ro/source/ro_nrr.cpp | 81 +++++++ stratosphere/ro/source/ro_nrr.hpp | 56 +++++ stratosphere/ro/source/ro_registration.cpp | 58 ++++- stratosphere/ro/source/ro_registration.hpp | 29 +-- 7 files changed, 589 insertions(+), 32 deletions(-) create mode 100644 stratosphere/ro/source/ro_map.cpp create mode 100644 stratosphere/ro/source/ro_map.hpp create mode 100644 stratosphere/ro/source/ro_nrr.cpp create mode 100644 stratosphere/ro/source/ro_nrr.hpp diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index c05157bca..3751fd592 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -70,7 +70,7 @@ Result MapUtils::LocateSpaceForMapModern(u64 *out, u64 out_size) { cur_base = address_space.map_end; } else { if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { - /* TODO: panic. */ + std::abort(); } if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { *out = cur_base; @@ -105,7 +105,7 @@ Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { rc = ResultKernelOutOfMemory; while (true) { if (mem_info.type == 0x10) { - return rc; + return rc; } if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { *out = cur_base; diff --git a/stratosphere/ro/source/ro_map.cpp b/stratosphere/ro/source/ro_map.cpp new file mode 100644 index 000000000..69046f9ae --- /dev/null +++ b/stratosphere/ro/source/ro_map.cpp @@ -0,0 +1,257 @@ +/* + * 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? */ + if (R_FAILED(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address - 1))) { + std::abort(); + } + if (mem_info.type == MemType_Unmapped && address - GuardRegionSize >= mem_info.addr) { + if (R_FAILED(svcQueryProcessMemory(&mem_info, &page_info, process_handle, address + size))) { + std::abort(); + } + 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, bool is_64_bit, u64 base_address, u64 size) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + return MapCodeMemoryForProcessModern(out_mcm, process_handle, base_address, size); + } else { + return MapCodeMemoryForProcessDeprecated(out_mcm, process_handle, is_64_bit, base_address, size); + } +} + +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; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, CUR_PROCESS_HANDLE)))) { + return rc; + } + + cur_base = address_space.addspace_base; + + rc = ResultKernelOutOfMemory; + cur_end = cur_base + out_size; + if (cur_end <= 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 { + if (R_FAILED(svcQueryMemory(&mem_info, &page_info, cur_base))) { + std::abort(); + } + 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 rc; + } + cur_base = mem_info.addr + mem_info.size; + if (cur_base >= address_space.addspace_end) { + return rc; + } + } + cur_end = cur_base + out_size; + if (cur_base + out_size <= cur_base) { + return rc; + } + } +} + + +Result MapUtils::LocateSpaceForMapDeprecated(u64 *out, u64 out_size) { + MemoryInfo mem_info = {}; + u32 page_info = 0; + Result rc; + + u64 cur_base = 0x8000000ULL; + if (R_FAILED((rc = svcQueryMemory(&mem_info, &page_info, cur_base)))) { + return rc; + } + + rc = ResultKernelOutOfMemory; + while (true) { + if (mem_info.type == 0x10) { + return rc; + } + if (mem_info.type == 0 && mem_info.addr - cur_base + mem_info.size >= out_size) { + *out = cur_base; + return ResultSuccess; + } + u64 mem_end = mem_info.addr + mem_info.size; + if (mem_end < cur_base) { + return rc; + } + if (mem_end >> 31) { + break; + } + cur_base = mem_end; + if (R_FAILED((rc = svcQueryMemory(&mem_info, &page_info, cur_base)))) { + return rc; + } + } + return rc; +} + +Result MapUtils::MapCodeMemoryForProcessModern(MappedCodeMemory &out_mcm, Handle process_handle, u64 base_address, u64 size) { + AddressSpaceInfo address_space = {}; + Result rc; + + if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, process_handle)))) { + return rc; + } + + 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); + rc = tmp_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + 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) { + Result rc; + 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); + rc = tmp_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + 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) { + Result rc; + if (R_FAILED((rc = svcGetInfo(&out->heap_base, 4, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->heap_size, 5, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_base, 2, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->map_size, 3, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_base, 12, process_h, 0)))) { + return rc; + } + if (R_FAILED((rc = svcGetInfo(&out->addspace_size, 13, process_h, 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 ResultSuccess; +} \ No newline at end of file diff --git a/stratosphere/ro/source/ro_map.hpp b/stratosphere/ro/source/ro_map.hpp new file mode 100644 index 000000000..bfffee190 --- /dev/null +++ b/stratosphere/ro/source/ro_map.hpp @@ -0,0 +1,136 @@ +/* + * 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)) { + if (R_FAILED((this->result = svcUnmapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size)))) { + std::abort(); + } + } + } + + 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)) { + if (R_FAILED((this->result = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size)))) { + std::abort(); + } + } + } + + 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, bool is_64_bit, 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 new file mode 100644 index 000000000..ee752dc9c --- /dev/null +++ b/stratosphere/ro/source/ro_nrr.cpp @@ -0,0 +1,81 @@ +/* + * 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. */ + Result rc = ValidateNrrSignature(header); + if (R_FAILED(rc)) { + if (!ease_nro_restriction) { + return rc; + } + } + + /* 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 new file mode 100644 index 000000000..08e2204d9 --- /dev/null +++ b/stratosphere/ro/source/ro_nrr.hpp @@ -0,0 +1,56 @@ +/* + * 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_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index 03a8c7941..3697c675a 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -20,6 +20,8 @@ #include #include "ro_registration.hpp" +#include "ro_map.hpp" +#include "ro_nrr.hpp" /* Declare process contexts as global array. */ static Registration::RoProcessContext g_process_contexts[Registration::MaxSessions] = {}; @@ -45,6 +47,18 @@ void Registration::Initialize() { } } +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++) { @@ -132,7 +146,7 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad /* Map. */ NrrHeader *header = nullptr; u64 mapped_code_address = 0; - Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size); + Result rc = MapAndValidateNrr(&header, &mapped_code_address, context->process_handle, title_id, nrr_address, nrr_size, expected_type, enforce_type); if (R_FAILED(rc)) { return rc; } @@ -179,9 +193,45 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address); } -Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size) { - /* TODO */ - return ResultKernelConnectionClosed; +Result Registration::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) { + Result rc; + MappedCodeMemory nrr_mcm; + + /* First, map the NRR. */ + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, true, nrr_heap_address, nrr_heap_size)))) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + /* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */ + rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, false, nrr_heap_address, nrr_heap_size); + } + if (R_FAILED(rc)) { + return rc; + } + } + + 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); + if (R_FAILED((rc = NrrUtils::ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type)))) { + return rc; + } + + /* 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) { diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index bc1057c33..30f0910a3 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -19,10 +19,7 @@ #include -enum RoModuleType : u32 { - RoModuleType_ForSelf = 0, - RoModuleType_ForOthers = 1, -}; +#include "ro_nrr.hpp" class Registration { public: @@ -31,27 +28,6 @@ class Registration { static constexpr size_t MaxNrrInfos = 0x40; static constexpr size_t MaxNroInfos = 0x40; public: - struct NrrHeader { - u32 magic; - u32 _0x4; - u32 _0x8; - u32 _0xC; - u64 title_id_mask; - u64 title_id_pattern; - u64 _0x20; - u64 _0x28; - u8 modulus[0x100]; - u8 fixed_key_signature[0x100]; - u8 nrr_signature[0x100]; - u64 title_id_min; - u32 nrr_size; - u8 nrr_type; /* 7.0.0+ */ - u8 _0x33D[3]; - u32 hash_offset; - u32 num_hashes; - u64 _0x348; - }; - static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!"); struct NroHeader { u32 entrypoint_insn; @@ -105,10 +81,11 @@ class Registration { bool in_use; }; private: - static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size); + static Result 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); public: static void Initialize(); + static bool ShouldEaseNroRestriction(); static Result RegisterProcess(RoProcessContext **out_context, Handle process_handle, u64 process_id); static void UnregisterProcess(RoProcessContext *context); From 13ded6bd1c9d69703d53ff4faef1c1cd9c2389d2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 07:28:07 -0700 Subject: [PATCH 08/13] ro: implement loadnro/unloadnro --- stratosphere/libstratosphere | 2 +- stratosphere/ro/source/ro_registration.cpp | 310 +++++++++++++++++++-- stratosphere/ro/source/ro_registration.hpp | 28 ++ stratosphere/ro/source/ro_service.cpp | 15 +- 4 files changed, 330 insertions(+), 25 deletions(-) diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 880bce909..4d6bf21f9 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 880bce9092fbef0bcbf101b8ec2e3d2c5af3fb98 +Subproject commit 4d6bf21f9ce6255086e649073b5e2fa7c26d1d27 diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index 3697c675a..e7ea8543d 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include #include #include @@ -33,11 +33,11 @@ void Registration::Initialize() { std::abort(); } ON_SCOPE_EXIT { splExit(); }; - + if (R_FAILED(splIsDevelopment(&g_is_development_hardware))) { std::abort(); } - + { u64 out_val = 0; if (R_FAILED(splGetConfig(SplConfigItem_IsDebugMode, &out_val))) { @@ -49,11 +49,11 @@ void Registration::Initialize() { 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; @@ -66,10 +66,11 @@ Result Registration::RegisterProcess(RoProcessContext **out_context, Handle proc 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; @@ -77,7 +78,7 @@ Result Registration::RegisterProcess(RoProcessContext **out_context, Handle proc return ResultSuccess; } } - + /* Failure to find a free context is actually an abort condition. */ /* TODO: Should this return an unofficial error code? */ std::abort(); @@ -90,6 +91,7 @@ void Registration::UnregisterProcess(RoProcessContext *context) { 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)); } @@ -129,7 +131,7 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad 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++) { @@ -140,9 +142,9 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad if (slot == Registration::MaxNrrInfos) { return ResultRoTooManyNrr; } - + NrrInfo *nrr_info = &context->nrr_infos[slot]; - + /* Map. */ NrrHeader *header = nullptr; u64 mapped_code_address = 0; @@ -150,14 +152,14 @@ Result Registration::LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_ad if (R_FAILED(rc)) { return rc; } - + /* Set NRR info. */ nrr_info->header = header; nrr_info->nrr_heap_address = nrr_address; nrr_info->nrr_heap_size = nrr_size; nrr_info->mapped_code_address = mapped_code_address; context->nrr_in_use[slot] = true; - + /* TODO. */ return ResultSuccess; } @@ -174,7 +176,7 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { if (!context->nrr_in_use[slot]) { continue; } - + if (context->nrr_infos[slot].nrr_heap_address == nrr_address) { break; } @@ -193,10 +195,195 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) { 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. */ + Result rc = MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size); + if (R_FAILED(rc)) { + return rc; + } + + /* Validate the NRO (parsing region extents). */ + u64 rx_size, ro_size, rw_size; + if (R_FAILED((rc = 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); + return rc; + } + + /* Set NRO perms. */ + if (R_FAILED((rc = 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); + return rc; + } + + 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; + } + } + } + + *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) { + Result rc; + const u64 rx_offset = 0; + const u64 ro_offset = rx_offset + rx_size; + const u64 rw_offset = ro_offset + ro_size; + + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, 5)))) { + return rc; + } + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, 1)))) { + return rc; + } + if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, 3)))) { + return rc; + } + + 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].nro_heap_address == nro_address) { + break; + } + } + if (slot == Registration::MaxNrrInfos) { + 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) { Result rc; MappedCodeMemory nrr_mcm; - + /* First, map the NRR. */ if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(nrr_mcm, process_handle, true, nrr_heap_address, nrr_heap_size)))) { if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { @@ -207,28 +394,28 @@ Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_c return rc; } } - + 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); if (R_FAILED((rc = NrrUtils::ValidateNrr(nrr_header, nrr_heap_size, title_id, expected_type, enforce_type)))) { return rc; } - + /* 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; @@ -239,6 +426,89 @@ Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u6 if (R_FAILED(rc)) { return rc; } - + 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) { + Result rc; + 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; + bool is_64_bit = true; + if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size)))) { + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + /* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */ + is_64_bit = false; + rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size); + } + if (R_FAILED(rc)) { + return rc; + } + } + 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); + rc = tmp_bss_mcm.GetResult(); + if (rc == ResultKernelInvalidMemoryState) { + continue; + } + if (R_FAILED(rc)) { + return rc; + } + + 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) { + Result rc; + + /* First, unmap bss. */ + if (bss_heap_size > 0) { + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size)))) { + return rc; + } + } + + /* Next, unmap .rwdata */ + if (rw_size > 0) { + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size)))) { + return rc; + } + } + + /* Finally, unmap .text + .rodata. */ + if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size)))) { + return rc; + } + + return ResultSuccess; +} diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index 30f0910a3..ca34791ee 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -27,6 +27,8 @@ class Registration { 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 { @@ -54,6 +56,24 @@ class Registration { 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!"); struct NroInfo { u64 base_address; @@ -83,6 +103,12 @@ class Registration { 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(); @@ -92,6 +118,8 @@ class Registration { 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 d384fe282..0b0a9e7c6 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -25,6 +25,7 @@ RelocatableObjectsService::~RelocatableObjectsService() { if (this->IsInitialized()) { Registration::UnregisterProcess(this->context); + this->context = nullptr; } } @@ -57,13 +58,19 @@ u64 RelocatableObjectsService::GetTitleId(Handle process_handle) { } Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - /* TODO */ - return ResultKernelConnectionClosed; + 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) { - /* TODO */ - return ResultKernelConnectionClosed; + 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) { From 253afc90a42893c2662e04ce07d92e94b40b5159 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 21 Apr 2019 09:08:08 -0700 Subject: [PATCH 09/13] loader: remove ldr:ro (moved into ro sysmodule) --- stratosphere/loader/source/ldr_main.cpp | 17 +- stratosphere/loader/source/ldr_nro.cpp | 143 ----------------- stratosphere/loader/source/ldr_nro.hpp | 72 --------- .../loader/source/ldr_process_manager.cpp | 4 +- .../loader/source/ldr_registration.cpp | 144 ----------------- .../loader/source/ldr_registration.hpp | 29 +--- stratosphere/loader/source/ldr_ro_service.cpp | 148 ------------------ stratosphere/loader/source/ldr_ro_service.hpp | 60 ------- 8 files changed, 7 insertions(+), 610 deletions(-) delete mode 100644 stratosphere/loader/source/ldr_nro.cpp delete mode 100644 stratosphere/loader/source/ldr_nro.hpp delete mode 100644 stratosphere/loader/source/ldr_ro_service.cpp delete mode 100644 stratosphere/loader/source/ldr_ro_service.hpp diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index a278f6295..4fd24c72b 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -26,7 +26,6 @@ #include "ldr_process_manager.hpp" #include "ldr_debug_monitor.hpp" #include "ldr_shell.hpp" -#include "ldr_ro_service.hpp" extern "C" { extern u32 __start__; @@ -114,21 +113,15 @@ int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); - auto server_manager = new WaitableManager(1); + static auto s_server_manager = WaitableManager(1); /* Add services to manager. */ - server_manager->AddWaitable(new ServiceServer("ldr:pm", 1)); - server_manager->AddWaitable(new ServiceServer("ldr:shel", 3)); - server_manager->AddWaitable(new ServiceServer("ldr:dmnt", 2)); - if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { - /* On 1.0.0-2.3.0, Loader services ldr:ro instead of ro. */ - server_manager->AddWaitable(new ServiceServer("ldr:ro", 0x20)); - } + s_server_manager.AddWaitable(new ServiceServer("ldr:pm", 1)); + s_server_manager.AddWaitable(new ServiceServer("ldr:shel", 3)); + s_server_manager.AddWaitable(new ServiceServer("ldr:dmnt", 2)); /* Loop forever, servicing our services. */ - server_manager->Process(); - - delete server_manager; + s_server_manager.Process(); return 0; } diff --git a/stratosphere/loader/source/ldr_nro.cpp b/stratosphere/loader/source/ldr_nro.cpp deleted file mode 100644 index a28da564a..000000000 --- a/stratosphere/loader/source/ldr_nro.cpp +++ /dev/null @@ -1,143 +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 "ldr_nro.hpp" -#include "ldr_registration.hpp" -#include "ldr_map.hpp" - -Result NroUtils::ValidateNrrHeader(NrrHeader *header, u64 size, u64 title_id_min) { - if (header->magic != MAGIC_NRR0) { - return ResultLoaderInvalidNrr; - } - if (header->nrr_size != size) { - return ResultLoaderInvalidSize; - } - - /* TODO: Check NRR signature. */ - if (false) { - return ResultLoaderInvalidSignature; - } - - if (header->title_id_min != title_id_min) { - return ResultLoaderInvalidNrr; - } - - return ResultSuccess; -} - -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_hdr = {}; - MappedCodeMemory mcm_nro = {}; - MappedCodeMemory mcm_bss = {}; - unsigned int i; - Result rc = ResultSuccess; - u8 nro_hash[0x20]; - - /* Perform cleanup on failure. */ - ON_SCOPE_EXIT { - if (R_FAILED(rc)) { - mcm_nro.Close(); - mcm_bss.Close(); - } - }; - - /* Ensure there is an available NRO slot. */ - if (std::all_of(target_proc->nro_infos.begin(), target_proc->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use))) { - rc = ResultLoaderInsufficientNroRegistrations; - return rc; - } - 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, mcm_nro.code_memory_address + nro_heap_size))) { - break; - } else { - mcm_nro.Close(); - } - } - } - if (i >= 0x200) { - rc = ResultLoaderInsufficientAddressSpace; - return rc; - } - - /* Map the NRO. */ - if (R_FAILED((rc = mcm_nro.Map()))) { - return rc; - } - - /* Read data from NRO while it's mapped. */ - { - nro_hdr = *((NroHeader *)mcm_nro.mapped_address); - - if (nro_hdr.magic != MAGIC_NRO0) { - rc = ResultLoaderInvalidNro; - return rc; - } - if (nro_hdr.nro_size != nro_heap_size || nro_hdr.bss_size != bss_heap_size) { - rc = ResultLoaderInvalidNro; - return rc; - } - if ((nro_hdr.text_size & 0xFFF) || (nro_hdr.ro_size & 0xFFF) || (nro_hdr.rw_size & 0xFFF) || (nro_hdr.bss_size & 0xFFF)) { - rc = ResultLoaderInvalidNro; - return rc; - } - if (nro_hdr.text_offset != 0 || nro_hdr.text_offset + nro_hdr.text_size != nro_hdr.ro_offset || nro_hdr.ro_offset + nro_hdr.ro_size != nro_hdr.rw_offset || nro_hdr.rw_offset + nro_hdr.rw_size != nro_hdr.nro_size) { - rc = ResultLoaderInvalidNro; - return rc; - } - - sha256CalculateHash(nro_hash, mcm_nro.mapped_address, nro_hdr.nro_size); - } - - /* Unmap the NRO. */ - if (R_FAILED((rc = mcm_nro.Unmap()))) { - return rc; - } - - if (!Registration::IsNroHashPresent(target_proc->index, nro_hash)) { - rc = ResultLoaderInvalidSignature; - return rc; - } - - if (Registration::IsNroAlreadyLoaded(target_proc->index, nro_hash)) { - rc = ResultLoaderNroAlreadyLoaded; - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address, nro_hdr.text_size, 5)))) { - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.ro_offset, nro_hdr.ro_size, 1)))) { - return rc; - } - - if (R_FAILED((rc = svcSetProcessMemoryPermission(process_h, mcm_nro.code_memory_address + nro_hdr.rw_offset, nro_hdr.rw_size + nro_hdr.bss_size, 3)))) { - return rc; - } - - Registration::AddNroToProcess(target_proc->index, &mcm_nro, &mcm_bss, nro_hdr.text_size, nro_hdr.ro_size, nro_hdr.rw_size, nro_hdr.build_id); - *out_address = mcm_nro.code_memory_address; - rc = ResultSuccess; - - return rc; -} diff --git a/stratosphere/loader/source/ldr_nro.hpp b/stratosphere/loader/source/ldr_nro.hpp deleted file mode 100644 index a2886d141..000000000 --- a/stratosphere/loader/source/ldr_nro.hpp +++ /dev/null @@ -1,72 +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 "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_process_manager.cpp b/stratosphere/loader/source/ldr_process_manager.cpp index ddef59a8c..c6a6d11cf 100644 --- a/stratosphere/loader/source/ldr_process_manager.cpp +++ b/stratosphere/loader/source/ldr_process_manager.cpp @@ -27,9 +27,7 @@ Result ProcessManagerService::CreateProcess(Out proc_h, u64 index, Registration::TidSid tid_sid; LaunchQueue::LaunchItem *launch_item; char nca_path[FS_MAX_PATH] = {0}; - - fprintf(stderr, "CreateProcess(%016lx, %08x, %08x);\n", index, flags, reslimit_h.handle); - + ON_SCOPE_EXIT { /* Loader doesn't persist the copied resource limit handle. */ svcCloseHandle(reslimit_h.handle); diff --git a/stratosphere/loader/source/ldr_registration.cpp b/stratosphere/loader/source/ldr_registration.cpp index 3d01cad43..44c075255 100644 --- a/stratosphere/loader/source/ldr_registration.cpp +++ b/stratosphere/loader/source/ldr_registration.cpp @@ -20,7 +20,6 @@ #include #include #include "ldr_registration.hpp" -#include "ldr_nro.hpp" static Registration::List g_registration_list = {}; static u64 g_num_registered = 1; @@ -51,15 +50,6 @@ Registration::Process *Registration::GetProcessByProcessId(u64 pid) { return NULL; } -Registration::Process *Registration::GetProcessByRoService(void *service) { - for (unsigned int i = 0; i < Registration::MaxProcesses; 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) { @@ -124,140 +114,6 @@ void Registration::AddModuleInfo(u64 index, u64 base_address, u64 size, const un } } -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 < Registration::MaxNrrInfos; 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::AddNrrInfo(u64 index, MappedCodeMemory *nrr_info) { - Registration::Process *target_process = GetProcess(index); - if (target_process == NULL) { - /* TODO: std::abort(); */ - return ResultLoaderProcessNotRegistered; - } - - auto nrr_info_it = std::find_if_not(target_process->nrr_infos.begin(), target_process->nrr_infos.end(), std::mem_fn(&MappedCodeMemory::IsActive)); - if (nrr_info_it == target_process->nrr_infos.end()) { - return ResultLoaderInsufficientNrrRegistrations; - } - *nrr_info_it = *nrr_info; - return ResultSuccess; -} - -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 ResultLoaderProcessNotRegistered; - } - - for (unsigned int i = 0; i < Registration::MaxNrrInfos; i++) { - if (target_process->nrr_infos[i].IsActive() && target_process->nrr_infos[i].base_address == base_address) { - target_process->nrr_infos[i].Close(); - return ResultSuccess; - } - } - return ResultLoaderNotRegistered; -} - - -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 < Registration::MaxNrrInfos; 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 < Registration::MaxNroInfos; i++) { - if (target_process->nro_infos[i].in_use && std::equal(build_id, build_id + 0x20, target_process->nro_infos[i].build_id)) { - 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; - } - - auto nro_info_it = std::find_if_not(target_process->nro_infos.begin(), target_process->nro_infos.end(), std::mem_fn(&Registration::NroInfo::in_use)); - if (nro_info_it != target_process->nro_infos.end()) { - nro_info_it->base_address = nro->code_memory_address; - nro_info_it->nro_heap_address = nro->base_address; - nro_info_it->nro_heap_size = nro->size; - nro_info_it->bss_heap_address = bss->base_address; - nro_info_it->bss_heap_size = bss->size; - nro_info_it->text_size = text_size; - nro_info_it->ro_size = ro_size; - nro_info_it->rw_size = rw_size; - std::copy(build_id, build_id + sizeof(nro_info_it->build_id), nro_info_it->build_id); - nro_info_it->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 ResultLoaderProcessNotRegistered; - } - - for (unsigned int i = 0; i < Registration::MaxNroInfos; 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] = {}; - return rc; - } - } - return ResultLoaderNotLoaded; -} - Result Registration::GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written) { Registration::Process *target_process = GetProcessByProcessId(process_id); if (target_process == NULL) { diff --git a/stratosphere/loader/source/ldr_registration.hpp b/stratosphere/loader/source/ldr_registration.hpp index 27f458ee7..6588a8ae1 100644 --- a/stratosphere/loader/source/ldr_registration.hpp +++ b/stratosphere/loader/source/ldr_registration.hpp @@ -24,28 +24,12 @@ class Registration { public: static constexpr size_t MaxProcesses = 0x40; static constexpr size_t MaxModuleInfos = 0x20; - static constexpr size_t MaxNrrInfos = 0x40; - static constexpr size_t MaxNroInfos = 0x40; public: struct ModuleInfoHolder { bool in_use; LoaderModuleInfo 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; @@ -59,9 +43,6 @@ class Registration { u64 title_id; Registration::TidSid tid_sid; std::array module_infos; - std::array nro_infos; - std::array nrr_infos; - void *owner_ro_service; }; struct List { @@ -72,19 +53,11 @@ class Registration { static Registration::Process *GetFreeProcess(); static Registration::Process *GetProcess(u64 index); static Registration::Process *GetProcessByProcessId(u64 pid); - static Registration::Process *GetProcessByRoService(void *service); static Result GetRegisteredTidSid(u64 index, Registration::TidSid *out); static bool RegisterTidSid(const TidSid *tid_sid, u64 *out_index); static bool UnregisterIndex(u64 index); static void SetProcessIdTidAndIs64BitAddressSpace(u64 index, u64 process_id, u64 tid, bool is_64_bit_addspace); static void AddModuleInfo(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 GetProcessModuleInfo(LoaderModuleInfo *out, u32 max_out, u64 process_id, u32 *num_written); /* Atmosphere MitM Extension. */ diff --git a/stratosphere/loader/source/ldr_ro_service.cpp b/stratosphere/loader/source/ldr_ro_service.cpp deleted file mode 100644 index 5348a2edd..000000000 --- a/stratosphere/loader/source/ldr_ro_service.cpp +++ /dev/null @@ -1,148 +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 "ldr_ro_service.hpp" -#include "ldr_registration.hpp" -#include "ldr_map.hpp" -#include "ldr_nro.hpp" - -Result RelocatableObjectsService::LoadNro(Out load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nro_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - if (nro_address + nro_size <= nro_address || !nro_size || (nro_size & 0xFFF)) { - return ResultLoaderInvalidSize; - } - if (bss_size && bss_address + bss_size <= bss_address) { - return ResultLoaderInvalidSize; - } - /* Ensure no overflow for combined sizes. */ - if (U64_MAX - nro_size < bss_size) { - return ResultLoaderInvalidSize; - } - 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 ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return NroUtils::LoadNro(target_proc, this->process_handle, nro_address, nro_size, bss_address, bss_size, load_address.GetPointer()); -} - -Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nro_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - - 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 ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return Registration::RemoveNroInfo(target_proc->index, this->process_handle, nro_address); -} - -Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) { - Result rc = ResultSuccess; - Registration::Process *target_proc = NULL; - MappedCodeMemory nrr_info = {}; - ON_SCOPE_EXIT { - if (R_FAILED(rc) && nrr_info.IsActive()) { - nrr_info.Close(); - } - }; - - if (!this->has_initialized || this->process_id != pid_desc.pid) { - rc = ResultLoaderInvalidProcess; - return rc; - } - if (nrr_address & 0xFFF) { - rc = ResultLoaderInvalidAddress; - return rc; - } - if (nrr_address + nrr_size <= nrr_address || !nrr_size || (nrr_size & 0xFFF)) { - rc = ResultLoaderInvalidSize; - return rc; - } - - 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 = ResultLoaderInvalidSession; - return rc; - } - 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)))) { - return rc; - } - - if (R_FAILED((rc = nrr_info.Map()))) { - return rc; - } - - rc = NroUtils::ValidateNrrHeader((NroUtils::NrrHeader *)nrr_info.mapped_address, nrr_size, target_proc->title_id); - if (R_SUCCEEDED(rc)) { - Registration::AddNrrInfo(target_proc->index, &nrr_info); - } - - return rc; -} - -Result RelocatableObjectsService::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) { - Registration::Process *target_proc = NULL; - if (!this->has_initialized || this->process_id != pid_desc.pid) { - return ResultLoaderInvalidProcess; - } - if (nrr_address & 0xFFF) { - return ResultLoaderInvalidAddress; - } - - 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 ResultLoaderInvalidSession; - } - target_proc->owner_ro_service = this; - - return Registration::RemoveNrrInfo(target_proc->index, nrr_address); -} - -Result RelocatableObjectsService::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) { - u64 handle_pid; - 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; - return ResultSuccess; - } - return ResultLoaderInvalidProcess; -} diff --git a/stratosphere/loader/source/ldr_ro_service.hpp b/stratosphere/loader/source/ldr_ro_service.hpp deleted file mode 100644 index f5cc4b62d..000000000 --- a/stratosphere/loader/source/ldr_ro_service.hpp +++ /dev/null @@ -1,60 +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 "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, - Ro_Cmd_LoadNrrEx = 10, -}; - -class RelocatableObjectsService final : public IServiceObject { - Handle process_handle = 0; - u64 process_id = U64_MAX; - bool has_initialized = false; - public: - virtual ~RelocatableObjectsService() override { - Registration::CloseRoService(this, this->process_handle); - if (this->has_initialized) { - svcCloseHandle(this->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(), - }; -}; From cb74bc6bb8b8fdbc3f08c6ad717181c98c9cacd7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 00:31:17 -0700 Subject: [PATCH 10/13] ro: fix UnloadNro logic error --- stratosphere/ro/source/ro_registration.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index e7ea8543d..a179a8cff 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -362,11 +362,11 @@ Result Registration::UnloadNro(RoProcessContext *context, u64 nro_address) { continue; } - if (context->nro_infos[slot].nro_heap_address == nro_address) { + if (context->nro_infos[slot].base_address == nro_address) { break; } } - if (slot == Registration::MaxNrrInfos) { + if (slot == Registration::MaxNroInfos) { return ResultRoNotLoaded; } From bfd04cfe921101ff8f7564f62d01a94d1236f714 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 00:40:19 -0700 Subject: [PATCH 11/13] loader: remove more unused ro functionality --- stratosphere/loader/source/ldr_map.cpp | 73 -------------------- stratosphere/loader/source/ldr_map.hpp | 94 -------------------------- 2 files changed, 167 deletions(-) diff --git a/stratosphere/loader/source/ldr_map.cpp b/stratosphere/loader/source/ldr_map.cpp index 3751fd592..468a57a0b 100644 --- a/stratosphere/loader/source/ldr_map.cpp +++ b/stratosphere/loader/source/ldr_map.cpp @@ -27,15 +27,6 @@ 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 = {}; AddressSpaceInfo address_space = {}; @@ -126,70 +117,6 @@ 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 = {}; - Result rc; - - if (R_FAILED((rc = GetAddressSpaceInfo(&address_space, process_h)))) { - return rc; - } - - if (size > address_space.addspace_size) { - return ResultLoaderInsufficientAddressSpace; - } - - u64 try_address; - for (unsigned int i = 0; i < 0x200; 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; - } - rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); - if (rc != ResultKernelInvalidMemoryState) { - 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 ResultLoaderInsufficientAddressSpace; - } - - u64 try_address; - for (unsigned int i = 0; i < 0x200; i++) { - try_address = addspace_base + (StratosphereRandomUtils::GetRandomU64((u64)(addspace_size - size) >> 12) << 12); - rc = svcMapProcessCodeMemory(process_h, try_address, base_address, size); - if (rc != ResultKernelInvalidMemoryState) { - 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, process_h, 0)))) { diff --git a/stratosphere/loader/source/ldr_map.hpp b/stratosphere/loader/source/ldr_map.hpp index 134d89a2f..c9a83b608 100644 --- a/stratosphere/loader/source/ldr_map.hpp +++ b/stratosphere/loader/source/ldr_map.hpp @@ -35,11 +35,6 @@ class MapUtils { 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 { @@ -84,92 +79,3 @@ class AutoCloseMap { } } }; - -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 ResultLoaderInternalError; - } - - 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 ResultLoaderInternalError; - } - 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 ResultLoaderInternalError; - } - if (R_FAILED(rc = MapUtils::LocateSpaceForMap(&try_address, size))) { - return rc; - } - - if (R_FAILED((rc = svcMapProcessMemory((void *)try_address, this->process_handle, this->code_memory_address, size)))) { - return rc; - } - - this->mapped_address = (void *)try_address; - return rc; - } - - Result Unmap() { - Result rc = ResultSuccess; - if (this->IsMapped()) { - if (R_FAILED((rc = svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->code_memory_address, this->size)))) { - /* TODO: panic(). */ - } - } - this->mapped_address = NULL; - return rc; - } - - 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 = {}; - } -}; From f50bfaf7d74b643defc27dbff4e82581cf0e0ddb Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 01:54:44 -0700 Subject: [PATCH 12/13] dist: add boot2.flag for ro (to launch on <3.0.0) --- Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 384f76d63..a380a5d94 100644 --- a/Makefile +++ b/Makefile @@ -50,13 +50,13 @@ dist: all mkdir atmosphere-$(AMSVER)/atmosphere mkdir atmosphere-$(AMSVER)/sept mkdir atmosphere-$(AMSVER)/switch - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036 + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037 mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin - mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin @@ -67,14 +67,16 @@ dist: all cp common/defaults/system_settings.ini atmosphere-$(AMSVER)/atmosphere/system_settings.ini cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches cp -r common/defaults/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html - cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp - cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp + cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp + cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp + cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp - cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag - cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags + touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags/boot2.flag + cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; rm -r atmosphere-$(AMSVER) mkdir out From a1d4caa7b4e31896bb4b2db510a7cfbac0083d74 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 22 Apr 2019 04:32:15 -0700 Subject: [PATCH 13/13] ro: add support for applying ips patches to NROs. --- stratosphere/ro/source/ro_patcher.cpp | 188 +++++++++++++++++++++ stratosphere/ro/source/ro_patcher.hpp | 26 +++ stratosphere/ro/source/ro_registration.cpp | 4 + stratosphere/ro/source/ro_registration.hpp | 35 +--- stratosphere/ro/source/ro_types.hpp | 42 +++++ 5 files changed, 266 insertions(+), 29 deletions(-) create mode 100644 stratosphere/ro/source/ro_patcher.cpp create mode 100644 stratosphere/ro/source/ro_patcher.hpp create mode 100644 stratosphere/ro/source/ro_types.hpp diff --git a/stratosphere/ro/source/ro_patcher.cpp b/stratosphere/ro/source/ro_patcher.cpp new file mode 100644 index 000000000..355a7d2ce --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.cpp @@ -0,0 +1,188 @@ +/* + * 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: + patch_offset -= sizeof(Registration::NroHeader); + 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: + patch_offset -= sizeof(Registration::NroHeader); + 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_patcher.hpp b/stratosphere/ro/source/ro_patcher.hpp new file mode 100644 index 000000000..ed88c7223 --- /dev/null +++ b/stratosphere/ro/source/ro_patcher.hpp @@ -0,0 +1,26 @@ +/* + * 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_types.hpp" + +class PatchUtils { + public: + static void ApplyPatches(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size); +}; \ No newline at end of file diff --git a/stratosphere/ro/source/ro_registration.cpp b/stratosphere/ro/source/ro_registration.cpp index a179a8cff..49c090030 100644 --- a/stratosphere/ro/source/ro_registration.cpp +++ b/stratosphere/ro/source/ro_registration.cpp @@ -22,6 +22,7 @@ #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] = {}; @@ -323,6 +324,9 @@ Result Registration::ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 } } + /* 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; diff --git a/stratosphere/ro/source/ro_registration.hpp b/stratosphere/ro/source/ro_registration.hpp index ca34791ee..f416534f1 100644 --- a/stratosphere/ro/source/ro_registration.hpp +++ b/stratosphere/ro/source/ro_registration.hpp @@ -13,24 +13,24 @@ * 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 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; @@ -52,29 +52,6 @@ class Registration { }; static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!"); - 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!"); - struct NroInfo { u64 base_address; u64 nro_heap_address; @@ -104,7 +81,7 @@ class Registration { 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); @@ -115,7 +92,7 @@ class Registration { 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); diff --git a/stratosphere/ro/source/ro_types.hpp b/stratosphere/ro/source/ro_types.hpp new file mode 100644 index 000000000..b6473adb1 --- /dev/null +++ b/stratosphere/ro/source/ro_types.hpp @@ -0,0 +1,42 @@ +/* + * 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!");