diff --git a/Makefile b/Makefile index af0c8ac..cdc0d4d 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches 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/set_mitm/set_mitm.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp + cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/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/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index 8a964e2..ed51bff 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -83,7 +83,7 @@ export OUTPUT := $(CURDIR)/$(TARGET) export TOPDIR := $(CURDIR) -export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/fs_mitm +export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/ams_mitm export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ $(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \ @@ -94,7 +94,7 @@ CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -KIPFILES := loader.kip pm.kip sm.kip fs_mitm.kip boot_100.kip boot_200.kip +KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip boot_100.kip boot_200.kip BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp $(KIPFILES) #--------------------------------------------------------------------------------- diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index cd00da4..54561b4 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -556,7 +556,7 @@ }, { /* 7.0.0. */ .hash = {0xA2, 0x5E, 0x47, 0x0C, 0x8E, 0x6D, 0x2F, 0xD7, 0x5D, 0xAD, 0x24, 0xD7, 0xD8, 0x24, 0x34, 0xFB, 0xCD, 0x77, 0xBB, 0xE6, 0x66, 0x03, 0xCB, 0xAF, 0xAB, 0x85, 0x45, 0xA0, 0x91, 0xAF, 0x34, 0x25}, - .free_code_space_offset = 0x5FE80, + .free_code_space_offset = 0x5FEC0, KERNEL_HOOKS(700) } }; diff --git a/fusee/fusee-secondary/src/stratosphere.c b/fusee/fusee-secondary/src/stratosphere.c index 9deaa87..53d402b 100644 --- a/fusee/fusee-secondary/src/stratosphere.c +++ b/fusee/fusee-secondary/src/stratosphere.c @@ -32,6 +32,7 @@ #include "loader_kip.h" #include "pm_kip.h" #include "sm_kip.h" +#include "ams_mitm_kip.h" #include "boot_100_kip.h" #include "boot_200_kip.h" #undef u8 @@ -43,13 +44,13 @@ static bool g_stratosphere_loader_enabled = true; static bool g_stratosphere_sm_enabled = true; static bool g_stratosphere_pm_enabled = true; -static bool g_stratosphere_fs_mitm_enabled = true; +static bool g_stratosphere_ams_mitm_enabled = true; static bool g_stratosphere_boot_enabled = false; extern const uint8_t boot_100_kip[], boot_200_kip[]; -extern const uint8_t loader_kip[], pm_kip[], sm_kip[], fs_mitm_kip[]; +extern const uint8_t loader_kip[], pm_kip[], sm_kip[], ams_mitm_kip[]; extern const uint32_t boot_100_kip_size, boot_200_kip_size; -extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, fs_mitm_kip_size; +extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, ams_mitm_kip_size; /* GCC doesn't consider the size as const... we have to write it ourselves. */ @@ -89,8 +90,8 @@ num_processes++; } - if (g_stratosphere_fs_mitm_enabled) { - size += fs_mitm_kip_size; + if (g_stratosphere_ams_mitm_enabled) { + size += ams_mitm_kip_size; num_processes++; } @@ -128,9 +129,9 @@ data += sm_kip_size; } - if (g_stratosphere_fs_mitm_enabled) { - memcpy(data, fs_mitm_kip, fs_mitm_kip_size); - data += fs_mitm_kip_size; + if (g_stratosphere_ams_mitm_enabled) { + memcpy(data, ams_mitm_kip, ams_mitm_kip_size); + data += ams_mitm_kip_size; } if (g_stratosphere_boot_enabled) { diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 5369af9..2e718b3 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot fs_mitm set_mitm creport fatal +MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal SUBFOLDERS := libstratosphere $(MODULES) diff --git a/stratosphere/ams_mitm/Makefile b/stratosphere/ams_mitm/Makefile new file mode 100644 index 0000000..404225b --- /dev/null +++ b/stratosphere/ams_mitm/Makefile @@ -0,0 +1,160 @@ +#--------------------------------------------------------------------------------- +.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 source/fs_mitm source/set_mitm +DATA := data +INCLUDES := include ../../common/include +EXEFS_SRC := exefs_src + +DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" + +#--------------------------------------------------------------------------------- +# 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).kip $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).kip + +$(OUTPUT).kip : $(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/ams_mitm/ams_mitm.json b/stratosphere/ams_mitm/ams_mitm.json new file mode 100644 index 0000000..e2efc65 --- /dev/null +++ b/stratosphere/ams_mitm/ams_mitm.json @@ -0,0 +1,78 @@ +{ + "name": "ams.mitm", + "title_id": "0x010041544D530000", + "main_thread_stack_size": "0x20000", + "main_thread_priority": 43, + "default_cpu_id": 3, + "process_category": 1, + "kernel_capabilities": [ + { + "type": "handle_table_size", + "value": 512 + }, + { + "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", + "svcCreateInterruptEvent": "0x53", + "svcReadWriteRegister": "0x4E", + "svcQueryIoMapping": "0x55", + "svcCreateDeviceAddressSpace": "0x56", + "svcAttachDeviceAddressSpace": "0x57", + "svcDetachDeviceAddressSpace": "0x58", + "svcMapDeviceAddressSpaceAligned": "0x5a", + "svcUnmapDeviceAddressSpace": "0x5c", + "svcGetSystemInfo": "0x6f", + "svcCallSecureMonitor": "0x7F" + } + } + ] +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp new file mode 100644 index 0000000..8f1a5f8 --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 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 "amsmitm_modules.hpp" +#include "utils.hpp" + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x1000000 + 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); +} + + +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; + + rc = smInitialize(); + if (R_FAILED(rc)) { + fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); + } + + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); +} + +void __appExit(void) { + /* Cleanup services. */ + fsExit(); + smExit(); +} + +int main(int argc, char **argv) +{ + consoleDebugInit(debugDevice_SVC); + HosThread initializer_thread; + + LaunchAllMitmModules(); + + if (R_FAILED(initializer_thread.Initialize(&Utils::InitializeThreadFunc, NULL, 0x4000, 0x15))) { + /* TODO: Panic. */ + } + if (R_FAILED(initializer_thread.Start())) { + /* TODO: Panic. */ + } + + /* Wait for all mitm modules to end. */ + WaitAllMitmModules(); + + return 0; +} + diff --git a/stratosphere/ams_mitm/source/amsmitm_modules.cpp b/stratosphere/ams_mitm/source/amsmitm_modules.cpp new file mode 100644 index 0000000..d2446b7 --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_modules.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 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 "debug.hpp" + +#include "amsmitm_modules.hpp" + +#include "fs_mitm/fsmitm_main.hpp" +#include "set_mitm/setmitm_main.hpp" + +static HosThread g_module_threads[MitmModuleId_Count]; + +static const struct { + ThreadFunc main; + u32 priority; + u32 stack_size; +} g_module_definitions[MitmModuleId_Count] = { + { &FsMitmMain, FsMitmPriority, FsMitmStackSize }, /* FsMitm */ + { &SetMitmMain, SetMitmPriority, SetMitmStackSize }, /* SetMitm */ +}; + +void LaunchAllMitmModules() { + /* Create thread for each module. */ + for (u32 i = 0; i < static_cast(MitmModuleId_Count); i++) { + const auto cur_module = &g_module_definitions[i]; + if (R_FAILED(g_module_threads[i].Initialize(cur_module->main, nullptr, cur_module->stack_size, cur_module->priority))) { + std::abort(); + } + } + + /* Start thread for each module. */ + for (u32 i = 0; i < static_cast(MitmModuleId_Count); i++) { + if (R_FAILED(g_module_threads[i].Start())) { + std::abort(); + } + } +} + +void WaitAllMitmModules() { + /* Wait on thread for each module. */ + for (u32 i = 0; i < static_cast(MitmModuleId_Count); i++) { + g_module_threads[i].Join(); + } +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/amsmitm_modules.hpp b/stratosphere/ams_mitm/source/amsmitm_modules.hpp new file mode 100644 index 0000000..eac0cb6 --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_modules.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 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 + +enum MitmModuleId : u32 { + MitmModuleId_FsMitm = 0, + MitmModuleId_SetMitm = 1, + + MitmModuleId_Count = 2, +}; + +void LaunchAllMitmModules(); + +void WaitAllMitmModules(); \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/debug.cpp b/stratosphere/ams_mitm/source/debug.cpp new file mode 100644 index 0000000..1531eff --- /dev/null +++ b/stratosphere/ams_mitm/source/debug.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 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 "debug.hpp" + +void Reboot() { + /* ... */ +} + +void Log(const void *data, int size) { + /* ... */ +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/debug.hpp b/stratosphere/ams_mitm/source/debug.hpp new file mode 100644 index 0000000..0c1e21e --- /dev/null +++ b/stratosphere/ams_mitm/source/debug.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 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 + +void Reboot(); +void Log(const void *data, int size); \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp new file mode 100644 index 0000000..77e5830 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018 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 "fs_shim.h" + +#include "../debug.hpp" + +enum FsIStorageCmd : u32 { + FsIStorageCmd_Read = 0, + FsIStorageCmd_Write = 1, + FsIStorageCmd_Flush = 2, + FsIStorageCmd_SetSize = 3, + FsIStorageCmd_GetSize = 4, + FsIStorageCmd_OperateRange = 5, +}; + +class IStorage { + public: + virtual ~IStorage(); + + virtual Result Read(void *buffer, size_t size, u64 offset) = 0; + virtual Result Write(void *buffer, size_t size, u64 offset) = 0; + virtual Result Flush() = 0; + virtual Result SetSize(u64 size) = 0; + virtual Result GetSize(u64 *out_size) = 0; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; +}; + +class IStorageInterface : public IServiceObject { + private: + IStorage *base_storage; + public: + IStorageInterface(IStorage *s) : base_storage(s) { + /* ... */ + }; + + ~IStorageInterface() { + delete base_storage; + }; + + private: + /* Actual command API. */ + virtual Result Read(OutBuffer buffer, u64 offset, u64 size) final { + return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset); + }; + virtual Result Write(InBuffer buffer, u64 offset, u64 size) final { + return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset); + }; + virtual Result Flush() final { + return this->base_storage->Flush(); + }; + virtual Result SetSize(u64 size) final { + return this->base_storage->SetSize(size); + }; + virtual Result GetSize(Out size) final { + return this->base_storage->GetSize(size.GetPointer()); + }; + virtual Result OperateRange(Out range_info, u32 operation_type, u64 offset, u64 size) final { + return this->base_storage->OperateRange(operation_type, offset, size, range_info.GetPointer()); + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + /* 4.0.0- */ + MakeServiceCommandMeta(), + }; +}; + +class IROStorage : public IStorage { + public: + virtual Result Read(void *buffer, size_t size, u64 offset) = 0; + virtual Result Write(void *buffer, size_t size, u64 offset) final { + (void)(buffer); + (void)(offset); + (void)(size); + return 0x313802; + }; + virtual Result Flush() final { + return 0x0; + }; + virtual Result SetSize(u64 size) final { + (void)(size); + return 0x313802; + }; + virtual Result GetSize(u64 *out_size) = 0; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; +}; + + +class ProxyStorage : public IStorage { + private: + FsStorage *base_storage; + public: + ProxyStorage(FsStorage *s) : base_storage(s) { + /* ... */ + }; + ProxyStorage(FsStorage s) { + this->base_storage = new FsStorage(s); + }; + virtual ~ProxyStorage() { + fsStorageClose(base_storage); + delete base_storage; + }; + public: + virtual Result Read(void *buffer, size_t size, u64 offset) override { + return fsStorageRead(this->base_storage, offset, buffer, size); + }; + virtual Result Write(void *buffer, size_t size, u64 offset) override { + return fsStorageWrite(this->base_storage, offset, buffer, size); + }; + virtual Result Flush() override { + return fsStorageFlush(this->base_storage); + }; + virtual Result GetSize(u64 *out_size) override { + return fsStorageGetSize(this->base_storage, out_size); + }; + virtual Result SetSize(u64 size) override { + return fsStorageSetSize(this->base_storage, size); + }; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); + }; +}; + +class ROProxyStorage : public IROStorage { + private: + FsStorage *base_storage; + public: + ROProxyStorage(FsStorage *s) : base_storage(s) { + /* ... */ + }; + ROProxyStorage(FsStorage s) { + this->base_storage = new FsStorage(s); + }; + virtual ~ROProxyStorage() { + fsStorageClose(base_storage); + delete base_storage; + }; + public: + virtual Result Read(void *buffer, size_t size, u64 offset) override { + return fsStorageRead(this->base_storage, offset, buffer, size); + }; + virtual Result GetSize(u64 *out_size) override { + return fsStorageGetSize(this->base_storage, out_size); + }; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); + }; +}; \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c new file mode 100644 index 0000000..915366b --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2018 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 "fs_shim.h" + +/* Missing fsp-srv commands. */ +Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 PartitionId; + } *raw; + + raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 12; + raw->PartitionId = PartitionId; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->s, s, &r, 0); + } + } + + return rc; +} + +Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 200; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->s, s, &r, 0); + } + } + + return rc; +} + +Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + FsStorageId storage_id; + u64 data_id; + } *raw; + + raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 202; + raw->storage_id = storage_id; + raw->data_id = data_id; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->s, s, &r, 0); + } + } + + return rc; +} + +/* Missing FS File commands. */ +Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 op_id; + u64 off; + u64 len; + } *raw; + + raw = serviceIpcPrepareHeader(&f->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->op_id = op_id; + raw->off = off; + raw->len = len; + + Result rc = serviceIpcDispatch(&f->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + FsRangeInfo range_info; + } *resp; + + serviceIpcParse(&f->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out) *out = resp->range_info; + } + + return rc; +} + +/* Missing FS Storage commands. */ +Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 op_id; + u64 off; + u64 len; + } *raw; + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->op_id = op_id; + raw->off = off; + raw->len = len; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + FsRangeInfo range_info; + } *resp; + + serviceIpcParse(&s->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out) *out = resp->range_info; + } + + return rc; +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h new file mode 100644 index 0000000..730a2bf --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h @@ -0,0 +1,32 @@ +/** + * @file fs_shim.h + * @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO: Reverse this more. */ +typedef struct { + u32 flags[0x40/sizeof(u32)]; +} FsRangeInfo; + +/* Missing fsp-srv commands. */ +Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId); +Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out); +Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out); + +/* Missing FS File commands. */ +Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out); + +/* Missing FS Storage commands. */ +Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp new file mode 100644 index 0000000..749eae8 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018 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 "fsmitm_boot0storage.hpp" + +static HosMutex g_boot0_mutex; +static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset]; + +bool Boot0Storage::CanModifyBctPubks() { + return this->title_id != 0x010000000000001FULL; +} + +Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) { + std::scoped_lock lk{g_boot0_mutex}; + + return Base::Read(_buffer, size, offset); +} + +Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) { + std::scoped_lock lk{g_boot0_mutex}; + + Result rc = 0; + u8 *buffer = static_cast(_buffer); + + /* Protect the keyblob region from writes. */ + if (offset <= EksStart) { + if (offset + size < EksStart) { + /* Fall through, no need to do anything here. */ + } else { + if (offset + size < EksEnd) { + /* Adjust size to avoid writing end of data. */ + size = EksStart - offset; + } else { + /* Perform portion of write falling past end of keyblobs. */ + const u64 diff = EksEnd - offset; + if (R_FAILED((rc = Base::Write(buffer + diff, size - diff, EksEnd)))) { + return rc; + } + /* Adjust size to avoid writing end of data. */ + size = EksStart - offset; + } + } + } else { + if (offset < EksEnd) { + if (offset + size < EksEnd) { + /* Ignore writes falling strictly within the region. */ + return 0; + } else { + /* Only write past the end of the keyblob region. */ + buffer = buffer + (EksEnd - offset); + size -= (EksEnd - offset); + offset = EksEnd; + } + } else { + /* Fall through, no need to do anything here. */ + } + } + + if (size == 0) { + return 0; + } + + /* We care about protecting autorcm from NS. */ + if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) { + return Base::Write(buffer, size, offset); + } + + /* First, let's deal with the data past the end. */ + if (offset + size >= BctEndOffset) { + const u64 diff = BctEndOffset - offset; + if (R_FAILED((rc = ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset)))) { + return rc; + } + size = diff; + } + + /* Read in the current BCT region. */ + if (R_FAILED((rc = ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0)))) { + return rc; + } + + /* Update the bct buffer. */ + for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) { + const u64 cur_bct_rel_ofs = cur_ofs % BctSize; + if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) { + g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset]; + } + } + + return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0); +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp new file mode 100644 index 0000000..b86791f --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018 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 + +#include "fs_istorage.hpp" + +/* Represents a sectored storage. */ +template +class SectoredProxyStorage : public ProxyStorage { + private: + u64 cur_seek = 0; + u64 cur_sector = 0; + u64 cur_sector_ofs = 0; + u8 sector_buf[SectorSize]; + private: + void Seek(u64 offset) { + this->cur_sector_ofs = offset % SectorSize; + this->cur_seek = offset - this->cur_sector_ofs; + } + public: + SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { } + SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { } + public: + virtual Result Read(void *_buffer, size_t size, u64 offset) override { + Result rc = 0; + u8 *buffer = static_cast(_buffer); + this->Seek(offset); + + if (this->cur_sector_ofs == 0 && size % SectorSize == 0) { + /* Fast case. */ + return ProxyStorage::Read(buffer, size, offset); + } + + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) { + return rc; + } + + if (size + this->cur_sector_ofs <= SectorSize) { + memcpy(buffer, sector_buf + this->cur_sector_ofs, size); + } else { + /* Leaving the sector... */ + size_t ofs = SectorSize - this->cur_sector_ofs; + memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs); + size -= ofs; + + /* We're guaranteed alignment, here. */ + const size_t aligned_remaining_size = size - (size % SectorSize); + if (aligned_remaining_size) { + if (R_FAILED((rc = ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs)))) { + return rc; + } + ofs += aligned_remaining_size; + size -= aligned_remaining_size; + } + + /* Read any leftover data. */ + if (size) { + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) { + return rc; + } + memcpy(buffer + ofs, sector_buf, size); + } + } + + return rc; + }; + virtual Result Write(void *_buffer, size_t size, u64 offset) override { + Result rc = 0; + u8 *buffer = static_cast(_buffer); + this->Seek(offset); + + if (this->cur_sector_ofs == 0 && size % SectorSize == 0) { + /* Fast case. */ + return ProxyStorage::Write(buffer, size, offset); + } + + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) { + return rc; + } + + + if (size + this->cur_sector_ofs <= SectorSize) { + memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size); + rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek); + } else { + /* Leaving the sector... */ + size_t ofs = SectorSize - this->cur_sector_ofs; + memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs); + if (R_FAILED((rc = ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek)))) { + return rc; + } + size -= ofs; + + /* We're guaranteed alignment, here. */ + const size_t aligned_remaining_size = size - (size % SectorSize); + if (aligned_remaining_size) { + if (R_FAILED((rc = ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs)))) { + return rc; + } + ofs += aligned_remaining_size; + size -= aligned_remaining_size; + } + + /* Write any leftover data. */ + if (size) { + if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) { + return rc; + } + memcpy(this->sector_buf, buffer + ofs, size); + rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek); + } + } + + return rc; + }; +}; + +/* Represents an RCM-preserving BOOT0 partition. */ +class Boot0Storage : public SectoredProxyStorage<0x200> { + using Base = SectoredProxyStorage<0x200>; + + public: + static constexpr u64 BctEndOffset = 0xFC000; + static constexpr u64 BctSize = 0x4000; + static constexpr u64 BctPubkStart = 0x210; + static constexpr u64 BctPubkSize = 0x100; + static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize; + + static constexpr u64 EksStart = 0x180000; + static constexpr u64 EksSize = 0x4000; + static constexpr u64 EksEnd = EksStart + EksSize; + private: + u64 title_id; + private: + bool CanModifyBctPubks(); + public: + Boot0Storage(FsStorage *s, u64 t) : Base(s), title_id(t) { } + Boot0Storage(FsStorage s, u64 t) : Base(s), title_id(t) { } + public: + virtual Result Read(void *_buffer, size_t size, u64 offset) override; + virtual Result Write(void *_buffer, size_t size, u64 offset) override; +}; \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp new file mode 100644 index 0000000..fe81b7e --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018 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 "fsmitm_layeredrom.hpp" +#include "../utils.hpp" +#include "../debug.hpp" + +IStorage::~IStorage() = default; + +LayeredRomFS::LayeredRomFS(std::shared_ptr s_r, std::shared_ptr f_r, u64 tid) : storage_romfs(s_r), file_romfs(f_r), title_id(tid) { + /* Start building the new virtual romfs. */ + RomFSBuildContext build_ctx(this->title_id); + this->p_source_infos = std::shared_ptr>(new std::vector(), [](std::vector *to_delete) { + for (unsigned int i = 0; i < to_delete->size(); i++) { + (*to_delete)[i].Cleanup(); + } + delete to_delete; + }); + if (Utils::IsSdInitialized()) { + build_ctx.MergeSdFiles(); + } + if (this->file_romfs) { + build_ctx.MergeRomStorage(this->file_romfs.get(), RomFSDataSource::FileRomFS); + } + if (this->storage_romfs) { + build_ctx.MergeRomStorage(this->storage_romfs.get(), RomFSDataSource::BaseRomFS); + } + build_ctx.Build(this->p_source_infos.get()); +} + + +Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) { + /* Size zero reads should always succeed. */ + if (size == 0) { + return 0; + } + + /* Validate size. */ + u64 virt_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size; + if (offset >= virt_size) { + return 0x2F5A02; + } + if (virt_size - offset < size) { + size = virt_size - offset; + } + /* Find first source info via binary search. */ + u32 cur_source_ind = 0; + u32 low = 0, high = this->p_source_infos->size() - 1; + while (low <= high) { + u32 mid = (low + high) / 2; + if ((*this->p_source_infos)[mid].virtual_offset > offset) { + /* Too high. */ + high = mid - 1; + } else { + /* sources[mid].virtual_offset <= offset, invariant */ + if (mid == this->p_source_infos->size() - 1 || (*this->p_source_infos)[mid + 1].virtual_offset > offset) { + /* Success */ + cur_source_ind = mid; + break; + } + low = mid + 1; + } + } + + Result rc; + size_t read_so_far = 0; + while (read_so_far < size) { + RomFSSourceInfo *cur_source = &((*this->p_source_infos)[cur_source_ind]); + if (cur_source->virtual_offset + cur_source->size > offset) { + u64 cur_read_size = size - read_so_far; + if (cur_read_size > cur_source->size - (offset - cur_source->virtual_offset)) { + cur_read_size = cur_source->size - (offset - cur_source->virtual_offset); + } + switch (cur_source->type) { + case RomFSDataSource::MetaData: + { + FsFile file; + if (R_FAILED((rc = Utils::OpenSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file)))) { + fatalSimple(rc); + } + size_t out_read; + if (R_FAILED((rc = fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, &out_read)))) { + fatalSimple(rc); + } + if (out_read != cur_read_size) { + Reboot(); + } + fsFileClose(&file); + } + break; + case RomFSDataSource::LooseFile: + { + FsFile file; + if (R_FAILED((rc = Utils::OpenRomFSSdFile(this->title_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file)))) { + fatalSimple(rc); + } + size_t out_read; + if (R_FAILED((rc = fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, &out_read)))) { + fatalSimple(rc); + } + if (out_read != cur_read_size) { + Reboot(); + } + fsFileClose(&file); + } + break; + case RomFSDataSource::Memory: + { + memcpy((void *)((uintptr_t)buffer + read_so_far), cur_source->memory_source_info.data + (offset - cur_source->virtual_offset), cur_read_size); + } + break; + case RomFSDataSource::BaseRomFS: + { + if (R_FAILED((rc = this->storage_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset))))) { + /* TODO: Can this ever happen? */ + /* fatalSimple(rc); */ + return rc; + } + } + break; + case RomFSDataSource::FileRomFS: + { + if (R_FAILED((rc = this->file_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset))))) { + fatalSimple(rc); + } + } + break; + default: + fatalSimple(0xF601); + } + read_so_far += cur_read_size; + offset += cur_read_size; + } else { + /* Handle padding explicitly. */ + cur_source_ind++; + /* Zero out the padding we skip, here. */ + memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset); + read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset; + offset = ((*this->p_source_infos)[cur_source_ind]).virtual_offset; + } + } + + return 0; +} +Result LayeredRomFS::GetSize(u64 *out_size) { + *out_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size; + return 0x0; +} +Result LayeredRomFS::OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) { + /* TODO: How should I implement this for a virtual romfs? */ + if (operation_type == 3) { + *out_range_info = {0}; + } + return 0; +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.hpp new file mode 100644 index 0000000..b297039 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 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 "fsmitm_romstorage.hpp" +#include "fsmitm_romfsbuild.hpp" +#include "../utils.hpp" + + +/* Represents a merged RomFS. */ +class LayeredRomFS : public IROStorage { + private: + /* Data Sources. */ + std::shared_ptr storage_romfs; + std::shared_ptr file_romfs; + /* Information about the merged RomFS. */ + u64 title_id; + std::shared_ptr> p_source_infos; + + public: + LayeredRomFS(std::shared_ptr s_r, std::shared_ptr f_r, u64 tid); + virtual ~LayeredRomFS() = default; + + virtual Result Read(void *buffer, size_t size, u64 offset) override; + virtual Result GetSize(u64 *out_size) override; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override; +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.cpp new file mode 100644 index 0000000..bf66ed5 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 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 "fsmitm_main.hpp" +#include "fsmitm_service.hpp" + +#include "../utils.hpp" + +struct FsMitmManagerOptions { + static const size_t PointerBufferSize = 0x800; + static const size_t MaxDomains = 0x40; + static const size_t MaxDomainObjects = 0x4000; +}; +using FsMitmManager = WaitableManager; + +void FsMitmMain(void *arg) { + /* Create server manager. */ + auto server_manager = new FsMitmManager(5); + + /* Create fsp-srv mitm. */ + AddMitmServerToManager(server_manager, "fsp-srv", 61); + + /* Loop forever, servicing our services. */ + server_manager->Process(); + + delete server_manager; +} + diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.hpp new file mode 100644 index 0000000..400ada6 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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 + +constexpr u32 FsMitmPriority = 43; +constexpr u32 FsMitmStackSize = 0x8000; + +void FsMitmMain(void *arg); \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.cpp new file mode 100644 index 0000000..611b949 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2018 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 "../utils.hpp" +#include "fsmitm_romfsbuild.hpp" + +#include "../debug.hpp" + +void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent) { + FsDir dir; + Result rc; + + std::vector child_dirs; + + /* Open the current parent directory. */ + if (R_FAILED((rc = Utils::OpenRomFSDir(filesys, this->title_id, parent->path, &dir)))) { + fatalSimple(rc); + } + + u64 read_entries; + while (R_SUCCEEDED((rc = fsDirRead(&dir, 0, &read_entries, 1, &this->dir_entry))) && read_entries == 1) { + if (this->dir_entry.type == ENTRYTYPE_DIR) { + RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0}); + /* Set child's path. */ + child->cur_path_ofs = parent->path_len + 1; + child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name); + child->path = new char[child->path_len + 1]; + strcpy(child->path, parent->path); + if (child->path_len > FS_MAX_PATH - 1) { + fatalSimple(0xF601); + } + strcat(child->path + parent->path_len, "/"); + strcat(child->path + parent->path_len, this->dir_entry.name); + + if (!this->AddDirectory(parent, child, NULL)) { + delete child->path; + delete child; + } else { + child_dirs.push_back(child); + } + } else if (this->dir_entry.type == ENTRYTYPE_FILE) { + RomFSBuildFileContext *child = new RomFSBuildFileContext({0}); + /* Set child's path. */ + child->cur_path_ofs = parent->path_len + 1; + child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name); + child->path = new char[child->path_len + 1]; + strcpy(child->path, parent->path); + if (child->path_len > FS_MAX_PATH - 1) { + fatalSimple(0xF601); + } + strcat(child->path + parent->path_len, "/"); + strcat(child->path + parent->path_len, this->dir_entry.name); + + child->source = this->cur_source_type; + + child->size = this->dir_entry.fileSize; + + if (!this->AddFile(parent, child)) { + delete child->path; + delete child; + } + } else { + fatalSimple(rc); + } + } + fsDirClose(&dir); + + for (auto &child : child_dirs) { + this->VisitDirectory(filesys, child); + } +} + +void RomFSBuildContext::MergeSdFiles() { + FsFileSystem sd_filesystem; + FsDir dir; + if (!Utils::IsSdInitialized()) { + return; + } + if (R_FAILED((Utils::OpenSdDirForAtmosphere(this->title_id, "/romfs", &dir)))) { + return; + } + fsDirClose(&dir); + if (R_FAILED(fsMountSdcard(&sd_filesystem))) { + return; + } + this->cur_source_type = RomFSDataSource::LooseFile; + this->VisitDirectory(&sd_filesystem, this->root); + fsFsClose(&sd_filesystem); +} + +void RomFSBuildContext::VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size) { + RomFSDirectoryEntry *parent_entry = romfs_get_direntry(dir_table, parent_offset); + if (parent_entry->file != ROMFS_ENTRY_EMPTY) { + RomFSFileEntry *cur_file = romfs_get_fentry(file_table, parent_entry->file); + while (cur_file != NULL) { + RomFSBuildFileContext *child = new RomFSBuildFileContext({0}); + /* Set child's path. */ + child->cur_path_ofs = parent->path_len + 1; + child->path_len = child->cur_path_ofs + cur_file->name_size; + child->path = new char[child->path_len + 1]; + strcpy(child->path, parent->path); + if (child->path_len > FS_MAX_PATH - 1) { + fatalSimple(0xF601); + } + strcat(child->path + parent->path_len, "/"); + strncat(child->path + parent->path_len, cur_file->name, cur_file->name_size); + child->size = cur_file->size; + + child->source = this->cur_source_type; + child->orig_offset = cur_file->offset; + if (!this->AddFile(parent, child)) { + delete child->path; + delete child; + } + if (cur_file->sibling == ROMFS_ENTRY_EMPTY) { + cur_file = NULL; + } else { + cur_file = romfs_get_fentry(file_table, cur_file->sibling); + } + } + } + if (parent_entry->child != ROMFS_ENTRY_EMPTY) { + RomFSDirectoryEntry *cur_child = romfs_get_direntry(dir_table, parent_entry->child); + u32 cur_child_offset = parent_entry->child; + while (cur_child != NULL) { + RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0}); + /* Set child's path. */ + child->cur_path_ofs = parent->path_len + 1; + child->path_len = child->cur_path_ofs + cur_child->name_size; + child->path = new char[child->path_len + 1]; + strcpy(child->path, parent->path); + if (child->path_len > FS_MAX_PATH - 1) { + fatalSimple(0xF601); + } + strcat(child->path + parent->path_len, "/"); + strncat(child->path + parent->path_len, cur_child->name, cur_child->name_size); + + RomFSBuildDirectoryContext *real = NULL; + if (!this->AddDirectory(parent, child, &real)) { + delete child->path; + delete child; + } + if (real == NULL) { + fatalSimple(0xF601); + } + + this->VisitDirectory(real, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size); + + if (cur_child->sibling == ROMFS_ENTRY_EMPTY) { + cur_child = NULL; + } else { + cur_child_offset = cur_child->sibling; + cur_child = romfs_get_direntry(dir_table, cur_child->sibling); + } + } + } +} + +void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) { + Result rc; + RomFSHeader header; + if (R_FAILED((rc = storage->Read(&header, sizeof(header), 0)))) { + fatalSimple(rc); + } + if (header.header_size != sizeof(header)) { + /* what */ + return; + } + + /* Read tables. */ + auto dir_table = std::make_unique(header.dir_table_size); + auto file_table = std::make_unique(header.file_table_size); + if (R_FAILED((rc = storage->Read(dir_table.get(), header.dir_table_size, header.dir_table_ofs)))) { + fatalSimple(rc); + } + if (R_FAILED((rc = storage->Read(file_table.get(), header.file_table_size, header.file_table_ofs)))) { + fatalSimple(rc); + } + + this->cur_source_type = source; + this->VisitDirectory(this->root, 0x0, dir_table.get(), (size_t)header.dir_table_size, file_table.get(), (size_t)header.file_table_size); +} + +bool RomFSBuildContext::AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx) { + /* Check whether it's already in the known directories. */ + auto existing = this->directories.find(dir_ctx->path); + if (existing != this->directories.end()) { + if (out_dir_ctx) { + *out_dir_ctx = existing->second; + } + return false; + } + + /* Add a new directory. */ + this->num_dirs++; + this->dir_table_size += sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3); + dir_ctx->parent = parent_dir_ctx; + this->directories.insert({dir_ctx->path, dir_ctx}); + + if (out_dir_ctx) { + *out_dir_ctx = dir_ctx; + } + return true; +} + +bool RomFSBuildContext::AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx) { + /* Check whether it's already in the known files. */ + auto existing = this->files.find(file_ctx->path); + if (existing != this->files.end()) { + return false; + } + + /* Add a new file. */ + this->num_files++; + this->file_table_size += sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3); + file_ctx->parent = parent_dir_ctx; + this->files.insert({file_ctx->path, file_ctx}); + + return true; +} + +void RomFSBuildContext::Build(std::vector *out_infos) { + RomFSBuildFileContext *cur_file; + RomFSBuildDirectoryContext *cur_dir; + u32 entry_offset; + + u32 dir_hash_table_entry_count = romfs_get_hash_table_count(this->num_dirs); + u32 file_hash_table_entry_count = romfs_get_hash_table_count(this->num_files); + this->dir_hash_table_size = 4 * dir_hash_table_entry_count; + this->file_hash_table_size = 4 * file_hash_table_entry_count; + + /* Assign metadata pointers */ + RomFSHeader *header = new RomFSHeader({0}); + u8 *metadata = new u8[this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size]; + u32 *dir_hash_table = (u32 *)((uintptr_t)metadata); + RomFSDirectoryEntry *dir_table = (RomFSDirectoryEntry *)((uintptr_t)dir_hash_table + this->dir_hash_table_size); + u32 *file_hash_table = (u32 *)((uintptr_t)dir_table + this->dir_table_size); + RomFSFileEntry *file_table = (RomFSFileEntry *)((uintptr_t)file_hash_table + this->file_hash_table_size); + + /* Clear out hash tables. */ + for (u32 i = 0; i < dir_hash_table_entry_count; i++) { + dir_hash_table[i] = ROMFS_ENTRY_EMPTY; + } + for (u32 i = 0; i < file_hash_table_entry_count; i++) { + file_hash_table[i] = ROMFS_ENTRY_EMPTY; + } + + out_infos->clear(); + out_infos->emplace_back(0, sizeof(*header), header, RomFSDataSource::Memory); + + /* Determine file offsets. */ + entry_offset = 0; + RomFSBuildFileContext *prev_file = NULL; + for (const auto &it : this->files) { + cur_file = it.second; + this->file_partition_size = (this->file_partition_size + 0xFULL) & ~0xFULL; + /* Check for extra padding in the original romfs source and preserve it, to help ourselves later. */ + if (prev_file && prev_file->source == cur_file->source && (prev_file->source == RomFSDataSource::BaseRomFS || prev_file->source == RomFSDataSource::FileRomFS)) { + u64 expected = (this->file_partition_size - prev_file->offset + prev_file->orig_offset); + if (expected != cur_file->orig_offset) { + if (expected > cur_file->orig_offset) { + /* This case should NEVER happen. */ + fatalSimple(0xF601); + } + this->file_partition_size += cur_file->orig_offset - expected; + } + } + cur_file->offset = this->file_partition_size; + this->file_partition_size += cur_file->size; + cur_file->entry_offset = entry_offset; + entry_offset += sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3); + prev_file = cur_file; + } + /* Assign deferred parent/sibling ownership. */ + for (auto it = this->files.rbegin(); it != this->files.rend(); it++) { + cur_file = it->second; + cur_file->sibling = cur_file->parent->file; + cur_file->parent->file = cur_file; + } + + /* Determine directory offsets. */ + entry_offset = 0; + for (const auto &it : this->directories) { + cur_dir = it.second; + cur_dir->entry_offset = entry_offset; + entry_offset += sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3); + } + /* Assign deferred parent/sibling ownership. */ + for (auto it = this->directories.rbegin(); it->second != this->root; it++) { + cur_dir = it->second; + cur_dir->sibling = cur_dir->parent->child; + cur_dir->parent->child = cur_dir; + } + + + /* Populate file tables. */ + for (const auto &it : this->files) { + cur_file = it.second; + RomFSFileEntry *cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset); + + cur_entry->parent = cur_file->parent->entry_offset; + cur_entry->sibling = (cur_file->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset; + cur_entry->offset = cur_file->offset; + cur_entry->size = cur_file->size; + + u32 name_size = cur_file->path_len - cur_file->cur_path_ofs; + u32 hash = romfs_calc_path_hash(cur_file->parent->entry_offset, (unsigned char *)cur_file->path + cur_file->cur_path_ofs, 0, name_size); + cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count]; + file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset; + + cur_entry->name_size = name_size; + memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3); + memcpy(cur_entry->name, cur_file->path + cur_file->cur_path_ofs, name_size); + + switch (cur_file->source) { + case RomFSDataSource::BaseRomFS: + case RomFSDataSource::FileRomFS: + /* Try to compact, if possible. */ + if (out_infos->back().type == cur_file->source) { + out_infos->back().size = cur_file->offset + ROMFS_FILEPARTITION_OFS + cur_file->size - out_infos->back().virtual_offset; + } else { + out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, cur_file->orig_offset + ROMFS_FILEPARTITION_OFS, cur_file->source); + } + break; + case RomFSDataSource::LooseFile: + { + char *path = new char[cur_file->path_len + 1]; + strcpy(path, cur_file->path); + out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, path, cur_file->source); + } + break; + default: + fatalSimple(0xF601); + } + } + + /* Populate dir tables. */ + for (const auto &it : this->directories) { + cur_dir = it.second; + RomFSDirectoryEntry *cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset); + cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset; + cur_entry->sibling = (cur_dir->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset; + cur_entry->child = (cur_dir->child == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset; + cur_entry->file = (cur_dir->file == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset; + + u32 name_size = cur_dir->path_len - cur_dir->cur_path_ofs; + u32 hash = romfs_calc_path_hash(cur_dir == this->root ? 0 : cur_dir->parent->entry_offset, (unsigned char *)cur_dir->path + cur_dir->cur_path_ofs, 0, name_size); + cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count]; + dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset; + + cur_entry->name_size = name_size; + memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3); + memcpy(cur_entry->name, cur_dir->path + cur_dir->cur_path_ofs, name_size); + } + + /* Delete directories. */ + for (const auto &it : this->directories) { + cur_dir = it.second; + delete cur_dir->path; + delete cur_dir; + } + this->root = NULL; + this->directories.clear(); + + /* Delete files. */ + for (const auto &it : this->files) { + cur_file = it.second; + delete cur_file->path; + delete cur_file; + } + this->files.clear(); + + /* Set header fields. */ + header->header_size = sizeof(*header); + header->file_hash_table_size = this->file_hash_table_size; + header->file_table_size = this->file_table_size; + header->dir_hash_table_size = this->dir_hash_table_size; + header->dir_table_size = this->dir_table_size; + header->file_partition_ofs = ROMFS_FILEPARTITION_OFS; + header->dir_hash_table_ofs = (header->file_partition_ofs + this->file_partition_size + 3ULL) & ~3ULL; + header->dir_table_ofs = header->dir_hash_table_ofs + header->dir_hash_table_size; + header->file_hash_table_ofs = header->dir_table_ofs + header->dir_table_size; + header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size; + + const size_t metadata_size = this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size; + + /* Try to save metadata to the SD card, to save on memory space. */ + if (R_SUCCEEDED(Utils::SaveSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, metadata, metadata_size))) { + out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, RomFSDataSource::MetaData); + delete metadata; + } else { + out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, metadata, RomFSDataSource::Memory); + } + +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.hpp new file mode 100644 index 0000000..e76b7e7 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.hpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2018 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 "fsmitm_romstorage.hpp" + +#include "../debug.hpp" + +#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF +#define ROMFS_FILEPARTITION_OFS 0x200 + +#define ROMFS_METADATA_FILE_PATH "romfs_metadata.bin" + +/* Types for RomFS Meta construction. */ +enum class RomFSDataSource { + BaseRomFS, + FileRomFS, + LooseFile, + MetaData, + Memory, +}; + +struct RomFSBaseSourceInfo { + u64 offset; +}; + +struct RomFSFileSourceInfo { + u64 offset; +}; + +struct RomFSLooseSourceInfo { + const char *path; +}; + +struct RomFSMemorySourceInfo { + const u8 *data; +}; + +struct RomFSMetaDataSourceInfo { + +}; + +struct RomFSSourceInfo { + u64 virtual_offset; + u64 size; + union { + RomFSBaseSourceInfo base_source_info; + RomFSFileSourceInfo file_source_info; + RomFSLooseSourceInfo loose_source_info; + RomFSMemorySourceInfo memory_source_info; + RomFSMemorySourceInfo metadata_source_info; + }; + RomFSDataSource type; + + RomFSSourceInfo(u64 v_o, u64 s, u64 offset, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) { + switch (this->type) { + case RomFSDataSource::BaseRomFS: + this->base_source_info.offset = offset; + break; + case RomFSDataSource::FileRomFS: + this->file_source_info.offset = offset; + break; + case RomFSDataSource::LooseFile: + case RomFSDataSource::MetaData: + case RomFSDataSource::Memory: + default: + fatalSimple(0xF601); + } + } + + RomFSSourceInfo(u64 v_o, u64 s, const void *arg, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) { + switch (this->type) { + case RomFSDataSource::LooseFile: + this->loose_source_info.path = (decltype(this->loose_source_info.path))arg; + break; + case RomFSDataSource::Memory: + this->memory_source_info.data = (decltype(this->memory_source_info.data))arg; + break; + case RomFSDataSource::MetaData: + case RomFSDataSource::BaseRomFS: + case RomFSDataSource::FileRomFS: + default: + fatalSimple(0xF601); + } + } + + RomFSSourceInfo(u64 v_o, u64 s, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) { + switch (this->type) { + case RomFSDataSource::MetaData: + break; + case RomFSDataSource::LooseFile: + case RomFSDataSource::Memory: + case RomFSDataSource::BaseRomFS: + case RomFSDataSource::FileRomFS: + default: + fatalSimple(0xF601); + } + } + + void Cleanup() { + switch (this->type) { + case RomFSDataSource::BaseRomFS: + case RomFSDataSource::FileRomFS: + case RomFSDataSource::MetaData: + break; + case RomFSDataSource::LooseFile: + delete this->loose_source_info.path; + break; + case RomFSDataSource::Memory: + delete this->memory_source_info.data; + break; + default: + fatalSimple(0xF601); + } + } + + static bool Compare(RomFSSourceInfo *a, RomFSSourceInfo *b) { + return (a->virtual_offset < b->virtual_offset); + } +}; + +/* Types for building a RomFS. */ +struct RomFSHeader { + u64 header_size; + u64 dir_hash_table_ofs; + u64 dir_hash_table_size; + u64 dir_table_ofs; + u64 dir_table_size; + u64 file_hash_table_ofs; + u64 file_hash_table_size; + u64 file_table_ofs; + u64 file_table_size; + u64 file_partition_ofs; +}; + +static_assert(sizeof(RomFSHeader) == 0x50, "Incorrect RomFS Header definition!"); + +struct RomFSDirectoryEntry { + u32 parent; + u32 sibling; + u32 child; + u32 file; + u32 hash; + u32 name_size; + char name[]; +}; + +static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "Incorrect RomFSDirectoryEntry definition!"); + +struct RomFSFileEntry { + u32 parent; + u32 sibling; + u64 offset; + u64 size; + u32 hash; + u32 name_size; + char name[]; +}; + +static_assert(sizeof(RomFSFileEntry) == 0x20, "Incorrect RomFSFileEntry definition!"); + +struct RomFSBuildFileContext; + +/* Used as comparator for std::map */ +struct build_ctx_cmp { + bool operator()(const char *a, const char *b) const { + return strcmp(a, b) < 0; + } +}; + +struct RomFSBuildDirectoryContext { + char *path; + u32 cur_path_ofs; + u32 path_len; + u32 entry_offset = 0; + RomFSBuildDirectoryContext *parent = NULL; + RomFSBuildDirectoryContext *child = NULL; + RomFSBuildDirectoryContext *sibling = NULL; + RomFSBuildFileContext *file = NULL; +}; + +struct RomFSBuildFileContext { + char *path; + u32 cur_path_ofs; + u32 path_len; + u32 entry_offset = 0; + u64 offset = 0; + u64 size = 0; + RomFSBuildDirectoryContext *parent = NULL; + RomFSBuildFileContext *sibling = NULL; + RomFSDataSource source{0}; + u64 orig_offset = 0; +}; + +class RomFSBuildContext { + private: + u64 title_id; + RomFSBuildDirectoryContext *root; + std::map directories; + std::map files; + u64 num_dirs = 0; + u64 num_files = 0; + u64 dir_table_size = 0; + u64 file_table_size = 0; + u64 dir_hash_table_size = 0; + u64 file_hash_table_size = 0; + u64 file_partition_size = 0; + + FsDirectoryEntry dir_entry; + RomFSDataSource cur_source_type; + + void VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent); + void VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size); + + bool AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx); + bool AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx); + public: + RomFSBuildContext(u64 tid) : title_id(tid) { + this->root = new RomFSBuildDirectoryContext({0}); + this->root->path = new char[1]; + this->root->path[0] = '\x00'; + this->directories.insert({this->root->path, this->root}); + this->num_dirs = 1; + this->dir_table_size = 0x18; + } + + void MergeSdFiles(); + void MergeRomStorage(IROStorage *storage, RomFSDataSource source); + + /* This finalizes the context. */ + void Build(std::vector *out_infos); +}; + + +static inline RomFSDirectoryEntry *romfs_get_direntry(void *directories, uint32_t offset) { + return (RomFSDirectoryEntry *)((uintptr_t)directories + offset); +} + +static inline RomFSFileEntry *romfs_get_fentry(void *files, uint32_t offset) { + return (RomFSFileEntry *)((uintptr_t)files + offset); +} + +static inline uint32_t romfs_calc_path_hash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) { + uint32_t hash = parent ^ 123456789; + for (uint32_t i = 0; i < path_len; i++) { + hash = (hash >> 5) | (hash << 27); + hash ^= path[start + i]; + } + + return hash; +} + +static inline uint32_t romfs_get_hash_table_count(uint32_t num_entries) { + if (num_entries < 3) { + return 3; + } else if (num_entries < 19) { + return num_entries | 1; + } + uint32_t count = num_entries; + while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) { + count++; + } + return count; +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romstorage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romstorage.hpp new file mode 100644 index 0000000..1a186ec --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romstorage.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 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 "fs_istorage.hpp" + +/* Represents a RomFS stored in some file. */ +class RomFileStorage : public IROStorage { + private: + FsFile *base_file; + public: + RomFileStorage(FsFile *f) : base_file(f) { + /* ... */ + }; + RomFileStorage(FsFile f) { + this->base_file = new FsFile(f); + }; + virtual ~RomFileStorage() { + fsFileClose(base_file); + delete base_file; + }; + public: + Result Read(void *buffer, size_t size, u64 offset) override { + size_t out_sz = 0; + Result rc = fsFileRead(this->base_file, offset, buffer, size, &out_sz); + if (R_SUCCEEDED(rc) && out_sz != size && out_sz) { + return this->Read((void *)((uintptr_t)buffer + out_sz), size - out_sz, offset + out_sz); + } + return rc; + }; + Result GetSize(u64 *out_size) override { + return fsFileGetSize(this->base_file, out_size); + }; + Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + /* TODO: Merge into libnx? */ + return fsFileOperateRange(this->base_file, operation_type, offset, size, out_range_info); + }; +}; + +/* Represents a RomFS accessed via some IStorage. */ +using RomInterfaceStorage = ROProxyStorage; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp new file mode 100644 index 0000000..5804a6b --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2018 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 "fsmitm_service.hpp" +#include "fs_shim.h" + +#include "../utils.hpp" +#include "fsmitm_boot0storage.hpp" +#include "fsmitm_romstorage.hpp" +#include "fsmitm_layeredrom.hpp" + +#include "../debug.hpp" + +static HosMutex g_StorageCacheLock; +static std::unordered_map> g_StorageCache; + +static bool StorageCacheGetEntry(u64 title_id, std::shared_ptr *out) { + std::scoped_lock lock(g_StorageCacheLock); + if (g_StorageCache.find(title_id) == g_StorageCache.end()) { + return false; + } + + auto intf = g_StorageCache[title_id].lock(); + if (intf != nullptr) { + *out = intf; + return true; + } + return false; +} + +static void StorageCacheSetEntry(u64 title_id, std::shared_ptr *ptr) { + std::scoped_lock lock(g_StorageCacheLock); + + /* Ensure we always use the cached copy if present. */ + if (g_StorageCache.find(title_id) != g_StorageCache.end()) { + auto intf = g_StorageCache[title_id].lock(); + if (intf != nullptr) { + *ptr = intf; + } + } + + g_StorageCache[title_id] = *ptr; +} + +void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) { + auto this_ptr = static_cast(obj); + switch ((FspSrvCmd)ctx->cmd_id) { + case FspSrvCmd_SetCurrentProcess: + if (R_SUCCEEDED(ctx->rc)) { + this_ptr->has_initialized = true; + this_ptr->process_id = ctx->request.Pid; + this_ptr->title_id = this_ptr->process_id; + if (R_FAILED(MitmQueryUtils::GetAssociatedTidForPid(this_ptr->process_id, &this_ptr->title_id))) { + /* Log here, if desired. */ + } + break; + } + break; + default: + break; + } +} + +/* Gate access to the BIS partitions. */ +Result FsMitmService::OpenBisStorage(Out> out_storage, u32 bis_partition_id) { + std::shared_ptr storage = nullptr; + u32 out_domain_id = 0; + Result rc = 0; + + ON_SCOPE_EXIT { + if (R_SUCCEEDED(rc)) { + out_storage.SetValue(std::move(storage)); + if (out_storage.IsDomain()) { + out_storage.ChangeObjectId(out_domain_id); + } + } + }; + + { + FsStorage bis_storage; + rc = fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id); + if (R_SUCCEEDED(rc)) { + const bool is_sysmodule = this->title_id < 0x0100000000001000; + const bool has_bis_write_flag = Utils::HasFlag(this->title_id, "bis_write"); + const bool has_cal0_read_flag = Utils::HasFlag(this->title_id, "cal_read"); + if (bis_partition_id == BisStorageId_Boot0) { + storage = std::make_shared(new Boot0Storage(bis_storage, this->title_id)); + } else if (bis_partition_id == BisStorageId_Prodinfo) { + /* PRODINFO should *never* be writable. */ + if (is_sysmodule || has_cal0_read_flag) { + storage = std::make_shared(new ROProxyStorage(bis_storage)); + } else { + /* Do not allow non-sysmodules to read *or* write CAL0. */ + fsStorageClose(&bis_storage); + return 0x320002; + } + } else { + if (is_sysmodule || has_bis_write_flag) { + /* Sysmodules should still be allowed to read and write. */ + storage = std::make_shared(new ProxyStorage(bis_storage)); + } else { + /* Non-sysmodules should be allowed to read. */ + storage = std::make_shared(new ROProxyStorage(bis_storage)); + } + } + if (out_storage.IsDomain()) { + out_domain_id = bis_storage.s.object_id; + } + } + } + + return rc; +} + +/* Add redirection for RomFS to the SD card. */ +Result FsMitmService::OpenDataStorageByCurrentProcess(Out> out_storage) { + std::shared_ptr storage = nullptr; + u32 out_domain_id = 0; + Result rc = 0; + + if (!this->should_override_contents) { + return RESULT_FORWARD_TO_SESSION; + } + + bool has_cache = StorageCacheGetEntry(this->title_id, &storage); + + ON_SCOPE_EXIT { + if (R_SUCCEEDED(rc)) { + if (!has_cache) { + StorageCacheSetEntry(this->title_id, &storage); + } + + out_storage.SetValue(std::move(storage)); + if (out_storage.IsDomain()) { + out_storage.ChangeObjectId(out_domain_id); + } + } + }; + + + if (has_cache) { + if (out_storage.IsDomain()) { + FsStorage s = {0}; + rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &s); + if (R_SUCCEEDED(rc)) { + out_domain_id = s.s.object_id; + } + } else { + rc = 0; + } + if (R_FAILED(rc)) { + storage.reset(); + } + } else { + FsStorage data_storage; + FsFile data_file; + + rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage); + + Log(armGetTls(), 0x100); + if (R_SUCCEEDED(rc)) { + if (Utils::HasSdRomfsContent(this->title_id)) { + /* TODO: Is there a sensible path that ends in ".romfs" we can use?" */ + if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(this->title_id, "romfs.bin", FS_OPEN_READ, &data_file))) { + storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), this->title_id)); + } else { + storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, this->title_id)); + } + if (out_storage.IsDomain()) { + out_domain_id = data_storage.s.object_id; + } + } else { + /* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */ + fsStorageClose(&data_storage); + rc = RESULT_FORWARD_TO_SESSION; + } + } + } + + return rc; +} + +/* Add redirection for System Data Archives to the SD card. */ +Result FsMitmService::OpenDataStorageByDataId(Out> out_storage, u64 data_id, u8 sid) { + FsStorageId storage_id = (FsStorageId)sid; + FsStorage data_storage; + FsFile data_file; + + if (!this->should_override_contents) { + return RESULT_FORWARD_TO_SESSION; + } + + std::shared_ptr storage = nullptr; + u32 out_domain_id = 0; + Result rc = 0; + + bool has_cache = StorageCacheGetEntry(data_id, &storage); + + ON_SCOPE_EXIT { + if (R_SUCCEEDED(rc)) { + if (!has_cache) { + StorageCacheSetEntry(data_id, &storage); + } + + out_storage.SetValue(std::move(storage)); + if (out_storage.IsDomain()) { + out_storage.ChangeObjectId(out_domain_id); + } + } + }; + + if (has_cache) { + if (out_storage.IsDomain()) { + FsStorage s = {0}; + rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &s); + if (R_SUCCEEDED(rc)) { + out_domain_id = s.s.object_id; + } + } else { + rc = 0; + } + if (R_FAILED(rc)) { + storage.reset(); + } + } else { + rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &data_storage); + + if (R_SUCCEEDED(rc)) { + if (Utils::HasSdRomfsContent(data_id)) { + /* TODO: Is there a sensible path that ends in ".romfs" we can use?" */ + if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(data_id, "romfs.bin", FS_OPEN_READ, &data_file))) { + storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), data_id)); + } else { + storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, data_id)); + } + if (out_storage.IsDomain()) { + out_domain_id = data_storage.s.object_id; + } + } else { + /* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */ + fsStorageClose(&data_storage); + rc = RESULT_FORWARD_TO_SESSION; + } + } + } + + return rc; +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp new file mode 100644 index 0000000..c7cd6ed --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 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 "fs_istorage.hpp" +#include "../utils.hpp" + +enum FspSrvCmd : u32 { + FspSrvCmd_SetCurrentProcess = 1, + FspSrvCmd_OpenBisStorage = 12, + FspSrvCmd_OpenDataStorageByCurrentProcess = 200, + FspSrvCmd_OpenDataStorageByDataId = 202, +}; + +class FsMitmService : public IMitmServiceObject { + private: + bool has_initialized = false; + bool should_override_contents; + public: + FsMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { + if (Utils::HasSdDisableMitMFlag(this->title_id)) { + this->should_override_contents = false; + } else { + this->should_override_contents = (this->title_id >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id); + } + } + + static bool ShouldMitm(u64 pid, u64 tid) { + /* Don't Mitm KIPs */ + if (pid < 0x50) { + return false; + } + + static std::atomic_bool has_launched_qlaunch = false; + + /* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */ + /* Figure out why, and address it. */ + if (tid == 0x0100000000001000ULL) { + has_launched_qlaunch = true; + } + + return has_launched_qlaunch || tid == 0x010000000000001FULL || tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid); + } + + static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx); + + protected: + /* Overridden commands. */ + Result OpenBisStorage(Out> out, u32 bis_partition_id); + Result OpenDataStorageByCurrentProcess(Out> out); + Result OpenDataStorageByDataId(Out> out, u64 data_id, u8 storage_id); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/ams_mitm/source/ini.c b/stratosphere/ams_mitm/source/ini.c new file mode 100644 index 0000000..426430c --- /dev/null +++ b/stratosphere/ams_mitm/source/ini.c @@ -0,0 +1,269 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 72 +#define MAX_NAME 72 + +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to null at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size - 1); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; + int max_line = INI_MAX_LINE; +#else + char* line; + int max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC + char* new_line; + int offset; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_INITIAL_ALLOC); + if (!line) { + return -2; + } +#endif + +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + + /* Scan through stream line by line */ + while (reader(line, max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = realloc(line, max_line); + if (!new_line) { + free(line); + return -2; + } + line = new_line; + if (reader(line + offset, max_line - offset, stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (strchr(INI_START_COMMENT_PREFIXES, *start)) { + /* Start-of-line comment */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = lskip(value); + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!HANDLER(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +/* An ini_reader function to read the next line from a string buffer. This + is the fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +} diff --git a/stratosphere/ams_mitm/source/ini.h b/stratosphere/ams_mitm/source/ini.h new file mode 100644 index 0000000..f45ba40 --- /dev/null +++ b/stratosphere/ams_mitm/source/ini.h @@ -0,0 +1,130 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + +/* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); +#endif + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +int ini_parse_string(const char* string, ini_handler handler, void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Chars that begin a start-of-line comment. Per Python configparser, allow + both ; and # comments at the start of a line by default. */ +#ifndef INI_START_COMMENT_PREFIXES +#define INI_START_COMMENT_PREFIXES ";#" +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp new file mode 100644 index 0000000..b2ca562 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 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 "setmitm_main.hpp" +#include "setsys_mitm_service.hpp" +#include "setsys_settings_items.hpp" + +#include "../utils.hpp" + +struct SetSysManagerOptions { + static const size_t PointerBufferSize = 0x100; + static const size_t MaxDomains = 4; + static const size_t MaxDomainObjects = 0x100; +}; + +using SetMitmManager = WaitableManager; + +void SetMitmMain(void *arg) { + /* Wait for SD to initialize. */ + Utils::WaitSdInitialized(); + + /* Load settings */ + SettingsItemManager::LoadConfiguration(); + + /* Create server manager */ + auto server_manager = new SetMitmManager(3); + + /* Create set:sys mitm. */ + AddMitmServerToManager(server_manager, "set:sys", 60); + + /* Loop forever, servicing our services. */ + server_manager->Process(); + + delete server_manager; + +} + diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_main.hpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_main.hpp new file mode 100644 index 0000000..5c09004 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_main.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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 + +constexpr u32 SetMitmPriority = 43; +constexpr u32 SetMitmStackSize = 0x8000; + +void SetMitmMain(void *arg); \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_firmware_version.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_firmware_version.cpp new file mode 100644 index 0000000..b37c172 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_firmware_version.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 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 "setsys_firmware_version.hpp" + +static HosMutex g_version_mutex; +static bool g_got_version = false; +static SetSysFirmwareVersion g_fw_version = {0}; + +Result VersionManager::GetFirmwareVersion(u64 title_id, SetSysFirmwareVersion *out) { + std::scoped_lock lock(g_version_mutex); + if (!g_got_version) { + Result rc = setsysGetFirmwareVersion(&g_fw_version); + if (R_FAILED(rc)) { + return rc; + } + + /* Modify the output firmware version. */ + { + u32 major, minor, micro; + char display_version[sizeof(g_fw_version.display_version)] = {0}; + + GetAtmosphereApiVersion(&major, &minor, µ, nullptr, nullptr); + snprintf(display_version, sizeof(display_version), "%s (AMS %u.%u.%u)", g_fw_version.display_version, major, minor, micro); + + memcpy(g_fw_version.display_version, display_version, sizeof(g_fw_version.display_version)); + } + + g_got_version = true; + } + + /* Report atmosphere string to qlaunch, maintenance and nothing else. */ + if (title_id == 0x0100000000001000ULL || title_id == 0x0100000000001015ULL) { + *out = g_fw_version; + return 0; + } else { + return setsysGetFirmwareVersion(out); + } +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_firmware_version.hpp b/stratosphere/ams_mitm/source/set_mitm/setsys_firmware_version.hpp new file mode 100644 index 0000000..de035ed --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_firmware_version.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018 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 VersionManager { + public: + static Result GetFirmwareVersion(u64 title_id, SetSysFirmwareVersion *out); +}; diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp new file mode 100644 index 0000000..e12e3d5 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018 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 "setsys_mitm_service.hpp" +#include "setsys_firmware_version.hpp" +#include "setsys_settings_items.hpp" + +void SetSysMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) { + /* No commands need postprocessing. */ +} + +Result SetSysMitmService::GetFirmwareVersion(OutPointerWithServerSize out) { + Result rc = VersionManager::GetFirmwareVersion(this->title_id, out.pointer); + + /* GetFirmwareVersion sanitizes these fields. */ + if (R_SUCCEEDED(rc)) { + out.pointer->revision_major = 0; + out.pointer->revision_minor = 0; + } + + return rc; +} + +Result SetSysMitmService::GetFirmwareVersion2(OutPointerWithServerSize out) { + return VersionManager::GetFirmwareVersion(this->title_id, out.pointer); +} + +Result SetSysMitmService::GetSettingsItemValueSize(Out out_size, InPointer in_name, InPointer in_key) { + char name[SET_MAX_NAME_SIZE] = {0}; + char key[SET_MAX_NAME_SIZE] = {0}; + + Result rc = SettingsItemManager::ValidateName(in_name.pointer); + if (R_FAILED(rc)) { + return rc; + } + + rc = SettingsItemManager::ValidateKey(in_key.pointer); + if (R_FAILED(rc)) { + return rc; + } + + if (in_name.num_elements < SET_MAX_NAME_SIZE) { + strncpy(name, in_name.pointer, in_name.num_elements); + } else { + strncpy(name, in_name.pointer, SET_MAX_NAME_SIZE-1); + } + + if (in_key.num_elements < SET_MAX_NAME_SIZE) { + strncpy(key, in_key.pointer, in_key.num_elements); + } else { + strncpy(key, in_key.pointer, SET_MAX_NAME_SIZE-1); + } + + rc = SettingsItemManager::GetValueSize(name, key, out_size.GetPointer()); + if (R_FAILED(rc)) { + rc = setsysGetSettingsItemValueSize(name, key, out_size.GetPointer()); + } + + return rc; +} + +Result SetSysMitmService::GetSettingsItemValue(Out out_size, OutBuffer out_value, InPointer in_name, InPointer in_key) { + char name[SET_MAX_NAME_SIZE] = {0}; + char key[SET_MAX_NAME_SIZE] = {0}; + + Result rc = SettingsItemManager::ValidateName(in_name.pointer); + if (R_FAILED(rc)) { + return rc; + } + + rc = SettingsItemManager::ValidateKey(in_key.pointer); + if (R_FAILED(rc)) { + return rc; + } + + if (out_value.buffer == nullptr) { + return 0x19A69; + } + + if (in_name.num_elements < SET_MAX_NAME_SIZE) { + strncpy(name, in_name.pointer, in_name.num_elements); + } else { + strncpy(name, in_name.pointer, SET_MAX_NAME_SIZE-1); + } + + if (in_key.num_elements < SET_MAX_NAME_SIZE) { + strncpy(key, in_key.pointer, in_key.num_elements); + } else { + strncpy(key, in_key.pointer, SET_MAX_NAME_SIZE-1); + } + + rc = SettingsItemManager::GetValue(name, key, out_value.buffer, out_value.num_elements, out_size.GetPointer()); + + if (R_FAILED(rc)) { + rc = setsysGetSettingsItemValueFwd(this->forward_service.get(), name, key, out_value.buffer, out_value.num_elements, out_size.GetPointer()); + } + + return rc; +} + +Result SetSysMitmService::GetEdid(OutPointerWithServerSize out) { + return setsysGetEdidFwd(this->forward_service.get(), out.pointer); +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp new file mode 100644 index 0000000..36d12e0 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 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 "setsys_shim.h" + +enum SetSysCmd : u32 { + SetSysCmd_GetFirmwareVersion = 3, + SetSysCmd_GetFirmwareVersion2 = 4, + SetSysCmd_GetSettingsItemValueSize = 37, + SetSysCmd_GetSettingsItemValue = 38, + + /* Commands for which set:sys *must* act as a passthrough. */ + /* TODO: Solve the relevant IPC detection problem. */ + SetSysCmd_GetEdid = 41, +}; + +class SetSysMitmService : public IMitmServiceObject { + public: + SetSysMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { + /* ... */ + } + + static bool ShouldMitm(u64 pid, u64 tid) { + /* Mitm everything. */ + return true; + } + + static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx); + + protected: + /* Overridden commands. */ + Result GetFirmwareVersion(OutPointerWithServerSize out); + Result GetFirmwareVersion2(OutPointerWithServerSize out); + Result GetSettingsItemValueSize(Out out_size, InPointer name, InPointer key); + Result GetSettingsItemValue(Out out_size, OutBuffer out_value, InPointer name, InPointer key); + + /* Forced passthrough commands. */ + Result GetEdid(OutPointerWithServerSize out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_settings_items.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_settings_items.cpp new file mode 100644 index 0000000..cd845c4 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_settings_items.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2018 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 + +#include "setsys_settings_items.hpp" +#include "../utils.hpp" +#include "../ini.h" + +struct SettingsItemValue { + size_t size; + u8 *data; +}; + +std::map g_settings_items; + +static bool g_threw_fatal = false; +static HosThread g_fatal_thread; + +static void FatalThreadFunc(void *arg) { + Result rc = (Result)((uintptr_t)arg); + + svcSleepThread(5000000000ULL); + + fatalSimple(rc); +} + +static bool IsCorrectFormat(const char *str, size_t len) { + if (len > 0 && str[len - 1] == '.') { + return false; + } + + for (size_t i = 0; i < len; i++) { + const char c = *(str++); + + if ('a' <= c && c <= 'z') { + continue; + } + + if ('0' <= c && c <= '9') { + continue; + } + + if (c == '-' || c == '.' || c == '_') { + continue; + } + + return false; + } + + return true; +} + +Result SettingsItemManager::ValidateName(const char *name, size_t max_size) { + if (name == nullptr) { + return 0x19269; + } + + const size_t name_len = strnlen(name, std::min(max_size, MaxNameLength + 1)); + if (name_len == 0) { + return 0x1BA69; + } else if (name_len > MaxNameLength) { + return 0x1E269; + } + + if (!IsCorrectFormat(name, name_len)) { + return 0x20A69; + } + + return 0x0; +} + +Result SettingsItemManager::ValidateName(const char *name) { + return ValidateName(name, MaxNameLength + 1); +} + +Result SettingsItemManager::ValidateKey(const char *key, size_t max_size) { + if (key == nullptr) { + return 0x19469; + } + + const size_t key_len = strnlen(key, std::min(max_size, MaxKeyLength + 1)); + if (key_len == 0) { + return 0x1BC69; + } else if (key_len > MaxKeyLength) { + return 0x1E469; + } + + if (!IsCorrectFormat(key, key_len)) { + return 0x20C69; + } + + return 0x0; +} + +Result SettingsItemManager::ValidateKey(const char *key) { + return ValidateKey(key, MaxKeyLength + 1); +} + +static bool IsHexadecimal(const char *str) { + while (*str) { + if (isxdigit(*str)) { + str++; + } else { + return false; + } + } + return true; +} + +static char hextoi(char c) { + if ('a' <= c && c <= 'f') return c - 'a' + 0xA; + if ('A' <= c && c <= 'F') return c - 'A' + 0xA; + if ('0' <= c && c <= '9') return c - '0'; + return 0; +} + +static Result ParseValue(const char *name, const char *key, const char *val_tup) { + const char *delimiter = strchr(val_tup, '!'); + const char *value_str = delimiter + 1; + const char *type = val_tup; + + if (delimiter == NULL) { + return 0x20E69; + } + + while (isspace(*type) && type != delimiter) { + type++; + } + + size_t type_len = delimiter - type; + size_t value_len = strlen(value_str); + if (delimiter == NULL || value_len == 0 || type_len == 0) { + return 0x20E69; + } + + std::string kv = std::string(name) + "!" + std::string(key); + SettingsItemValue value; + + if (strncasecmp(type, "str", type_len) == 0 || strncasecmp(type, "string", type_len) == 0) { + /* String */ + value.size = value_len + 1; + value.data = reinterpret_cast(strdup(value_str)); + if (value.data == nullptr) { + return 0xCC69; + } + } else if (strncasecmp(type, "hex", type_len) == 0 || strncasecmp(type, "bytes", type_len) == 0) { + /* hex */ + if (value_len % 2 || !IsHexadecimal(value_str)) { + return 0x20E69; + } + value.size = value_len / 2; + u8 *data = reinterpret_cast(malloc(value.size)); + if (data == nullptr) { + return 0xCC69; + } + + memset(data, 0, value.size); + for (size_t i = 0; i < value_len; i++) { + data[i >> 1] |= hextoi(value_str[i]) << (4 * (i & 1)); + } + + value.data = data; + } else if (strncasecmp(type, "u8", type_len) == 0) { + /* u8 */ + value.size = sizeof(u8); + u8 *data = reinterpret_cast(malloc(value.size)); + if (data == nullptr) { + return 0xCC69; + } + *data = (u8)(strtoul(value_str, nullptr, 0)); + value.data = reinterpret_cast(data); + } else if (strncasecmp(type, "u16", type_len) == 0) { + /* u16 */ + value.size = sizeof(u16); + u16 *data = reinterpret_cast(malloc(value.size)); + if (data == nullptr) { + return 0xCC69; + } + *data = (u16)(strtoul(value_str, nullptr, 0)); + value.data = reinterpret_cast(data); + } else if (strncasecmp(type, "u32", type_len) == 0) { + /* u32 */ + value.size = sizeof(u32); + u32 *data = reinterpret_cast(malloc(value.size)); + if (data == nullptr) { + return 0xCC69; + } + *data = (u32)(strtoul(value_str, nullptr, 0)); + value.data = reinterpret_cast(data); + } else if (strncasecmp(type, "u64", type_len) == 0) { + /* u64 */ + value.size = sizeof(u64); + u64 *data = reinterpret_cast(malloc(value.size)); + if (data == nullptr) { + return 0xCC69; + } + *data = (u64)(strtoul(value_str, nullptr, 0)); + value.data = reinterpret_cast(data); + } else { + return 0x20E69; + } + + g_settings_items[kv] = value; + return 0x0; +} + +static int SettingsItemIniHandler(void *user, const char *name, const char *key, const char *value) { + Result rc = *(reinterpret_cast(user)); + ON_SCOPE_EXIT { *(reinterpret_cast(user)) = rc; }; + + if (R_SUCCEEDED(rc)) { + rc = SettingsItemManager::ValidateName(name); + } + if (R_SUCCEEDED(rc)) { + rc = SettingsItemManager::ValidateKey(name); + } + if (R_SUCCEEDED(rc)) { + rc = ParseValue(name, key, value); + } + + return R_SUCCEEDED(rc) ? 1 : 0; +} + +void SettingsItemManager::LoadConfiguration() { + /* Open file. */ + FsFile config_file; + Result rc = Utils::OpenSdFile("/atmosphere/system_settings.ini", FS_OPEN_READ, &config_file); + if (R_FAILED(rc)) { + return; + } + ON_SCOPE_EXIT { + fsFileClose(&config_file); + }; + + /* Allocate buffer. */ + char *config_buf = new char[0x10000]; + std::memset(config_buf, 0, 0x10000); + ON_SCOPE_EXIT { + delete config_buf; + }; + + /* Read from file. */ + if (R_SUCCEEDED(rc)) { + size_t actual_size; + rc = fsFileRead(&config_file, 0, config_buf, 0xFFFF, &actual_size); + } + + if (R_SUCCEEDED(rc)) { + ini_parse_string(config_buf, SettingsItemIniHandler, &rc); + } + + /* Report error if we encountered one. */ + if (R_FAILED(rc) && !g_threw_fatal) { + g_threw_fatal = true; + g_fatal_thread.Initialize(&FatalThreadFunc, reinterpret_cast(rc), 0x1000, 49); + g_fatal_thread.Start(); + } +} + +Result SettingsItemManager::GetValueSize(const char *name, const char *key, u64 *out_size) { + std::string kv = std::string(name) + "!" + std::string(key); + + auto it = g_settings_items.find(kv); + if (it == g_settings_items.end()) { + return 0x1669; + } + + *out_size = it->second.size; + return 0x0; +} + +Result SettingsItemManager::GetValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size) { + std::string kv = std::string(name) + "!" + std::string(key); + + auto it = g_settings_items.find(kv); + if (it == g_settings_items.end()) { + return 0x1669; + } + + size_t copy_size = it->second.size; + if (max_size < copy_size) { + copy_size = max_size; + } + *out_size = copy_size; + + memcpy(out, it->second.data, copy_size); + return 0x0; +} diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_settings_items.hpp b/stratosphere/ams_mitm/source/set_mitm/setsys_settings_items.hpp new file mode 100644 index 0000000..8e2a81c --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_settings_items.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 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 SettingsItemManager { + public: + static constexpr size_t MaxNameLength = 64; + static constexpr size_t MaxKeyLength = 64; + public: + static Result ValidateName(const char *name, size_t max_size); + static Result ValidateName(const char *name); + + static Result ValidateKey(const char *key, size_t max_size); + static Result ValidateKey(const char *key); + + static void LoadConfiguration(); + static Result GetValueSize(const char *name, const char *key, u64 *out_size); + static Result GetValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size); +}; diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c new file mode 100644 index 0000000..6526494 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 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 "setsys_shim.h" + +/* Command forwarders. */ +Result setsysGetEdidFwd(Service* s, SetSysEdid* out) { + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvStatic(&c, out, sizeof(*out), 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 41; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result setsysGetSettingsItemValueFwd(Service *s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out) { + char send_name[SET_MAX_NAME_SIZE]; + char send_item_key[SET_MAX_NAME_SIZE]; + + memset(send_name, 0, SET_MAX_NAME_SIZE); + memset(send_item_key, 0, SET_MAX_NAME_SIZE); + strncpy(send_name, name, SET_MAX_NAME_SIZE-1); + strncpy(send_item_key, item_key, SET_MAX_NAME_SIZE-1); + + IpcCommand c; + ipcInitialize(&c); + ipcAddSendStatic(&c, send_name, SET_MAX_NAME_SIZE, 0); + ipcAddSendStatic(&c, send_item_key, SET_MAX_NAME_SIZE, 0); + ipcAddRecvBuffer(&c, value_out, value_out_size, 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 38; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u64 size_out; + } *resp; + + serviceIpcParse(s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc)) { + *size_out = resp->size_out; + } + } + + return rc; +} diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_shim.h b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.h new file mode 100644 index 0000000..c2b5f73 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.h @@ -0,0 +1,24 @@ +/** + * @file setsys_shim.h + * @brief System Settings Services (set:sys) IPC wrapper. To be merged into libnx, eventually. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char edid[0x100]; +} SetSysEdid; + +/* Command forwarders. */ +Result setsysGetEdidFwd(Service* s, SetSysEdid* out); +Result setsysGetSettingsItemValueFwd(Service* s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/sha256.c b/stratosphere/ams_mitm/source/sha256.c new file mode 100644 index 0000000..02d40a7 --- /dev/null +++ b/stratosphere/ams_mitm/source/sha256.c @@ -0,0 +1,113 @@ +/* Based on linux source code */ +/* + * sha256_base.h - core logic for SHA-256 implementations + * + * Copyright (C) 2015 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "sha256.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) + +void sha256_block_data_order (uint32_t *ctx, const void *in, size_t num); + +int sha256_init(struct sha256_state *sctx) +{ + sctx->state[0] = SHA256_H0; + sctx->state[1] = SHA256_H1; + sctx->state[2] = SHA256_H2; + sctx->state[3] = SHA256_H3; + sctx->state[4] = SHA256_H4; + sctx->state[5] = SHA256_H5; + sctx->state[6] = SHA256_H6; + sctx->state[7] = SHA256_H7; + sctx->count = 0; + + return 0; +} + +int sha256_update(struct sha256_state *sctx, + const void *data, + size_t len) +{ + const u8 *data8 = (const u8 *)data; + unsigned int len32 = (unsigned int)len; + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->count += len32; + + if (unlikely((partial + len32) >= SHA256_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA256_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data8, p); + data8 += p; + len32 -= p; + + sha256_block_data_order(sctx->state, sctx->buf, 1); + } + + blocks = len32 / SHA256_BLOCK_SIZE; + len32 %= SHA256_BLOCK_SIZE; + + if (blocks) { + sha256_block_data_order(sctx->state, data8, blocks); + data8 += blocks * SHA256_BLOCK_SIZE; + } + partial = 0; + } + if (len32) + memcpy(sctx->buf + partial, data8, len32); + + return 0; +} + +int sha256_finalize(struct sha256_state *sctx) +{ + const int bit_offset = SHA256_BLOCK_SIZE - sizeof(u64); + u64 *bits = (u64 *)(sctx->buf + bit_offset); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial); + partial = 0; + + sha256_block_data_order(sctx->state, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + *bits = __builtin_bswap64(sctx->count << 3); + sha256_block_data_order(sctx->state, sctx->buf, 1); + + return 0; +} + +int sha256_finish(struct sha256_state *sctx, void *out) +{ + unsigned int digest_size = 32; + u32 *digest = (u32 *)out; + int i; + + // Switch: misalignment shouldn't be a problem here... + for (i = 0; digest_size > 0; i++, digest_size -= sizeof(u32)) + *digest++ = __builtin_bswap32(sctx->state[i]); + + *sctx = (struct sha256_state){}; + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/ams_mitm/source/sha256.h b/stratosphere/ams_mitm/source/sha256.h new file mode 100644 index 0000000..cfb09f8 --- /dev/null +++ b/stratosphere/ams_mitm/source/sha256.h @@ -0,0 +1,36 @@ +#pragma once + +/* Based on linux source code */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +#define SHA256_H0 0x6a09e667UL +#define SHA256_H1 0xbb67ae85UL +#define SHA256_H2 0x3c6ef372UL +#define SHA256_H3 0xa54ff53aUL +#define SHA256_H4 0x510e527fUL +#define SHA256_H5 0x9b05688cUL +#define SHA256_H6 0x1f83d9abUL +#define SHA256_H7 0x5be0cd19UL + +struct sha256_state { + u32 state[SHA256_DIGEST_SIZE / 4]; + u64 count; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +int sha256_init(struct sha256_state *sctx); +int sha256_update(struct sha256_state *sctx, const void *data, size_t len); +int sha256_finalize(struct sha256_state *sctx); +int sha256_finish(struct sha256_state *sctx, void *out); + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/ams_mitm/source/sha256_armv8.s b/stratosphere/ams_mitm/source/sha256_armv8.s new file mode 100644 index 0000000..0420d38 --- /dev/null +++ b/stratosphere/ams_mitm/source/sha256_armv8.s @@ -0,0 +1,163 @@ +.section .text.sha256_armv8, "ax", %progbits +.align 5 +.arch armv8-a+crypto + +# SHA256 assembly implementation for ARMv8 AArch64 (based on linux source code) + +.global sha256_block_data_order +.type sha256_block_data_order,%function +sha256_block_data_order: + +.Lsha256prolog: + + stp x29, x30, [sp,#-64]! + mov x29, sp + adr x3, .LKConstant256 + str q8, [sp, #16] + ld1 {v16.4s-v19.4s}, [x3], #64 + ld1 {v0.4s}, [x0], #16 + ld1 {v20.4s-v23.4s}, [x3], #64 + add x2, x1, x2, lsl #6 + ld1 {v1.4s}, [x0] + ld1 {v24.4s-v27.4s}, [x3], #64 + sub x0, x0, #16 + str q9, [sp, #32] + str q10, [sp, #48] + ld1 {v28.4s-v31.4s}, [x3], #64 + +.Lsha256loop: + + ld1 {v5.16b-v8.16b}, [x1], #64 + mov v2.16b, v0.16b + mov v3.16b, v1.16b + + rev32 v5.16b, v5.16b + rev32 v6.16b, v6.16b + add v9.4s, v5.4s, v16.4s + rev32 v7.16b, v7.16b + add v10.4s, v6.4s, v17.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + rev32 v8.16b, v8.16b + add v9.4s, v7.4s, v18.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + add v10.4s, v8.4s, v19.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v7.4s, v8.4s + sha256su1 v6.4s, v8.4s, v5.4s + add v9.4s, v5.4s, v20.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v8.4s, v5.4s + sha256su1 v7.4s, v5.4s, v6.4s + add v10.4s, v6.4s, v21.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + sha256su1 v8.4s, v6.4s, v7.4s + add v9.4s, v7.4s, v22.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + add v10.4s, v8.4s, v23.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v7.4s, v8.4s + sha256su1 v6.4s, v8.4s, v5.4s + add v9.4s, v5.4s, v24.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v8.4s, v5.4s + sha256su1 v7.4s, v5.4s, v6.4s + add v10.4s, v6.4s, v25.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v5.4s, v6.4s + sha256su1 v8.4s, v6.4s, v7.4s + add v9.4s, v7.4s, v26.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v6.4s, v7.4s + sha256su1 v5.4s, v7.4s, v8.4s + add v10.4s, v8.4s, v27.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su0 v7.4s, v8.4s + sha256su1 v6.4s, v8.4s, v5.4s + add v9.4s, v5.4s, v28.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + sha256su0 v8.4s, v5.4s + sha256su1 v7.4s, v5.4s, v6.4s + add v10.4s, v6.4s, v29.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + sha256su1 v8.4s, v6.4s, v7.4s + add v9.4s, v7.4s, v30.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + add v10.4s, v8.4s, v31.4s + mov v4.16b, v2.16b + sha256h q2, q3, v9.4s + sha256h2 q3, q4, v9.4s + mov v4.16b, v2.16b + sha256h q2, q3, v10.4s + sha256h2 q3, q4, v10.4s + cmp x1, x2 + add v1.4s, v1.4s, v3.4s + add v0.4s, v0.4s, v2.4s + b.ne .Lsha256loop + +.Lsha256epilog: + + st1 {v0.4s,v1.4s}, [x0] + ldr q10, [sp, #48] + ldr q9, [sp, #32] + ldr q8, [sp, #16] + ldr x29, [sp], #64 + ret + +.align 5 +.LKConstant256: +.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 +.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 +.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 +.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 +.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc +.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da +.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 +.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 +.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 +.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 +.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 +.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 +.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 +.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 +.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 +.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 + +.size sha256_block_data_order,.-sha256_block_data_order +.align 2 + + + diff --git a/stratosphere/ams_mitm/source/utils.cpp b/stratosphere/ams_mitm/source/utils.cpp new file mode 100644 index 0000000..8f3947d --- /dev/null +++ b/stratosphere/ams_mitm/source/utils.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2018 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 "debug.hpp" +#include "utils.hpp" +#include "ini.h" +#include "sha256.h" + +static FsFileSystem g_sd_filesystem = {0}; +static HosSignal g_sd_signal; + +static std::vector g_mitm_flagged_tids; +static std::vector g_disable_mitm_flagged_tids; +static std::atomic_bool g_has_initialized = false; +static std::atomic_bool g_has_hid_session = false; + +static u64 g_override_key_combination = KEY_R; +static u64 g_override_hbl_tid = 0x010000000000100DULL; +static bool g_override_any_app = false; +static bool g_override_by_default = true; + +/* Static buffer for loader.ini contents at runtime. */ +static char g_config_ini_data[0x800]; + +/* Backup file for CAL0 partition. */ +static constexpr size_t ProdinfoSize = 0x8000; +static FsFile g_cal0_file = {0}; +static u8 g_cal0_storage_backup[ProdinfoSize]; +static u8 g_cal0_backup[ProdinfoSize]; + +static bool IsHexadecimal(const char *str) { + while (*str) { + if (isxdigit(*str)) { + str++; + } else { + return false; + } + } + return true; +} + +void Utils::InitializeThreadFunc(void *args) { + /* Get required services. */ + Handle tmp_hnd = 0; + static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; + for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { + if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { + /* TODO: Panic */ + } else { + svcCloseHandle(tmp_hnd); + } + } + + /* Mount SD. */ + while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) { + svcSleepThread(1000000ULL); + } + + /* Back up CAL0, if it's not backed up already. */ + fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups"); + { + FsStorage cal0_storage; + if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdinfoSize))) { + std::abort(); + } + fsStorageClose(&cal0_storage); + + char serial_number[0x40] = {0}; + memcpy(serial_number, g_cal0_storage_backup + 0x250, 0x18); + + + char prodinfo_backup_path[FS_MAX_PATH] = {0}; + if (strlen(serial_number) > 0) { + snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/%s_PRODINFO.bin", serial_number); + } else { + snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin"); + } + + fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) { + bool has_auto_backup = false; + size_t read = 0; + if (R_SUCCEEDED(fsFileRead(&g_cal0_file, 0, g_cal0_backup, sizeof(g_cal0_backup), &read)) && read == sizeof(g_cal0_backup)) { + bool is_cal0_valid = true; + is_cal0_valid &= memcmp(g_cal0_backup, "CAL0", 4) == 0; + is_cal0_valid &= memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0; + u32 cal0_size = ((u32 *)g_cal0_backup)[2]; + is_cal0_valid &= cal0_size + 0x40 <= ProdinfoSize; + if (is_cal0_valid) { + struct sha256_state sha_ctx; + u8 calc_hash[0x20]; + sha256_init(&sha_ctx); + sha256_update(&sha_ctx, g_cal0_backup + 0x40, cal0_size); + sha256_finalize(&sha_ctx); + sha256_finish(&sha_ctx, calc_hash); + is_cal0_valid &= memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0; + } + has_auto_backup = is_cal0_valid; + } + + if (!has_auto_backup) { + fsFileSetSize(&g_cal0_file, ProdinfoSize); + fsFileWrite(&g_cal0_file, 0, g_cal0_backup, ProdinfoSize); + fsFileFlush(&g_cal0_file); + } + + /* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */ + memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup)); + memset(g_cal0_backup, 0, sizeof(g_cal0_backup)); + } + } + + /* Check for MitM flags. */ + FsDir titles_dir; + if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) { + FsDirectoryEntry dir_entry; + FsFile f; + u64 read_entries; + while (R_SUCCEEDED((fsDirRead(&titles_dir, 0, &read_entries, 1, &dir_entry))) && read_entries == 1) { + if (strlen(dir_entry.name) == 0x10 && IsHexadecimal(dir_entry.name)) { + u64 title_id = strtoul(dir_entry.name, NULL, 16); + char title_path[FS_MAX_PATH] = {0}; + strcpy(title_path, "/atmosphere/titles/"); + strcat(title_path, dir_entry.name); + strcat(title_path, "/flags/fsmitm.flag"); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { + g_mitm_flagged_tids.push_back(title_id); + fsFileClose(&f); + } else { + /* TODO: Deprecate. */ + memset(title_path, 0, sizeof(title_path)); + strcpy(title_path, "/atmosphere/titles/"); + strcat(title_path, dir_entry.name); + strcat(title_path, "/fsmitm.flag"); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { + g_mitm_flagged_tids.push_back(title_id); + fsFileClose(&f); + } + } + + memset(title_path, 0, sizeof(title_path)); + strcpy(title_path, "/atmosphere/titles/"); + strcat(title_path, dir_entry.name); + strcat(title_path, "/flags/fsmitm_disable.flag"); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { + g_disable_mitm_flagged_tids.push_back(title_id); + fsFileClose(&f); + } else { + /* TODO: Deprecate. */ + memset(title_path, 0, sizeof(title_path)); + strcpy(title_path, "/atmosphere/titles/"); + strcat(title_path, dir_entry.name); + strcat(title_path, "/fsmitm_disable.flag"); + if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { + g_disable_mitm_flagged_tids.push_back(title_id); + fsFileClose(&f); + } + } + } + } + fsDirClose(&titles_dir); + } + + Utils::RefreshConfiguration(); + + /* Initialize set:sys. */ + setsysInitialize(); + + /* Signal SD is initialized. */ + g_has_initialized = true; + g_sd_signal.Signal(); + + /* Initialize HID. */ + { + + while (R_FAILED(hidInitialize())) { + svcSleepThread(1000000ULL); + } + + g_has_hid_session = true; + + hidExit(); + } + + svcExitThread(); +} + +bool Utils::IsSdInitialized() { + return g_has_initialized; +} + +void Utils::WaitSdInitialized() { + g_sd_signal.Wait(); +} + +bool Utils::IsHidAvailable() { + return g_has_hid_session; +} + +Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + return fsFsOpenFile(&g_sd_filesystem, fn, flags, out); +} + +Result Utils::OpenSdFileForAtmosphere(u64 title_id, const char *fn, int flags, FsFile *out) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + char path[FS_MAX_PATH]; + if (*fn == '/') { + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn); + } else { + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", title_id, fn); + } + return fsFsOpenFile(&g_sd_filesystem, path, flags, out); +} + +Result Utils::OpenRomFSSdFile(u64 title_id, const char *fn, int flags, FsFile *out) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + return OpenRomFSFile(&g_sd_filesystem, title_id, fn, flags, out); +} + +Result Utils::OpenSdDir(const char *path, FsDir *out) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + return fsFsOpenDirectory(&g_sd_filesystem, path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out); +} + +Result Utils::OpenSdDirForAtmosphere(u64 title_id, const char *path, FsDir *out) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + char safe_path[FS_MAX_PATH]; + if (*path == '/') { + snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx%s", title_id, path); + } else { + snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/%s", title_id, path); + } + return fsFsOpenDirectory(&g_sd_filesystem, safe_path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out); +} + +Result Utils::OpenRomFSSdDir(u64 title_id, const char *path, FsDir *out) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + return OpenRomFSDir(&g_sd_filesystem, title_id, path, out); +} + + +Result Utils::OpenRomFSFile(FsFileSystem *fs, u64 title_id, const char *fn, int flags, FsFile *out) { + char path[FS_MAX_PATH]; + if (*fn == '/') { + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs%s", title_id, fn); + } else { + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs/%s", title_id, fn); + } + return fsFsOpenFile(fs, path, flags, out); +} + +Result Utils::OpenRomFSDir(FsFileSystem *fs, u64 title_id, const char *path, FsDir *out) { + char safe_path[FS_MAX_PATH]; + if (*path == '/') { + snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/romfs%s", title_id, path); + } else { + snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/romfs/%s", title_id, path); + } + return fsFsOpenDirectory(fs, safe_path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out); +} + +bool Utils::HasSdRomfsContent(u64 title_id) { + /* Check for romfs.bin. */ + FsFile data_file; + if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(title_id, "romfs.bin", FS_OPEN_READ, &data_file))) { + fsFileClose(&data_file); + return true; + } + + /* Check for romfs folder with non-zero content. */ + FsDir dir; + if (R_FAILED(Utils::OpenRomFSSdDir(title_id, "", &dir))) { + return false; + } + ON_SCOPE_EXIT { + fsDirClose(&dir); + }; + + FsDirectoryEntry dir_entry; + u64 read_entries; + return R_SUCCEEDED(fsDirRead(&dir, 0, &read_entries, 1, &dir_entry)) && read_entries == 1; +} + +Result Utils::SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data, size_t size) { + if (!IsSdInitialized()) { + return 0xFA202; + } + + Result rc = 0; + + char path[FS_MAX_PATH]; + if (*fn == '/') { + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn); + } else { + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", title_id, fn); + } + + /* Unconditionally create. */ + FsFile f; + fsFsCreateFile(&g_sd_filesystem, path, size, 0); + + /* Try to open. */ + rc = fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ | FS_OPEN_WRITE, &f); + if (R_FAILED(rc)) { + return rc; + } + + /* Always close, if we opened. */ + ON_SCOPE_EXIT { + fsFileClose(&f); + }; + + /* Try to make it big enough. */ + rc = fsFileSetSize(&f, size); + if (R_FAILED(rc)) { + return rc; + } + + /* Try to write the data. */ + rc = fsFileWrite(&f, 0, data, size); + + return rc; +} + +bool Utils::IsHblTid(u64 tid) { + return (g_override_any_app && tid >= 0x0100000000010000ULL) || (!g_override_any_app && tid == g_override_hbl_tid); +} + +bool Utils::HasTitleFlag(u64 tid, const char *flag) { + if (IsSdInitialized()) { + FsFile f; + char flag_path[FS_MAX_PATH]; + + memset(flag_path, 0, sizeof(flag_path)); + snprintf(flag_path, sizeof(flag_path) - 1, "flags/%s.flag", flag); + if (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) { + fsFileClose(&f); + return true; + } + + /* TODO: Deprecate. */ + snprintf(flag_path, sizeof(flag_path) - 1, "%s.flag", flag); + if (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) { + fsFileClose(&f); + return true; + } + } + return false; +} + +bool Utils::HasGlobalFlag(const char *flag) { + if (IsSdInitialized()) { + FsFile f; + char flag_path[FS_MAX_PATH] = {0}; + snprintf(flag_path, sizeof(flag_path), "/atmosphere/flags/%s.flag", flag); + if (fsFsOpenFile(&g_sd_filesystem, flag_path, FS_OPEN_READ, &f)) { + fsFileClose(&f); + return true; + } + } + return false; +} + +bool Utils::HasHblFlag(const char *flag) { + char hbl_flag[FS_MAX_PATH] = {0}; + snprintf(hbl_flag, sizeof(hbl_flag), "hbl_%s", flag); + return HasGlobalFlag(hbl_flag); +} + +bool Utils::HasFlag(u64 tid, const char *flag) { + return HasTitleFlag(tid, flag) || (IsHblTid(tid) && HasHblFlag(flag)); +} + +bool Utils::HasSdMitMFlag(u64 tid) { + if (IsHblTid(tid)) { + return true; + } + + if (IsSdInitialized()) { + return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end(); + } + return false; +} + +bool Utils::HasSdDisableMitMFlag(u64 tid) { + if (IsSdInitialized()) { + return std::find(g_disable_mitm_flagged_tids.begin(), g_disable_mitm_flagged_tids.end(), tid) != g_disable_mitm_flagged_tids.end(); + } + return false; +} + +Result Utils::GetKeysDown(u64 *keys) { + if (!Utils::IsHidAvailable() || R_FAILED(hidInitialize())) { + return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); + } + + hidScanInput(); + *keys = hidKeysDown(CONTROLLER_P1_AUTO); + + hidExit(); + return 0x0; +} + +bool Utils::HasOverrideButton(u64 tid) { + if (tid < 0x0100000000010000ULL || !IsSdInitialized()) { + /* Disable button override disable for non-applications. */ + return true; + } + + /* Unconditionally refresh loader.ini contents. */ + RefreshConfiguration(); + + u64 kDown = 0; + bool keys_triggered = (R_SUCCEEDED(GetKeysDown(&kDown)) && ((kDown & g_override_key_combination) != 0)); + return IsSdInitialized() && (g_override_by_default ^ keys_triggered); +} + +static int FsMitMIniHandler(void *user, const char *section, const char *name, const char *value) { + /* Taken and modified, with love, from Rajkosto's implementation. */ + if (strcasecmp(section, "config") == 0) { + if (strcasecmp(name, "hbl_tid") == 0) { + if (strcasecmp(value, "app") == 0) { + g_override_any_app = true; + } + else { + u64 override_tid = strtoul(value, NULL, 16); + if (override_tid != 0) { + g_override_hbl_tid = override_tid; + } + } + } else if (strcasecmp(name, "override_key") == 0) { + if (value[0] == '!') { + g_override_by_default = true; + value++; + } else { + g_override_by_default = false; + } + + if (strcasecmp(value, "A") == 0) { + g_override_key_combination = KEY_A; + } else if (strcasecmp(value, "B") == 0) { + g_override_key_combination = KEY_B; + } else if (strcasecmp(value, "X") == 0) { + g_override_key_combination = KEY_X; + } else if (strcasecmp(value, "Y") == 0) { + g_override_key_combination = KEY_Y; + } else if (strcasecmp(value, "LS") == 0) { + g_override_key_combination = KEY_LSTICK; + } else if (strcasecmp(value, "RS") == 0) { + g_override_key_combination = KEY_RSTICK; + } else if (strcasecmp(value, "L") == 0) { + g_override_key_combination = KEY_L; + } else if (strcasecmp(value, "R") == 0) { + g_override_key_combination = KEY_R; + } else if (strcasecmp(value, "ZL") == 0) { + g_override_key_combination = KEY_ZL; + } else if (strcasecmp(value, "ZR") == 0) { + g_override_key_combination = KEY_ZR; + } else if (strcasecmp(value, "PLUS") == 0) { + g_override_key_combination = KEY_PLUS; + } else if (strcasecmp(value, "MINUS") == 0) { + g_override_key_combination = KEY_MINUS; + } else if (strcasecmp(value, "DLEFT") == 0) { + g_override_key_combination = KEY_DLEFT; + } else if (strcasecmp(value, "DUP") == 0) { + g_override_key_combination = KEY_DUP; + } else if (strcasecmp(value, "DRIGHT") == 0) { + g_override_key_combination = KEY_DRIGHT; + } else if (strcasecmp(value, "DDOWN") == 0) { + g_override_key_combination = KEY_DDOWN; + } else if (strcasecmp(value, "SL") == 0) { + g_override_key_combination = KEY_SL; + } else if (strcasecmp(value, "SR") == 0) { + g_override_key_combination = KEY_SR; + } else { + g_override_key_combination = 0; + } + } + } else { + return 0; + } + return 1; +} + + +void Utils::RefreshConfiguration() { + FsFile config_file; + if (R_FAILED(fsFsOpenFile(&g_sd_filesystem, "/atmosphere/loader.ini", FS_OPEN_READ, &config_file))) { + return; + } + + u64 size; + if (R_FAILED(fsFileGetSize(&config_file, &size))) { + return; + } + + size = std::min(size, (decltype(size))0x7FF); + + /* Read in string. */ + std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0); + size_t r_s; + fsFileRead(&config_file, 0, g_config_ini_data, size, &r_s); + fsFileClose(&config_file); + + ini_parse_string(g_config_ini_data, FsMitMIniHandler, NULL); +} \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/utils.hpp b/stratosphere/ams_mitm/source/utils.hpp new file mode 100644 index 0000000..e929bb1 --- /dev/null +++ b/stratosphere/ams_mitm/source/utils.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 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 BisStorageId : u32 { + BisStorageId_Boot0 = 0, + BisStorageId_Boot1 = 10, + BisStorageId_RawNand = 20, + BisStorageId_BcPkg2_1 = 21, + BisStorageId_BcPkg2_2 = 22, + BisStorageId_BcPkg2_3 = 23, + BisStorageId_BcPkg2_4 = 24, + BisStorageId_BcPkg2_5 = 25, + BisStorageId_BcPkg2_6 = 26, + BisStorageId_Prodinfo = 27, + BisStorageId_ProdinfoF = 28, + BisStorageId_Safe = 29, + BisStorageId_User = 30, + BisStorageId_System = 31, + BisStorageId_SystemProperEncryption = 32, + BisStorageId_SystemProperPartition = 33, +}; + +class Utils { + public: + static bool IsSdInitialized(); + static void WaitSdInitialized(); + + static Result OpenSdFile(const char *fn, int flags, FsFile *out); + static Result OpenSdFileForAtmosphere(u64 title_id, const char *fn, int flags, FsFile *out); + static Result OpenRomFSSdFile(u64 title_id, const char *fn, int flags, FsFile *out); + static Result OpenSdDir(const char *path, FsDir *out); + static Result OpenSdDirForAtmosphere(u64 title_id, const char *path, FsDir *out); + static Result OpenRomFSSdDir(u64 title_id, const char *path, FsDir *out); + + static Result OpenRomFSFile(FsFileSystem *fs, u64 title_id, const char *fn, int flags, FsFile *out); + static Result OpenRomFSDir(FsFileSystem *fs, u64 title_id, const char *path, FsDir *out); + + static Result SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data, size_t size); + + static bool HasSdRomfsContent(u64 title_id); + + /* Delayed Initialization + MitM detection. */ + static void InitializeThreadFunc(void *args); + + static bool IsHblTid(u64 tid); + + static bool HasTitleFlag(u64 tid, const char *flag); + static bool HasHblFlag(const char *flag); + static bool HasGlobalFlag(const char *flag); + static bool HasFlag(u64 tid, const char *flag); + + static bool HasSdMitMFlag(u64 tid); + static bool HasSdDisableMitMFlag(u64 tid); + + + static bool IsHidAvailable(); + static Result GetKeysDown(u64 *keys); + static bool HasOverrideButton(u64 tid); + private: + static void RefreshConfiguration(); +}; \ No newline at end of file diff --git a/stratosphere/eclct.stub/Makefile b/stratosphere/eclct.stub/Makefile new file mode 100644 index 0000000..2d3bf28 --- /dev/null +++ b/stratosphere/eclct.stub/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/eclct.stub/eclct.stub.json b/stratosphere/eclct.stub/eclct.stub.json new file mode 100644 index 0000000..120ab2e --- /dev/null +++ b/stratosphere/eclct.stub/eclct.stub.json @@ -0,0 +1,98 @@ +{ + "name": "eclct.stun", + "title_id": "0x0100000000000032", + "title_id_range_min": "0x0100000000000032", + "title_id_range_max": "0x0100000000000032", + "main_thread_stack_size": "0x00004000", + "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_host": [], + "service_access": [], + "kernel_capabilities": [ + { + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "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", + "svcCreateInterruptEvent": "0x53", + "svcReadWriteRegister": "0x4E", + "svcQueryIoMapping": "0x55", + "svcCreateDeviceAddressSpace": "0x56", + "svcAttachDeviceAddressSpace": "0x57", + "svcDetachDeviceAddressSpace": "0x58", + "svcMapDeviceAddressSpaceAligned": "0x5a", + "svcUnmapDeviceAddressSpace": "0x5c", + "svcGetSystemInfo": "0x6f", + "svcCallSecureMonitor": "0x7F" + } + }, + { + "type": "min_kernel_version", + "value": "0x0030" + } + ] +} \ No newline at end of file diff --git a/stratosphere/eclct.stub/source/eclct_stub.cpp b/stratosphere/eclct.stub/source/eclct_stub.cpp new file mode 100644 index 0000000..1324557 --- /dev/null +++ b/stratosphere/eclct.stub/source/eclct_stub.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 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 0x8000 + 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); +} + + +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) { + /* nothing to do */ +} + +void __appExit(void) { + /* nothing to do */ +} + +int main(int argc, char **argv) +{ + return 0; +} + diff --git a/stratosphere/fs_mitm/Makefile b/stratosphere/fs_mitm/Makefile deleted file mode 100644 index 4ebc8f6..0000000 --- a/stratosphere/fs_mitm/Makefile +++ /dev/null @@ -1,160 +0,0 @@ -#--------------------------------------------------------------------------------- -.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)\" - -#--------------------------------------------------------------------------------- -# 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).kip $(TARGET).elf - - -#--------------------------------------------------------------------------------- -else -.PHONY: all - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -all : $(OUTPUT).kip - -$(OUTPUT).kip : $(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/fs_mitm/fs_mitm.json b/stratosphere/fs_mitm/fs_mitm.json deleted file mode 100644 index 5171ce0..0000000 --- a/stratosphere/fs_mitm/fs_mitm.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "fs.mitm", - "title_id": "0x010041544D530000", - "main_thread_stack_size": "0x20000", - "main_thread_priority": 43, - "default_cpu_id": 3, - "process_category": 1, - "kernel_capabilities": [ - { - "type": "handle_table_size", - "value": 512 - }, - { - "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", - "svcCreateInterruptEvent": "0x53", - "svcReadWriteRegister": "0x4E", - "svcQueryIoMapping": "0x55", - "svcCreateDeviceAddressSpace": "0x56", - "svcAttachDeviceAddressSpace": "0x57", - "svcDetachDeviceAddressSpace": "0x58", - "svcMapDeviceAddressSpaceAligned": "0x5a", - "svcUnmapDeviceAddressSpace": "0x5c", - "svcGetSystemInfo": "0x6f", - "svcCallSecureMonitor": "0x7F" - } - } - ] -} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/debug.cpp b/stratosphere/fs_mitm/source/debug.cpp deleted file mode 100644 index 1531eff..0000000 --- a/stratosphere/fs_mitm/source/debug.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018 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 "debug.hpp" - -void Reboot() { - /* ... */ -} - -void Log(const void *data, int size) { - /* ... */ -} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/debug.hpp b/stratosphere/fs_mitm/source/debug.hpp deleted file mode 100644 index 0c1e21e..0000000 --- a/stratosphere/fs_mitm/source/debug.hpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2018 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 - -void Reboot(); -void Log(const void *data, int size); \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fs_istorage.hpp b/stratosphere/fs_mitm/source/fs_istorage.hpp deleted file mode 100644 index 82a7561..0000000 --- a/stratosphere/fs_mitm/source/fs_istorage.hpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018 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 "fs_shim.h" - -#include "debug.hpp" - -enum FsIStorageCmd : u32 { - FsIStorageCmd_Read = 0, - FsIStorageCmd_Write = 1, - FsIStorageCmd_Flush = 2, - FsIStorageCmd_SetSize = 3, - FsIStorageCmd_GetSize = 4, - FsIStorageCmd_OperateRange = 5, -}; - -class IStorage { - public: - virtual ~IStorage(); - - virtual Result Read(void *buffer, size_t size, u64 offset) = 0; - virtual Result Write(void *buffer, size_t size, u64 offset) = 0; - virtual Result Flush() = 0; - virtual Result SetSize(u64 size) = 0; - virtual Result GetSize(u64 *out_size) = 0; - virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; -}; - -class IStorageInterface : public IServiceObject { - private: - IStorage *base_storage; - public: - IStorageInterface(IStorage *s) : base_storage(s) { - /* ... */ - }; - - ~IStorageInterface() { - delete base_storage; - }; - - private: - /* Actual command API. */ - virtual Result Read(OutBuffer buffer, u64 offset, u64 size) final { - return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset); - }; - virtual Result Write(InBuffer buffer, u64 offset, u64 size) final { - return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset); - }; - virtual Result Flush() final { - return this->base_storage->Flush(); - }; - virtual Result SetSize(u64 size) final { - return this->base_storage->SetSize(size); - }; - virtual Result GetSize(Out size) final { - return this->base_storage->GetSize(size.GetPointer()); - }; - virtual Result OperateRange(Out range_info, u32 operation_type, u64 offset, u64 size) final { - return this->base_storage->OperateRange(operation_type, offset, size, range_info.GetPointer()); - }; - public: - DEFINE_SERVICE_DISPATCH_TABLE { - /* 1.0.0- */ - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - - /* 4.0.0- */ - MakeServiceCommandMeta(), - }; -}; - -class IROStorage : public IStorage { - public: - virtual Result Read(void *buffer, size_t size, u64 offset) = 0; - virtual Result Write(void *buffer, size_t size, u64 offset) final { - (void)(buffer); - (void)(offset); - (void)(size); - return 0x313802; - }; - virtual Result Flush() final { - return 0x0; - }; - virtual Result SetSize(u64 size) final { - (void)(size); - return 0x313802; - }; - virtual Result GetSize(u64 *out_size) = 0; - virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; -}; - - -class ProxyStorage : public IStorage { - private: - FsStorage *base_storage; - public: - ProxyStorage(FsStorage *s) : base_storage(s) { - /* ... */ - }; - ProxyStorage(FsStorage s) { - this->base_storage = new FsStorage(s); - }; - virtual ~ProxyStorage() { - fsStorageClose(base_storage); - delete base_storage; - }; - public: - virtual Result Read(void *buffer, size_t size, u64 offset) override { - return fsStorageRead(this->base_storage, offset, buffer, size); - }; - virtual Result Write(void *buffer, size_t size, u64 offset) override { - return fsStorageWrite(this->base_storage, offset, buffer, size); - }; - virtual Result Flush() override { - return fsStorageFlush(this->base_storage); - }; - virtual Result GetSize(u64 *out_size) override { - return fsStorageGetSize(this->base_storage, out_size); - }; - virtual Result SetSize(u64 size) override { - return fsStorageSetSize(this->base_storage, size); - }; - virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { - return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); - }; -}; - -class ROProxyStorage : public IROStorage { - private: - FsStorage *base_storage; - public: - ROProxyStorage(FsStorage *s) : base_storage(s) { - /* ... */ - }; - ROProxyStorage(FsStorage s) { - this->base_storage = new FsStorage(s); - }; - virtual ~ROProxyStorage() { - fsStorageClose(base_storage); - delete base_storage; - }; - public: - virtual Result Read(void *buffer, size_t size, u64 offset) override { - return fsStorageRead(this->base_storage, offset, buffer, size); - }; - virtual Result GetSize(u64 *out_size) override { - return fsStorageGetSize(this->base_storage, out_size); - }; - virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { - return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); - }; -}; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fs_shim.c b/stratosphere/fs_mitm/source/fs_shim.c deleted file mode 100644 index 915366b..0000000 --- a/stratosphere/fs_mitm/source/fs_shim.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2018 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 "fs_shim.h" - -/* Missing fsp-srv commands. */ -Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - u32 PartitionId; - } *raw; - - raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 12; - raw->PartitionId = PartitionId; - - Result rc = serviceIpcDispatch(s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - } *resp; - - serviceIpcParse(s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - serviceCreateSubservice(&out->s, s, &r, 0); - } - } - - return rc; -} - -Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 200; - - Result rc = serviceIpcDispatch(s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - } *resp; - - serviceIpcParse(s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - serviceCreateSubservice(&out->s, s, &r, 0); - } - } - - return rc; -} - -Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - FsStorageId storage_id; - u64 data_id; - } *raw; - - raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 202; - raw->storage_id = storage_id; - raw->data_id = data_id; - - Result rc = serviceIpcDispatch(s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - } *resp; - - serviceIpcParse(s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - - if (R_SUCCEEDED(rc)) { - serviceCreateSubservice(&out->s, s, &r, 0); - } - } - - return rc; -} - -/* Missing FS File commands. */ -Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - u32 op_id; - u64 off; - u64 len; - } *raw; - - raw = serviceIpcPrepareHeader(&f->s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 5; - raw->op_id = op_id; - raw->off = off; - raw->len = len; - - Result rc = serviceIpcDispatch(&f->s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - FsRangeInfo range_info; - } *resp; - - serviceIpcParse(&f->s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - if (R_SUCCEEDED(rc) && out) *out = resp->range_info; - } - - return rc; -} - -/* Missing FS Storage commands. */ -Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out) { - IpcCommand c; - ipcInitialize(&c); - - struct { - u64 magic; - u64 cmd_id; - u32 op_id; - u64 off; - u64 len; - } *raw; - - raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 5; - raw->op_id = op_id; - raw->off = off; - raw->len = len; - - Result rc = serviceIpcDispatch(&s->s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - FsRangeInfo range_info; - } *resp; - - serviceIpcParse(&s->s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - if (R_SUCCEEDED(rc) && out) *out = resp->range_info; - } - - return rc; -} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fs_shim.h b/stratosphere/fs_mitm/source/fs_shim.h deleted file mode 100644 index 730a2bf..0000000 --- a/stratosphere/fs_mitm/source/fs_shim.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file fs_shim.h - * @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually. - * @author SciresM - * @copyright libnx Authors - */ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* TODO: Reverse this more. */ -typedef struct { - u32 flags[0x40/sizeof(u32)]; -} FsRangeInfo; - -/* Missing fsp-srv commands. */ -Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId); -Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out); -Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out); - -/* Missing FS File commands. */ -Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out); - -/* Missing FS Storage commands. */ -Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_boot0storage.cpp b/stratosphere/fs_mitm/source/fsmitm_boot0storage.cpp deleted file mode 100644 index 749eae8..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_boot0storage.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_boot0storage.hpp" - -static HosMutex g_boot0_mutex; -static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset]; - -bool Boot0Storage::CanModifyBctPubks() { - return this->title_id != 0x010000000000001FULL; -} - -Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) { - std::scoped_lock lk{g_boot0_mutex}; - - return Base::Read(_buffer, size, offset); -} - -Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) { - std::scoped_lock lk{g_boot0_mutex}; - - Result rc = 0; - u8 *buffer = static_cast(_buffer); - - /* Protect the keyblob region from writes. */ - if (offset <= EksStart) { - if (offset + size < EksStart) { - /* Fall through, no need to do anything here. */ - } else { - if (offset + size < EksEnd) { - /* Adjust size to avoid writing end of data. */ - size = EksStart - offset; - } else { - /* Perform portion of write falling past end of keyblobs. */ - const u64 diff = EksEnd - offset; - if (R_FAILED((rc = Base::Write(buffer + diff, size - diff, EksEnd)))) { - return rc; - } - /* Adjust size to avoid writing end of data. */ - size = EksStart - offset; - } - } - } else { - if (offset < EksEnd) { - if (offset + size < EksEnd) { - /* Ignore writes falling strictly within the region. */ - return 0; - } else { - /* Only write past the end of the keyblob region. */ - buffer = buffer + (EksEnd - offset); - size -= (EksEnd - offset); - offset = EksEnd; - } - } else { - /* Fall through, no need to do anything here. */ - } - } - - if (size == 0) { - return 0; - } - - /* We care about protecting autorcm from NS. */ - if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) { - return Base::Write(buffer, size, offset); - } - - /* First, let's deal with the data past the end. */ - if (offset + size >= BctEndOffset) { - const u64 diff = BctEndOffset - offset; - if (R_FAILED((rc = ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset)))) { - return rc; - } - size = diff; - } - - /* Read in the current BCT region. */ - if (R_FAILED((rc = ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0)))) { - return rc; - } - - /* Update the bct buffer. */ - for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) { - const u64 cur_bct_rel_ofs = cur_ofs % BctSize; - if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) { - g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset]; - } - } - - return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0); -} diff --git a/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp b/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp deleted file mode 100644 index b86791f..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2018 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 - -#include "fs_istorage.hpp" - -/* Represents a sectored storage. */ -template -class SectoredProxyStorage : public ProxyStorage { - private: - u64 cur_seek = 0; - u64 cur_sector = 0; - u64 cur_sector_ofs = 0; - u8 sector_buf[SectorSize]; - private: - void Seek(u64 offset) { - this->cur_sector_ofs = offset % SectorSize; - this->cur_seek = offset - this->cur_sector_ofs; - } - public: - SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { } - SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { } - public: - virtual Result Read(void *_buffer, size_t size, u64 offset) override { - Result rc = 0; - u8 *buffer = static_cast(_buffer); - this->Seek(offset); - - if (this->cur_sector_ofs == 0 && size % SectorSize == 0) { - /* Fast case. */ - return ProxyStorage::Read(buffer, size, offset); - } - - if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) { - return rc; - } - - if (size + this->cur_sector_ofs <= SectorSize) { - memcpy(buffer, sector_buf + this->cur_sector_ofs, size); - } else { - /* Leaving the sector... */ - size_t ofs = SectorSize - this->cur_sector_ofs; - memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs); - size -= ofs; - - /* We're guaranteed alignment, here. */ - const size_t aligned_remaining_size = size - (size % SectorSize); - if (aligned_remaining_size) { - if (R_FAILED((rc = ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs)))) { - return rc; - } - ofs += aligned_remaining_size; - size -= aligned_remaining_size; - } - - /* Read any leftover data. */ - if (size) { - if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) { - return rc; - } - memcpy(buffer + ofs, sector_buf, size); - } - } - - return rc; - }; - virtual Result Write(void *_buffer, size_t size, u64 offset) override { - Result rc = 0; - u8 *buffer = static_cast(_buffer); - this->Seek(offset); - - if (this->cur_sector_ofs == 0 && size % SectorSize == 0) { - /* Fast case. */ - return ProxyStorage::Write(buffer, size, offset); - } - - if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) { - return rc; - } - - - if (size + this->cur_sector_ofs <= SectorSize) { - memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size); - rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek); - } else { - /* Leaving the sector... */ - size_t ofs = SectorSize - this->cur_sector_ofs; - memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs); - if (R_FAILED((rc = ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek)))) { - return rc; - } - size -= ofs; - - /* We're guaranteed alignment, here. */ - const size_t aligned_remaining_size = size - (size % SectorSize); - if (aligned_remaining_size) { - if (R_FAILED((rc = ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs)))) { - return rc; - } - ofs += aligned_remaining_size; - size -= aligned_remaining_size; - } - - /* Write any leftover data. */ - if (size) { - if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) { - return rc; - } - memcpy(this->sector_buf, buffer + ofs, size); - rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek); - } - } - - return rc; - }; -}; - -/* Represents an RCM-preserving BOOT0 partition. */ -class Boot0Storage : public SectoredProxyStorage<0x200> { - using Base = SectoredProxyStorage<0x200>; - - public: - static constexpr u64 BctEndOffset = 0xFC000; - static constexpr u64 BctSize = 0x4000; - static constexpr u64 BctPubkStart = 0x210; - static constexpr u64 BctPubkSize = 0x100; - static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize; - - static constexpr u64 EksStart = 0x180000; - static constexpr u64 EksSize = 0x4000; - static constexpr u64 EksEnd = EksStart + EksSize; - private: - u64 title_id; - private: - bool CanModifyBctPubks(); - public: - Boot0Storage(FsStorage *s, u64 t) : Base(s), title_id(t) { } - Boot0Storage(FsStorage s, u64 t) : Base(s), title_id(t) { } - public: - virtual Result Read(void *_buffer, size_t size, u64 offset) override; - virtual Result Write(void *_buffer, size_t size, u64 offset) override; -}; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp b/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp deleted file mode 100644 index f6a7918..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_layeredrom.hpp" -#include "fsmitm_utils.hpp" -#include "debug.hpp" - -IStorage::~IStorage() = default; - -LayeredRomFS::LayeredRomFS(std::shared_ptr s_r, std::shared_ptr f_r, u64 tid) : storage_romfs(s_r), file_romfs(f_r), title_id(tid) { - /* Start building the new virtual romfs. */ - RomFSBuildContext build_ctx(this->title_id); - this->p_source_infos = std::shared_ptr>(new std::vector(), [](std::vector *to_delete) { - for (unsigned int i = 0; i < to_delete->size(); i++) { - (*to_delete)[i].Cleanup(); - } - delete to_delete; - }); - if (Utils::IsSdInitialized()) { - build_ctx.MergeSdFiles(); - } - if (this->file_romfs) { - build_ctx.MergeRomStorage(this->file_romfs.get(), RomFSDataSource::FileRomFS); - } - if (this->storage_romfs) { - build_ctx.MergeRomStorage(this->storage_romfs.get(), RomFSDataSource::BaseRomFS); - } - build_ctx.Build(this->p_source_infos.get()); -} - - -Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) { - /* Size zero reads should always succeed. */ - if (size == 0) { - return 0; - } - - /* Validate size. */ - u64 virt_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size; - if (offset >= virt_size) { - return 0x2F5A02; - } - if (virt_size - offset < size) { - size = virt_size - offset; - } - /* Find first source info via binary search. */ - u32 cur_source_ind = 0; - u32 low = 0, high = this->p_source_infos->size() - 1; - while (low <= high) { - u32 mid = (low + high) / 2; - if ((*this->p_source_infos)[mid].virtual_offset > offset) { - /* Too high. */ - high = mid - 1; - } else { - /* sources[mid].virtual_offset <= offset, invariant */ - if (mid == this->p_source_infos->size() - 1 || (*this->p_source_infos)[mid + 1].virtual_offset > offset) { - /* Success */ - cur_source_ind = mid; - break; - } - low = mid + 1; - } - } - - Result rc; - size_t read_so_far = 0; - while (read_so_far < size) { - RomFSSourceInfo *cur_source = &((*this->p_source_infos)[cur_source_ind]); - if (cur_source->virtual_offset + cur_source->size > offset) { - u64 cur_read_size = size - read_so_far; - if (cur_read_size > cur_source->size - (offset - cur_source->virtual_offset)) { - cur_read_size = cur_source->size - (offset - cur_source->virtual_offset); - } - switch (cur_source->type) { - case RomFSDataSource::MetaData: - { - FsFile file; - if (R_FAILED((rc = Utils::OpenSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file)))) { - fatalSimple(rc); - } - size_t out_read; - if (R_FAILED((rc = fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, &out_read)))) { - fatalSimple(rc); - } - if (out_read != cur_read_size) { - Reboot(); - } - fsFileClose(&file); - } - break; - case RomFSDataSource::LooseFile: - { - FsFile file; - if (R_FAILED((rc = Utils::OpenRomFSSdFile(this->title_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file)))) { - fatalSimple(rc); - } - size_t out_read; - if (R_FAILED((rc = fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, &out_read)))) { - fatalSimple(rc); - } - if (out_read != cur_read_size) { - Reboot(); - } - fsFileClose(&file); - } - break; - case RomFSDataSource::Memory: - { - memcpy((void *)((uintptr_t)buffer + read_so_far), cur_source->memory_source_info.data + (offset - cur_source->virtual_offset), cur_read_size); - } - break; - case RomFSDataSource::BaseRomFS: - { - if (R_FAILED((rc = this->storage_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset))))) { - /* TODO: Can this ever happen? */ - /* fatalSimple(rc); */ - return rc; - } - } - break; - case RomFSDataSource::FileRomFS: - { - if (R_FAILED((rc = this->file_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset))))) { - fatalSimple(rc); - } - } - break; - default: - fatalSimple(0xF601); - } - read_so_far += cur_read_size; - offset += cur_read_size; - } else { - /* Handle padding explicitly. */ - cur_source_ind++; - /* Zero out the padding we skip, here. */ - memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset); - read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset; - offset = ((*this->p_source_infos)[cur_source_ind]).virtual_offset; - } - } - - return 0; -} -Result LayeredRomFS::GetSize(u64 *out_size) { - *out_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size; - return 0x0; -} -Result LayeredRomFS::OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) { - /* TODO: How should I implement this for a virtual romfs? */ - if (operation_type == 3) { - *out_range_info = {0}; - } - return 0; -} diff --git a/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp b/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp deleted file mode 100644 index 01be3af..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_romstorage.hpp" -#include "fsmitm_romfsbuild.hpp" -#include "fsmitm_utils.hpp" - - -/* Represents a merged RomFS. */ -class LayeredRomFS : public IROStorage { - private: - /* Data Sources. */ - std::shared_ptr storage_romfs; - std::shared_ptr file_romfs; - /* Information about the merged RomFS. */ - u64 title_id; - std::shared_ptr> p_source_infos; - - public: - LayeredRomFS(std::shared_ptr s_r, std::shared_ptr f_r, u64 tid); - virtual ~LayeredRomFS() = default; - - virtual Result Read(void *buffer, size_t size, u64 offset) override; - virtual Result GetSize(u64 *out_size) override; - virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override; -}; diff --git a/stratosphere/fs_mitm/source/fsmitm_main.cpp b/stratosphere/fs_mitm/source/fsmitm_main.cpp deleted file mode 100644 index 24fa8b6..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_main.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_service.hpp" - -#include "fsmitm_utils.hpp" - -extern "C" { - extern u32 __start__; - - u32 __nx_applet_type = AppletType_None; - - #define INNER_HEAP_SIZE 0x1000000 - 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); -} - - -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; - - rc = smInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); - } - - rc = fsInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); - } - - CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); -} - -void __appExit(void) { - /* Cleanup services. */ - fsExit(); - smExit(); -} - -struct FsMitmManagerOptions { - static const size_t PointerBufferSize = 0x800; - static const size_t MaxDomains = 0x40; - static const size_t MaxDomainObjects = 0x4000; -}; -using FsMitmManager = WaitableManager; - -int main(int argc, char **argv) -{ - Thread sd_initializer_thread = {0}; - Thread hid_initializer_thread = {0}; - consoleDebugInit(debugDevice_SVC); - - /* TODO: What's a good timeout value to use here? */ - auto server_manager = new FsMitmManager(5); - - /* Create fsp-srv mitm. */ - AddMitmServerToManager(server_manager, "fsp-srv", 61); - - if (R_FAILED(threadCreate(&sd_initializer_thread, &Utils::InitializeSdThreadFunc, NULL, 0x4000, 0x15, 0))) { - /* TODO: Panic. */ - } - if (R_FAILED(threadStart(&sd_initializer_thread))) { - /* TODO: Panic. */ - } - - if (R_FAILED(threadCreate(&hid_initializer_thread, &Utils::InitializeHidThreadFunc, NULL, 0x4000, 0x15, 0))) { - /* TODO: Panic. */ - } - if (R_FAILED(threadStart(&hid_initializer_thread))) { - /* TODO: Panic. */ - } - - /* Loop forever, servicing our services. */ - server_manager->Process(); - - delete server_manager; - - return 0; -} - diff --git a/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp b/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp deleted file mode 100644 index 7b63b2b..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_utils.hpp" -#include "fsmitm_romfsbuild.hpp" - -#include "debug.hpp" - -void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent) { - FsDir dir; - Result rc; - - std::vector child_dirs; - - /* Open the current parent directory. */ - if (R_FAILED((rc = Utils::OpenRomFSDir(filesys, this->title_id, parent->path, &dir)))) { - fatalSimple(rc); - } - - u64 read_entries; - while (R_SUCCEEDED((rc = fsDirRead(&dir, 0, &read_entries, 1, &this->dir_entry))) && read_entries == 1) { - if (this->dir_entry.type == ENTRYTYPE_DIR) { - RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0}); - /* Set child's path. */ - child->cur_path_ofs = parent->path_len + 1; - child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name); - child->path = new char[child->path_len + 1]; - strcpy(child->path, parent->path); - if (child->path_len > FS_MAX_PATH - 1) { - fatalSimple(0xF601); - } - strcat(child->path + parent->path_len, "/"); - strcat(child->path + parent->path_len, this->dir_entry.name); - - if (!this->AddDirectory(parent, child, NULL)) { - delete child->path; - delete child; - } else { - child_dirs.push_back(child); - } - } else if (this->dir_entry.type == ENTRYTYPE_FILE) { - RomFSBuildFileContext *child = new RomFSBuildFileContext({0}); - /* Set child's path. */ - child->cur_path_ofs = parent->path_len + 1; - child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name); - child->path = new char[child->path_len + 1]; - strcpy(child->path, parent->path); - if (child->path_len > FS_MAX_PATH - 1) { - fatalSimple(0xF601); - } - strcat(child->path + parent->path_len, "/"); - strcat(child->path + parent->path_len, this->dir_entry.name); - - child->source = this->cur_source_type; - - child->size = this->dir_entry.fileSize; - - if (!this->AddFile(parent, child)) { - delete child->path; - delete child; - } - } else { - fatalSimple(rc); - } - } - fsDirClose(&dir); - - for (auto &child : child_dirs) { - this->VisitDirectory(filesys, child); - } -} - -void RomFSBuildContext::MergeSdFiles() { - FsFileSystem sd_filesystem; - FsDir dir; - if (!Utils::IsSdInitialized()) { - return; - } - if (R_FAILED((Utils::OpenSdDirForAtmosphere(this->title_id, "/romfs", &dir)))) { - return; - } - fsDirClose(&dir); - if (R_FAILED(fsMountSdcard(&sd_filesystem))) { - return; - } - this->cur_source_type = RomFSDataSource::LooseFile; - this->VisitDirectory(&sd_filesystem, this->root); - fsFsClose(&sd_filesystem); -} - -void RomFSBuildContext::VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size) { - RomFSDirectoryEntry *parent_entry = romfs_get_direntry(dir_table, parent_offset); - if (parent_entry->file != ROMFS_ENTRY_EMPTY) { - RomFSFileEntry *cur_file = romfs_get_fentry(file_table, parent_entry->file); - while (cur_file != NULL) { - RomFSBuildFileContext *child = new RomFSBuildFileContext({0}); - /* Set child's path. */ - child->cur_path_ofs = parent->path_len + 1; - child->path_len = child->cur_path_ofs + cur_file->name_size; - child->path = new char[child->path_len + 1]; - strcpy(child->path, parent->path); - if (child->path_len > FS_MAX_PATH - 1) { - fatalSimple(0xF601); - } - strcat(child->path + parent->path_len, "/"); - strncat(child->path + parent->path_len, cur_file->name, cur_file->name_size); - child->size = cur_file->size; - - child->source = this->cur_source_type; - child->orig_offset = cur_file->offset; - if (!this->AddFile(parent, child)) { - delete child->path; - delete child; - } - if (cur_file->sibling == ROMFS_ENTRY_EMPTY) { - cur_file = NULL; - } else { - cur_file = romfs_get_fentry(file_table, cur_file->sibling); - } - } - } - if (parent_entry->child != ROMFS_ENTRY_EMPTY) { - RomFSDirectoryEntry *cur_child = romfs_get_direntry(dir_table, parent_entry->child); - u32 cur_child_offset = parent_entry->child; - while (cur_child != NULL) { - RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0}); - /* Set child's path. */ - child->cur_path_ofs = parent->path_len + 1; - child->path_len = child->cur_path_ofs + cur_child->name_size; - child->path = new char[child->path_len + 1]; - strcpy(child->path, parent->path); - if (child->path_len > FS_MAX_PATH - 1) { - fatalSimple(0xF601); - } - strcat(child->path + parent->path_len, "/"); - strncat(child->path + parent->path_len, cur_child->name, cur_child->name_size); - - RomFSBuildDirectoryContext *real = NULL; - if (!this->AddDirectory(parent, child, &real)) { - delete child->path; - delete child; - } - if (real == NULL) { - fatalSimple(0xF601); - } - - this->VisitDirectory(real, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size); - - if (cur_child->sibling == ROMFS_ENTRY_EMPTY) { - cur_child = NULL; - } else { - cur_child_offset = cur_child->sibling; - cur_child = romfs_get_direntry(dir_table, cur_child->sibling); - } - } - } -} - -void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) { - Result rc; - RomFSHeader header; - if (R_FAILED((rc = storage->Read(&header, sizeof(header), 0)))) { - fatalSimple(rc); - } - if (header.header_size != sizeof(header)) { - /* what */ - return; - } - - /* Read tables. */ - auto dir_table = std::make_unique(header.dir_table_size); - auto file_table = std::make_unique(header.file_table_size); - if (R_FAILED((rc = storage->Read(dir_table.get(), header.dir_table_size, header.dir_table_ofs)))) { - fatalSimple(rc); - } - if (R_FAILED((rc = storage->Read(file_table.get(), header.file_table_size, header.file_table_ofs)))) { - fatalSimple(rc); - } - - this->cur_source_type = source; - this->VisitDirectory(this->root, 0x0, dir_table.get(), (size_t)header.dir_table_size, file_table.get(), (size_t)header.file_table_size); -} - -bool RomFSBuildContext::AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx) { - /* Check whether it's already in the known directories. */ - auto existing = this->directories.find(dir_ctx->path); - if (existing != this->directories.end()) { - if (out_dir_ctx) { - *out_dir_ctx = existing->second; - } - return false; - } - - /* Add a new directory. */ - this->num_dirs++; - this->dir_table_size += sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3); - dir_ctx->parent = parent_dir_ctx; - this->directories.insert({dir_ctx->path, dir_ctx}); - - if (out_dir_ctx) { - *out_dir_ctx = dir_ctx; - } - return true; -} - -bool RomFSBuildContext::AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx) { - /* Check whether it's already in the known files. */ - auto existing = this->files.find(file_ctx->path); - if (existing != this->files.end()) { - return false; - } - - /* Add a new file. */ - this->num_files++; - this->file_table_size += sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3); - file_ctx->parent = parent_dir_ctx; - this->files.insert({file_ctx->path, file_ctx}); - - return true; -} - -void RomFSBuildContext::Build(std::vector *out_infos) { - RomFSBuildFileContext *cur_file; - RomFSBuildDirectoryContext *cur_dir; - u32 entry_offset; - - u32 dir_hash_table_entry_count = romfs_get_hash_table_count(this->num_dirs); - u32 file_hash_table_entry_count = romfs_get_hash_table_count(this->num_files); - this->dir_hash_table_size = 4 * dir_hash_table_entry_count; - this->file_hash_table_size = 4 * file_hash_table_entry_count; - - /* Assign metadata pointers */ - RomFSHeader *header = new RomFSHeader({0}); - u8 *metadata = new u8[this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size]; - u32 *dir_hash_table = (u32 *)((uintptr_t)metadata); - RomFSDirectoryEntry *dir_table = (RomFSDirectoryEntry *)((uintptr_t)dir_hash_table + this->dir_hash_table_size); - u32 *file_hash_table = (u32 *)((uintptr_t)dir_table + this->dir_table_size); - RomFSFileEntry *file_table = (RomFSFileEntry *)((uintptr_t)file_hash_table + this->file_hash_table_size); - - /* Clear out hash tables. */ - for (u32 i = 0; i < dir_hash_table_entry_count; i++) { - dir_hash_table[i] = ROMFS_ENTRY_EMPTY; - } - for (u32 i = 0; i < file_hash_table_entry_count; i++) { - file_hash_table[i] = ROMFS_ENTRY_EMPTY; - } - - out_infos->clear(); - out_infos->emplace_back(0, sizeof(*header), header, RomFSDataSource::Memory); - - /* Determine file offsets. */ - entry_offset = 0; - RomFSBuildFileContext *prev_file = NULL; - for (const auto &it : this->files) { - cur_file = it.second; - this->file_partition_size = (this->file_partition_size + 0xFULL) & ~0xFULL; - /* Check for extra padding in the original romfs source and preserve it, to help ourselves later. */ - if (prev_file && prev_file->source == cur_file->source && (prev_file->source == RomFSDataSource::BaseRomFS || prev_file->source == RomFSDataSource::FileRomFS)) { - u64 expected = (this->file_partition_size - prev_file->offset + prev_file->orig_offset); - if (expected != cur_file->orig_offset) { - if (expected > cur_file->orig_offset) { - /* This case should NEVER happen. */ - fatalSimple(0xF601); - } - this->file_partition_size += cur_file->orig_offset - expected; - } - } - cur_file->offset = this->file_partition_size; - this->file_partition_size += cur_file->size; - cur_file->entry_offset = entry_offset; - entry_offset += sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3); - prev_file = cur_file; - } - /* Assign deferred parent/sibling ownership. */ - for (auto it = this->files.rbegin(); it != this->files.rend(); it++) { - cur_file = it->second; - cur_file->sibling = cur_file->parent->file; - cur_file->parent->file = cur_file; - } - - /* Determine directory offsets. */ - entry_offset = 0; - for (const auto &it : this->directories) { - cur_dir = it.second; - cur_dir->entry_offset = entry_offset; - entry_offset += sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3); - } - /* Assign deferred parent/sibling ownership. */ - for (auto it = this->directories.rbegin(); it->second != this->root; it++) { - cur_dir = it->second; - cur_dir->sibling = cur_dir->parent->child; - cur_dir->parent->child = cur_dir; - } - - - /* Populate file tables. */ - for (const auto &it : this->files) { - cur_file = it.second; - RomFSFileEntry *cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset); - - cur_entry->parent = cur_file->parent->entry_offset; - cur_entry->sibling = (cur_file->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset; - cur_entry->offset = cur_file->offset; - cur_entry->size = cur_file->size; - - u32 name_size = cur_file->path_len - cur_file->cur_path_ofs; - u32 hash = romfs_calc_path_hash(cur_file->parent->entry_offset, (unsigned char *)cur_file->path + cur_file->cur_path_ofs, 0, name_size); - cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count]; - file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset; - - cur_entry->name_size = name_size; - memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3); - memcpy(cur_entry->name, cur_file->path + cur_file->cur_path_ofs, name_size); - - switch (cur_file->source) { - case RomFSDataSource::BaseRomFS: - case RomFSDataSource::FileRomFS: - /* Try to compact, if possible. */ - if (out_infos->back().type == cur_file->source) { - out_infos->back().size = cur_file->offset + ROMFS_FILEPARTITION_OFS + cur_file->size - out_infos->back().virtual_offset; - } else { - out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, cur_file->orig_offset + ROMFS_FILEPARTITION_OFS, cur_file->source); - } - break; - case RomFSDataSource::LooseFile: - { - char *path = new char[cur_file->path_len + 1]; - strcpy(path, cur_file->path); - out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, path, cur_file->source); - } - break; - default: - fatalSimple(0xF601); - } - } - - /* Populate dir tables. */ - for (const auto &it : this->directories) { - cur_dir = it.second; - RomFSDirectoryEntry *cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset); - cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset; - cur_entry->sibling = (cur_dir->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset; - cur_entry->child = (cur_dir->child == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset; - cur_entry->file = (cur_dir->file == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset; - - u32 name_size = cur_dir->path_len - cur_dir->cur_path_ofs; - u32 hash = romfs_calc_path_hash(cur_dir == this->root ? 0 : cur_dir->parent->entry_offset, (unsigned char *)cur_dir->path + cur_dir->cur_path_ofs, 0, name_size); - cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count]; - dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset; - - cur_entry->name_size = name_size; - memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3); - memcpy(cur_entry->name, cur_dir->path + cur_dir->cur_path_ofs, name_size); - - } - - /* Delete directories. */ - for (const auto &it : this->directories) { - cur_dir = it.second; - delete cur_dir->path; - delete cur_dir; - } - this->root = NULL; - this->directories.clear(); - - /* Delete files. */ - for (const auto &it : this->files) { - cur_file = it.second; - delete cur_file->path; - delete cur_file; - } - this->files.clear(); - - /* Set header fields. */ - header->header_size = sizeof(*header); - header->file_hash_table_size = this->file_hash_table_size; - header->file_table_size = this->file_table_size; - header->dir_hash_table_size = this->dir_hash_table_size; - header->dir_table_size = this->dir_table_size; - header->file_partition_ofs = ROMFS_FILEPARTITION_OFS; - header->dir_hash_table_ofs = (header->file_partition_ofs + this->file_partition_size + 3ULL) & ~3ULL; - header->dir_table_ofs = header->dir_hash_table_ofs + header->dir_hash_table_size; - header->file_hash_table_ofs = header->dir_table_ofs + header->dir_table_size; - header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size; - - const size_t metadata_size = this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size; - - /* Try to save metadata to the SD card, to save on memory space. */ - if (R_SUCCEEDED(Utils::SaveSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, metadata, metadata_size))) { - out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, RomFSDataSource::MetaData); - delete metadata; - } else { - out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, metadata, RomFSDataSource::Memory); - } - -} diff --git a/stratosphere/fs_mitm/source/fsmitm_romfsbuild.hpp b/stratosphere/fs_mitm/source/fsmitm_romfsbuild.hpp deleted file mode 100644 index 4da8b30..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_romfsbuild.hpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_romstorage.hpp" - -#include "debug.hpp" - -#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF -#define ROMFS_FILEPARTITION_OFS 0x200 - -#define ROMFS_METADATA_FILE_PATH "romfs_metadata.bin" - -/* Types for RomFS Meta construction. */ -enum class RomFSDataSource { - BaseRomFS, - FileRomFS, - LooseFile, - MetaData, - Memory, -}; - -struct RomFSBaseSourceInfo { - u64 offset; -}; - -struct RomFSFileSourceInfo { - u64 offset; -}; - -struct RomFSLooseSourceInfo { - const char *path; -}; - -struct RomFSMemorySourceInfo { - const u8 *data; -}; - -struct RomFSMetaDataSourceInfo { - -}; - -struct RomFSSourceInfo { - u64 virtual_offset; - u64 size; - union { - RomFSBaseSourceInfo base_source_info; - RomFSFileSourceInfo file_source_info; - RomFSLooseSourceInfo loose_source_info; - RomFSMemorySourceInfo memory_source_info; - RomFSMemorySourceInfo metadata_source_info; - }; - RomFSDataSource type; - - RomFSSourceInfo(u64 v_o, u64 s, u64 offset, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) { - switch (this->type) { - case RomFSDataSource::BaseRomFS: - this->base_source_info.offset = offset; - break; - case RomFSDataSource::FileRomFS: - this->file_source_info.offset = offset; - break; - case RomFSDataSource::LooseFile: - case RomFSDataSource::MetaData: - case RomFSDataSource::Memory: - default: - fatalSimple(0xF601); - } - } - - RomFSSourceInfo(u64 v_o, u64 s, const void *arg, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) { - switch (this->type) { - case RomFSDataSource::LooseFile: - this->loose_source_info.path = (decltype(this->loose_source_info.path))arg; - break; - case RomFSDataSource::Memory: - this->memory_source_info.data = (decltype(this->memory_source_info.data))arg; - break; - case RomFSDataSource::MetaData: - case RomFSDataSource::BaseRomFS: - case RomFSDataSource::FileRomFS: - default: - fatalSimple(0xF601); - } - } - - RomFSSourceInfo(u64 v_o, u64 s, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) { - switch (this->type) { - case RomFSDataSource::MetaData: - break; - case RomFSDataSource::LooseFile: - case RomFSDataSource::Memory: - case RomFSDataSource::BaseRomFS: - case RomFSDataSource::FileRomFS: - default: - fatalSimple(0xF601); - } - } - - void Cleanup() { - switch (this->type) { - case RomFSDataSource::BaseRomFS: - case RomFSDataSource::FileRomFS: - case RomFSDataSource::MetaData: - break; - case RomFSDataSource::LooseFile: - delete this->loose_source_info.path; - break; - case RomFSDataSource::Memory: - delete this->memory_source_info.data; - break; - default: - fatalSimple(0xF601); - } - } - - static bool Compare(RomFSSourceInfo *a, RomFSSourceInfo *b) { - return (a->virtual_offset < b->virtual_offset); - } -}; - -/* Types for building a RomFS. */ -struct RomFSHeader { - u64 header_size; - u64 dir_hash_table_ofs; - u64 dir_hash_table_size; - u64 dir_table_ofs; - u64 dir_table_size; - u64 file_hash_table_ofs; - u64 file_hash_table_size; - u64 file_table_ofs; - u64 file_table_size; - u64 file_partition_ofs; -}; - -static_assert(sizeof(RomFSHeader) == 0x50, "Incorrect RomFS Header definition!"); - -struct RomFSDirectoryEntry { - u32 parent; - u32 sibling; - u32 child; - u32 file; - u32 hash; - u32 name_size; - char name[]; -}; - -static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "Incorrect RomFSDirectoryEntry definition!"); - -struct RomFSFileEntry { - u32 parent; - u32 sibling; - u64 offset; - u64 size; - u32 hash; - u32 name_size; - char name[]; -}; - -static_assert(sizeof(RomFSFileEntry) == 0x20, "Incorrect RomFSFileEntry definition!"); - -struct RomFSBuildFileContext; - -/* Used as comparator for std::map */ -struct build_ctx_cmp { - bool operator()(const char *a, const char *b) const { - return strcmp(a, b) < 0; - } -}; - -struct RomFSBuildDirectoryContext { - char *path; - u32 cur_path_ofs; - u32 path_len; - u32 entry_offset = 0; - RomFSBuildDirectoryContext *parent = NULL; - RomFSBuildDirectoryContext *child = NULL; - RomFSBuildDirectoryContext *sibling = NULL; - RomFSBuildFileContext *file = NULL; -}; - -struct RomFSBuildFileContext { - char *path; - u32 cur_path_ofs; - u32 path_len; - u32 entry_offset = 0; - u64 offset = 0; - u64 size = 0; - RomFSBuildDirectoryContext *parent = NULL; - RomFSBuildFileContext *sibling = NULL; - RomFSDataSource source{0}; - u64 orig_offset = 0; -}; - -class RomFSBuildContext { - private: - u64 title_id; - RomFSBuildDirectoryContext *root; - std::map directories; - std::map files; - u64 num_dirs = 0; - u64 num_files = 0; - u64 dir_table_size = 0; - u64 file_table_size = 0; - u64 dir_hash_table_size = 0; - u64 file_hash_table_size = 0; - u64 file_partition_size = 0; - - FsDirectoryEntry dir_entry; - RomFSDataSource cur_source_type; - - void VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent); - void VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size); - - bool AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx); - bool AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx); - public: - RomFSBuildContext(u64 tid) : title_id(tid) { - this->root = new RomFSBuildDirectoryContext({0}); - this->root->path = new char[1]; - this->root->path[0] = '\x00'; - this->directories.insert({this->root->path, this->root}); - this->num_dirs = 1; - this->dir_table_size = 0x18; - } - - void MergeSdFiles(); - void MergeRomStorage(IROStorage *storage, RomFSDataSource source); - - /* This finalizes the context. */ - void Build(std::vector *out_infos); -}; - - -static inline RomFSDirectoryEntry *romfs_get_direntry(void *directories, uint32_t offset) { - return (RomFSDirectoryEntry *)((uintptr_t)directories + offset); -} - -static inline RomFSFileEntry *romfs_get_fentry(void *files, uint32_t offset) { - return (RomFSFileEntry *)((uintptr_t)files + offset); -} - -static inline uint32_t romfs_calc_path_hash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) { - uint32_t hash = parent ^ 123456789; - for (uint32_t i = 0; i < path_len; i++) { - hash = (hash >> 5) | (hash << 27); - hash ^= path[start + i]; - } - - return hash; -} - -static inline uint32_t romfs_get_hash_table_count(uint32_t num_entries) { - if (num_entries < 3) { - return 3; - } else if (num_entries < 19) { - return num_entries | 1; - } - uint32_t count = num_entries; - while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) { - count++; - } - return count; -} diff --git a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp deleted file mode 100644 index 1a186ec..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018 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 "fs_istorage.hpp" - -/* Represents a RomFS stored in some file. */ -class RomFileStorage : public IROStorage { - private: - FsFile *base_file; - public: - RomFileStorage(FsFile *f) : base_file(f) { - /* ... */ - }; - RomFileStorage(FsFile f) { - this->base_file = new FsFile(f); - }; - virtual ~RomFileStorage() { - fsFileClose(base_file); - delete base_file; - }; - public: - Result Read(void *buffer, size_t size, u64 offset) override { - size_t out_sz = 0; - Result rc = fsFileRead(this->base_file, offset, buffer, size, &out_sz); - if (R_SUCCEEDED(rc) && out_sz != size && out_sz) { - return this->Read((void *)((uintptr_t)buffer + out_sz), size - out_sz, offset + out_sz); - } - return rc; - }; - Result GetSize(u64 *out_size) override { - return fsFileGetSize(this->base_file, out_size); - }; - Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { - /* TODO: Merge into libnx? */ - return fsFileOperateRange(this->base_file, operation_type, offset, size, out_range_info); - }; -}; - -/* Represents a RomFS accessed via some IStorage. */ -using RomInterfaceStorage = ROProxyStorage; diff --git a/stratosphere/fs_mitm/source/fsmitm_service.cpp b/stratosphere/fs_mitm/source/fsmitm_service.cpp deleted file mode 100644 index 0b1e1cf..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_service.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2018 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 "fsmitm_service.hpp" -#include "fs_shim.h" - -#include "fsmitm_utils.hpp" -#include "fsmitm_boot0storage.hpp" -#include "fsmitm_romstorage.hpp" -#include "fsmitm_layeredrom.hpp" - -#include "debug.hpp" - -static HosMutex g_StorageCacheLock; -static std::unordered_map> g_StorageCache; - -static bool StorageCacheGetEntry(u64 title_id, std::shared_ptr *out) { - std::scoped_lock lock(g_StorageCacheLock); - if (g_StorageCache.find(title_id) == g_StorageCache.end()) { - return false; - } - - auto intf = g_StorageCache[title_id].lock(); - if (intf != nullptr) { - *out = intf; - return true; - } - return false; -} - -static void StorageCacheSetEntry(u64 title_id, std::shared_ptr *ptr) { - std::scoped_lock lock(g_StorageCacheLock); - - /* Ensure we always use the cached copy if present. */ - if (g_StorageCache.find(title_id) != g_StorageCache.end()) { - auto intf = g_StorageCache[title_id].lock(); - if (intf != nullptr) { - *ptr = intf; - } - } - - g_StorageCache[title_id] = *ptr; -} - -void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) { - auto this_ptr = static_cast(obj); - switch ((FspSrvCmd)ctx->cmd_id) { - case FspSrvCmd_SetCurrentProcess: - if (R_SUCCEEDED(ctx->rc)) { - this_ptr->has_initialized = true; - this_ptr->process_id = ctx->request.Pid; - this_ptr->title_id = this_ptr->process_id; - if (R_FAILED(MitmQueryUtils::GetAssociatedTidForPid(this_ptr->process_id, &this_ptr->title_id))) { - /* Log here, if desired. */ - } - break; - } - break; - default: - break; - } -} - -/* Gate access to the BIS partitions. */ -Result FsMitmService::OpenBisStorage(Out> out_storage, u32 bis_partition_id) { - std::shared_ptr storage = nullptr; - u32 out_domain_id = 0; - Result rc = 0; - - ON_SCOPE_EXIT { - if (R_SUCCEEDED(rc)) { - out_storage.SetValue(std::move(storage)); - if (out_storage.IsDomain()) { - out_storage.ChangeObjectId(out_domain_id); - } - } - }; - - { - FsStorage bis_storage; - rc = fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id); - if (R_SUCCEEDED(rc)) { - const bool is_sysmodule = this->title_id < 0x0100000000001000; - const bool has_bis_write_flag = Utils::HasFlag(this->title_id, "bis_write"); - const bool has_cal0_read_flag = Utils::HasFlag(this->title_id, "cal_read"); - if (bis_partition_id == BisStorageId_Boot0) { - storage = std::make_shared(new Boot0Storage(bis_storage, this->title_id)); - } else if (bis_partition_id == BisStorageId_Prodinfo) { - /* PRODINFO should *never* be writable. */ - if (is_sysmodule || has_cal0_read_flag) { - storage = std::make_shared(new ROProxyStorage(bis_storage)); - } else { - /* Do not allow non-sysmodules to read *or* write CAL0. */ - fsStorageClose(&bis_storage); - return 0x320002; - } - } else { - if (is_sysmodule || has_bis_write_flag) { - /* Sysmodules should still be allowed to read and write. */ - storage = std::make_shared(new ProxyStorage(bis_storage)); - } else { - /* Non-sysmodules should be allowed to read. */ - storage = std::make_shared(new ROProxyStorage(bis_storage)); - } - } - if (out_storage.IsDomain()) { - out_domain_id = bis_storage.s.object_id; - } - } - } - - return rc; -} - -/* Add redirection for RomFS to the SD card. */ -Result FsMitmService::OpenDataStorageByCurrentProcess(Out> out_storage) { - std::shared_ptr storage = nullptr; - u32 out_domain_id = 0; - Result rc = 0; - - if (!this->should_override_contents) { - return RESULT_FORWARD_TO_SESSION; - } - - bool has_cache = StorageCacheGetEntry(this->title_id, &storage); - - ON_SCOPE_EXIT { - if (R_SUCCEEDED(rc)) { - if (!has_cache) { - StorageCacheSetEntry(this->title_id, &storage); - } - - out_storage.SetValue(std::move(storage)); - if (out_storage.IsDomain()) { - out_storage.ChangeObjectId(out_domain_id); - } - } - }; - - - if (has_cache) { - if (out_storage.IsDomain()) { - FsStorage s = {0}; - rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &s); - if (R_SUCCEEDED(rc)) { - out_domain_id = s.s.object_id; - } - } else { - rc = 0; - } - if (R_FAILED(rc)) { - storage.reset(); - } - } else { - FsStorage data_storage; - FsFile data_file; - - rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage); - - Log(armGetTls(), 0x100); - if (R_SUCCEEDED(rc)) { - if (Utils::HasSdRomfsContent(this->title_id)) { - /* TODO: Is there a sensible path that ends in ".romfs" we can use?" */ - if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(this->title_id, "romfs.bin", FS_OPEN_READ, &data_file))) { - storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), this->title_id)); - } else { - storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, this->title_id)); - } - if (out_storage.IsDomain()) { - out_domain_id = data_storage.s.object_id; - } - } else { - /* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */ - fsStorageClose(&data_storage); - rc = RESULT_FORWARD_TO_SESSION; - } - } - } - - return rc; -} - -/* Add redirection for System Data Archives to the SD card. */ -Result FsMitmService::OpenDataStorageByDataId(Out> out_storage, u64 data_id, u8 sid) { - FsStorageId storage_id = (FsStorageId)sid; - FsStorage data_storage; - FsFile data_file; - - if (!this->should_override_contents) { - return RESULT_FORWARD_TO_SESSION; - } - - std::shared_ptr storage = nullptr; - u32 out_domain_id = 0; - Result rc = 0; - - bool has_cache = StorageCacheGetEntry(data_id, &storage); - - ON_SCOPE_EXIT { - if (R_SUCCEEDED(rc)) { - if (!has_cache) { - StorageCacheSetEntry(data_id, &storage); - } - - out_storage.SetValue(std::move(storage)); - if (out_storage.IsDomain()) { - out_storage.ChangeObjectId(out_domain_id); - } - } - }; - - if (has_cache) { - if (out_storage.IsDomain()) { - FsStorage s = {0}; - rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &s); - if (R_SUCCEEDED(rc)) { - out_domain_id = s.s.object_id; - } - } else { - rc = 0; - } - if (R_FAILED(rc)) { - storage.reset(); - } - } else { - rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &data_storage); - - if (R_SUCCEEDED(rc)) { - if (Utils::HasSdRomfsContent(data_id)) { - /* TODO: Is there a sensible path that ends in ".romfs" we can use?" */ - if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(data_id, "romfs.bin", FS_OPEN_READ, &data_file))) { - storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), data_id)); - } else { - storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, data_id)); - } - if (out_storage.IsDomain()) { - out_domain_id = data_storage.s.object_id; - } - } else { - /* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */ - fsStorageClose(&data_storage); - rc = RESULT_FORWARD_TO_SESSION; - } - } - } - - return rc; -} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_service.hpp b/stratosphere/fs_mitm/source/fsmitm_service.hpp deleted file mode 100644 index e7d2ad5..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_service.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018 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 "fs_istorage.hpp" -#include "fsmitm_utils.hpp" - -enum FspSrvCmd : u32 { - FspSrvCmd_SetCurrentProcess = 1, - FspSrvCmd_OpenBisStorage = 12, - FspSrvCmd_OpenDataStorageByCurrentProcess = 200, - FspSrvCmd_OpenDataStorageByDataId = 202, -}; - -class FsMitmService : public IMitmServiceObject { - private: - bool has_initialized = false; - bool should_override_contents; - public: - FsMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { - if (Utils::HasSdDisableMitMFlag(this->title_id)) { - this->should_override_contents = false; - } else { - this->should_override_contents = (this->title_id >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id); - } - } - - static bool ShouldMitm(u64 pid, u64 tid) { - /* Don't Mitm KIPs */ - if (pid < 0x50) { - return false; - } - - static std::atomic_bool has_launched_qlaunch = false; - - /* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */ - /* Figure out why, and address it. */ - if (tid == 0x0100000000001000ULL) { - has_launched_qlaunch = true; - } - - return has_launched_qlaunch || tid == 0x010000000000001FULL || tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid); - } - - static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx); - - protected: - /* Overridden commands. */ - Result OpenBisStorage(Out> out, u32 bis_partition_id); - Result OpenDataStorageByCurrentProcess(Out> out); - Result OpenDataStorageByDataId(Out> out, u64 data_id, u8 storage_id); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - }; -}; diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.cpp b/stratosphere/fs_mitm/source/fsmitm_utils.cpp deleted file mode 100644 index a16d9aa..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_utils.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Copyright (c) 2018 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 "debug.hpp" -#include "fsmitm_utils.hpp" -#include "ini.h" -#include "sha256.h" - -static FsFileSystem g_sd_filesystem = {0}; -static std::vector g_mitm_flagged_tids; -static std::vector g_disable_mitm_flagged_tids; -static std::atomic_bool g_has_initialized = false; -static std::atomic_bool g_has_hid_session = false; - -static u64 g_override_key_combination = KEY_R; -static u64 g_override_hbl_tid = 0x010000000000100DULL; -static bool g_override_any_app = false; -static bool g_override_by_default = true; - -/* Static buffer for loader.ini contents at runtime. */ -static char g_config_ini_data[0x800]; - -/* Backup file for CAL0 partition. */ -static constexpr size_t ProdinfoSize = 0x8000; -static FsFile g_cal0_file = {0}; -static u8 g_cal0_storage_backup[ProdinfoSize]; -static u8 g_cal0_backup[ProdinfoSize]; - -static bool IsHexadecimal(const char *str) { - while (*str) { - if (isxdigit(*str)) { - str++; - } else { - return false; - } - } - return true; -} - -void Utils::InitializeSdThreadFunc(void *args) { - /* Get required services. */ - Handle tmp_hnd = 0; - static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; - for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { - if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) { - /* TODO: Panic */ - } else { - svcCloseHandle(tmp_hnd); - } - } - - /* Mount SD. */ - while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) { - svcSleepThread(1000000ULL); - } - - /* Back up CAL0, if it's not backed up already. */ - fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups"); - { - FsStorage cal0_storage; - if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdinfoSize))) { - std::abort(); - } - fsStorageClose(&cal0_storage); - - char serial_number[0x40] = {0}; - memcpy(serial_number, g_cal0_storage_backup + 0x250, 0x18); - - - char prodinfo_backup_path[FS_MAX_PATH] = {0}; - if (strlen(serial_number) > 0) { - snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/%s_PRODINFO.bin", serial_number); - } else { - snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin"); - } - - fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0); - if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) { - bool has_auto_backup = false; - size_t read = 0; - if (R_SUCCEEDED(fsFileRead(&g_cal0_file, 0, g_cal0_backup, sizeof(g_cal0_backup), &read)) && read == sizeof(g_cal0_backup)) { - bool is_cal0_valid = true; - is_cal0_valid &= memcmp(g_cal0_backup, "CAL0", 4) == 0; - is_cal0_valid &= memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0; - u32 cal0_size = ((u32 *)g_cal0_backup)[2]; - is_cal0_valid &= cal0_size + 0x40 <= ProdinfoSize; - if (is_cal0_valid) { - struct sha256_state sha_ctx; - u8 calc_hash[0x20]; - sha256_init(&sha_ctx); - sha256_update(&sha_ctx, g_cal0_backup + 0x40, cal0_size); - sha256_finalize(&sha_ctx); - sha256_finish(&sha_ctx, calc_hash); - is_cal0_valid &= memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0; - } - has_auto_backup = is_cal0_valid; - } - - if (!has_auto_backup) { - fsFileSetSize(&g_cal0_file, ProdinfoSize); - fsFileWrite(&g_cal0_file, 0, g_cal0_backup, ProdinfoSize); - fsFileFlush(&g_cal0_file); - } - - /* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */ - memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup)); - memset(g_cal0_backup, 0, sizeof(g_cal0_backup)); - } - } - - /* Check for MitM flags. */ - FsDir titles_dir; - if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) { - FsDirectoryEntry dir_entry; - FsFile f; - u64 read_entries; - while (R_SUCCEEDED((fsDirRead(&titles_dir, 0, &read_entries, 1, &dir_entry))) && read_entries == 1) { - if (strlen(dir_entry.name) == 0x10 && IsHexadecimal(dir_entry.name)) { - u64 title_id = strtoul(dir_entry.name, NULL, 16); - char title_path[FS_MAX_PATH] = {0}; - strcpy(title_path, "/atmosphere/titles/"); - strcat(title_path, dir_entry.name); - strcat(title_path, "/flags/fsmitm.flag"); - if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { - g_mitm_flagged_tids.push_back(title_id); - fsFileClose(&f); - } else { - /* TODO: Deprecate. */ - memset(title_path, 0, sizeof(title_path)); - strcpy(title_path, "/atmosphere/titles/"); - strcat(title_path, dir_entry.name); - strcat(title_path, "/fsmitm.flag"); - if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { - g_mitm_flagged_tids.push_back(title_id); - fsFileClose(&f); - } - } - - memset(title_path, 0, sizeof(title_path)); - strcpy(title_path, "/atmosphere/titles/"); - strcat(title_path, dir_entry.name); - strcat(title_path, "/flags/fsmitm_disable.flag"); - if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { - g_disable_mitm_flagged_tids.push_back(title_id); - fsFileClose(&f); - } else { - /* TODO: Deprecate. */ - memset(title_path, 0, sizeof(title_path)); - strcpy(title_path, "/atmosphere/titles/"); - strcat(title_path, dir_entry.name); - strcat(title_path, "/fsmitm_disable.flag"); - if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) { - g_disable_mitm_flagged_tids.push_back(title_id); - fsFileClose(&f); - } - } - } - } - fsDirClose(&titles_dir); - } - - Utils::RefreshConfiguration(); - - g_has_initialized = true; - - svcExitThread(); -} - -void Utils::InitializeHidThreadFunc(void *args) { - while (R_FAILED(hidInitialize())) { - svcSleepThread(1000ULL); - } - - g_has_hid_session = true; - - hidExit(); - - svcExitThread(); -} - -bool Utils::IsSdInitialized() { - return g_has_initialized; -} - -bool Utils::IsHidAvailable() { - return g_has_hid_session; -} - -Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - return fsFsOpenFile(&g_sd_filesystem, fn, flags, out); -} - -Result Utils::OpenSdFileForAtmosphere(u64 title_id, const char *fn, int flags, FsFile *out) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - char path[FS_MAX_PATH]; - if (*fn == '/') { - snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn); - } else { - snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", title_id, fn); - } - return fsFsOpenFile(&g_sd_filesystem, path, flags, out); -} - -Result Utils::OpenRomFSSdFile(u64 title_id, const char *fn, int flags, FsFile *out) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - return OpenRomFSFile(&g_sd_filesystem, title_id, fn, flags, out); -} - -Result Utils::OpenSdDir(const char *path, FsDir *out) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - return fsFsOpenDirectory(&g_sd_filesystem, path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out); -} - -Result Utils::OpenSdDirForAtmosphere(u64 title_id, const char *path, FsDir *out) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - char safe_path[FS_MAX_PATH]; - if (*path == '/') { - snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx%s", title_id, path); - } else { - snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/%s", title_id, path); - } - return fsFsOpenDirectory(&g_sd_filesystem, safe_path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out); -} - -Result Utils::OpenRomFSSdDir(u64 title_id, const char *path, FsDir *out) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - return OpenRomFSDir(&g_sd_filesystem, title_id, path, out); -} - - -Result Utils::OpenRomFSFile(FsFileSystem *fs, u64 title_id, const char *fn, int flags, FsFile *out) { - char path[FS_MAX_PATH]; - if (*fn == '/') { - snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs%s", title_id, fn); - } else { - snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs/%s", title_id, fn); - } - return fsFsOpenFile(fs, path, flags, out); -} - -Result Utils::OpenRomFSDir(FsFileSystem *fs, u64 title_id, const char *path, FsDir *out) { - char safe_path[FS_MAX_PATH]; - if (*path == '/') { - snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/romfs%s", title_id, path); - } else { - snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/romfs/%s", title_id, path); - } - return fsFsOpenDirectory(fs, safe_path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out); -} - -bool Utils::HasSdRomfsContent(u64 title_id) { - /* Check for romfs.bin. */ - FsFile data_file; - if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(title_id, "romfs.bin", FS_OPEN_READ, &data_file))) { - fsFileClose(&data_file); - return true; - } - - /* Check for romfs folder with non-zero content. */ - FsDir dir; - if (R_FAILED(Utils::OpenRomFSSdDir(title_id, "", &dir))) { - return false; - } - ON_SCOPE_EXIT { - fsDirClose(&dir); - }; - - FsDirectoryEntry dir_entry; - u64 read_entries; - return R_SUCCEEDED(fsDirRead(&dir, 0, &read_entries, 1, &dir_entry)) && read_entries == 1; -} - -Result Utils::SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data, size_t size) { - if (!IsSdInitialized()) { - return 0xFA202; - } - - Result rc = 0; - - char path[FS_MAX_PATH]; - if (*fn == '/') { - snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn); - } else { - snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", title_id, fn); - } - - /* Unconditionally create. */ - FsFile f; - fsFsCreateFile(&g_sd_filesystem, path, size, 0); - - /* Try to open. */ - rc = fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ | FS_OPEN_WRITE, &f); - if (R_FAILED(rc)) { - return rc; - } - - /* Always close, if we opened. */ - ON_SCOPE_EXIT { - fsFileClose(&f); - }; - - /* Try to make it big enough. */ - rc = fsFileSetSize(&f, size); - if (R_FAILED(rc)) { - return rc; - } - - /* Try to write the data. */ - rc = fsFileWrite(&f, 0, data, size); - - return rc; -} - -bool Utils::IsHblTid(u64 tid) { - return (g_override_any_app && tid >= 0x0100000000010000ULL) || (!g_override_any_app && tid == g_override_hbl_tid); -} - -bool Utils::HasTitleFlag(u64 tid, const char *flag) { - if (IsSdInitialized()) { - FsFile f; - char flag_path[FS_MAX_PATH]; - - memset(flag_path, 0, sizeof(flag_path)); - snprintf(flag_path, sizeof(flag_path) - 1, "flags/%s.flag", flag); - if (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) { - fsFileClose(&f); - return true; - } - - /* TODO: Deprecate. */ - snprintf(flag_path, sizeof(flag_path) - 1, "%s.flag", flag); - if (OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f)) { - fsFileClose(&f); - return true; - } - } - return false; -} - -bool Utils::HasGlobalFlag(const char *flag) { - if (IsSdInitialized()) { - FsFile f; - char flag_path[FS_MAX_PATH] = {0}; - snprintf(flag_path, sizeof(flag_path), "/atmosphere/flags/%s.flag", flag); - if (fsFsOpenFile(&g_sd_filesystem, flag_path, FS_OPEN_READ, &f)) { - fsFileClose(&f); - return true; - } - } - return false; -} - -bool Utils::HasHblFlag(const char *flag) { - char hbl_flag[FS_MAX_PATH] = {0}; - snprintf(hbl_flag, sizeof(hbl_flag), "hbl_%s", flag); - return HasGlobalFlag(hbl_flag); -} - -bool Utils::HasFlag(u64 tid, const char *flag) { - return HasTitleFlag(tid, flag) || (IsHblTid(tid) && HasHblFlag(flag)); -} - -bool Utils::HasSdMitMFlag(u64 tid) { - if (IsHblTid(tid)) { - return true; - } - - if (IsSdInitialized()) { - return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end(); - } - return false; -} - -bool Utils::HasSdDisableMitMFlag(u64 tid) { - if (IsSdInitialized()) { - return std::find(g_disable_mitm_flagged_tids.begin(), g_disable_mitm_flagged_tids.end(), tid) != g_disable_mitm_flagged_tids.end(); - } - return false; -} - -Result Utils::GetKeysDown(u64 *keys) { - if (!Utils::IsHidAvailable() || R_FAILED(hidInitialize())) { - return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID); - } - - hidScanInput(); - *keys = hidKeysDown(CONTROLLER_P1_AUTO); - - hidExit(); - return 0x0; -} - -bool Utils::HasOverrideButton(u64 tid) { - if (tid < 0x0100000000010000ULL || !IsSdInitialized()) { - /* Disable button override disable for non-applications. */ - return true; - } - - /* Unconditionally refresh loader.ini contents. */ - RefreshConfiguration(); - - u64 kDown = 0; - bool keys_triggered = (R_SUCCEEDED(GetKeysDown(&kDown)) && ((kDown & g_override_key_combination) != 0)); - return IsSdInitialized() && (g_override_by_default ^ keys_triggered); -} - -static int FsMitMIniHandler(void *user, const char *section, const char *name, const char *value) { - /* Taken and modified, with love, from Rajkosto's implementation. */ - if (strcasecmp(section, "config") == 0) { - if (strcasecmp(name, "hbl_tid") == 0) { - if (strcasecmp(value, "app") == 0) { - g_override_any_app = true; - } - else { - u64 override_tid = strtoul(value, NULL, 16); - if (override_tid != 0) { - g_override_hbl_tid = override_tid; - } - } - } else if (strcasecmp(name, "override_key") == 0) { - if (value[0] == '!') { - g_override_by_default = true; - value++; - } else { - g_override_by_default = false; - } - - if (strcasecmp(value, "A") == 0) { - g_override_key_combination = KEY_A; - } else if (strcasecmp(value, "B") == 0) { - g_override_key_combination = KEY_B; - } else if (strcasecmp(value, "X") == 0) { - g_override_key_combination = KEY_X; - } else if (strcasecmp(value, "Y") == 0) { - g_override_key_combination = KEY_Y; - } else if (strcasecmp(value, "LS") == 0) { - g_override_key_combination = KEY_LSTICK; - } else if (strcasecmp(value, "RS") == 0) { - g_override_key_combination = KEY_RSTICK; - } else if (strcasecmp(value, "L") == 0) { - g_override_key_combination = KEY_L; - } else if (strcasecmp(value, "R") == 0) { - g_override_key_combination = KEY_R; - } else if (strcasecmp(value, "ZL") == 0) { - g_override_key_combination = KEY_ZL; - } else if (strcasecmp(value, "ZR") == 0) { - g_override_key_combination = KEY_ZR; - } else if (strcasecmp(value, "PLUS") == 0) { - g_override_key_combination = KEY_PLUS; - } else if (strcasecmp(value, "MINUS") == 0) { - g_override_key_combination = KEY_MINUS; - } else if (strcasecmp(value, "DLEFT") == 0) { - g_override_key_combination = KEY_DLEFT; - } else if (strcasecmp(value, "DUP") == 0) { - g_override_key_combination = KEY_DUP; - } else if (strcasecmp(value, "DRIGHT") == 0) { - g_override_key_combination = KEY_DRIGHT; - } else if (strcasecmp(value, "DDOWN") == 0) { - g_override_key_combination = KEY_DDOWN; - } else if (strcasecmp(value, "SL") == 0) { - g_override_key_combination = KEY_SL; - } else if (strcasecmp(value, "SR") == 0) { - g_override_key_combination = KEY_SR; - } else { - g_override_key_combination = 0; - } - } - } else { - return 0; - } - return 1; -} - - -void Utils::RefreshConfiguration() { - FsFile config_file; - if (R_FAILED(fsFsOpenFile(&g_sd_filesystem, "/atmosphere/loader.ini", FS_OPEN_READ, &config_file))) { - return; - } - - u64 size; - if (R_FAILED(fsFileGetSize(&config_file, &size))) { - return; - } - - size = std::min(size, (decltype(size))0x7FF); - - /* Read in string. */ - std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0); - size_t r_s; - fsFileRead(&config_file, 0, g_config_ini_data, size, &r_s); - fsFileClose(&config_file); - - ini_parse_string(g_config_ini_data, FsMitMIniHandler, NULL); -} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.hpp b/stratosphere/fs_mitm/source/fsmitm_utils.hpp deleted file mode 100644 index 6aa8535..0000000 --- a/stratosphere/fs_mitm/source/fsmitm_utils.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018 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 BisStorageId : u32 { - BisStorageId_Boot0 = 0, - BisStorageId_Boot1 = 10, - BisStorageId_RawNand = 20, - BisStorageId_BcPkg2_1 = 21, - BisStorageId_BcPkg2_2 = 22, - BisStorageId_BcPkg2_3 = 23, - BisStorageId_BcPkg2_4 = 24, - BisStorageId_BcPkg2_5 = 25, - BisStorageId_BcPkg2_6 = 26, - BisStorageId_Prodinfo = 27, - BisStorageId_ProdinfoF = 28, - BisStorageId_Safe = 29, - BisStorageId_User = 30, - BisStorageId_System = 31, - BisStorageId_SystemProperEncryption = 32, - BisStorageId_SystemProperPartition = 33, -}; - -class Utils { - public: - static bool IsSdInitialized(); - - static Result OpenSdFile(const char *fn, int flags, FsFile *out); - static Result OpenSdFileForAtmosphere(u64 title_id, const char *fn, int flags, FsFile *out); - static Result OpenRomFSSdFile(u64 title_id, const char *fn, int flags, FsFile *out); - static Result OpenSdDir(const char *path, FsDir *out); - static Result OpenSdDirForAtmosphere(u64 title_id, const char *path, FsDir *out); - static Result OpenRomFSSdDir(u64 title_id, const char *path, FsDir *out); - - static Result OpenRomFSFile(FsFileSystem *fs, u64 title_id, const char *fn, int flags, FsFile *out); - static Result OpenRomFSDir(FsFileSystem *fs, u64 title_id, const char *path, FsDir *out); - - static Result SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data, size_t size); - - static bool HasSdRomfsContent(u64 title_id); - - /* SD card Initialization + MitM detection. */ - static void InitializeSdThreadFunc(void *args); - - static bool IsHblTid(u64 tid); - - static bool HasTitleFlag(u64 tid, const char *flag); - static bool HasHblFlag(const char *flag); - static bool HasGlobalFlag(const char *flag); - static bool HasFlag(u64 tid, const char *flag); - - static bool HasSdMitMFlag(u64 tid); - static bool HasSdDisableMitMFlag(u64 tid); - - - static bool IsHidAvailable(); - static void InitializeHidThreadFunc(void *args); - static Result GetKeysDown(u64 *keys); - static bool HasOverrideButton(u64 tid); - private: - static void RefreshConfiguration(); -}; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/ini.c b/stratosphere/fs_mitm/source/ini.c deleted file mode 100644 index 63626c7..0000000 --- a/stratosphere/fs_mitm/source/ini.c +++ /dev/null @@ -1,269 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include - -#include "ini.h" - -#if !INI_USE_STACK -#include -#endif - -#define MAX_SECTION 50 -#define MAX_NAME 50 - -/* Used by ini_parse_string() to keep track of string parsing state. */ -typedef struct { - const char* ptr; - size_t num_left; -} ini_parse_string_ctx; - -/* Strip whitespace chars off end of given string, in place. Return s. */ -static char* rstrip(char* s) -{ - char* p = s + strlen(s); - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* Return pointer to first char (of chars) or inline comment in given string, - or pointer to null at end of string if neither found. Inline comment must - be prefixed by a whitespace character to register as a comment. */ -static char* find_chars_or_comment(const char* s, const char* chars) -{ -#if INI_ALLOW_INLINE_COMMENTS - int was_space = 0; - while (*s && (!chars || !strchr(chars, *s)) && - !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { - was_space = isspace((unsigned char)(*s)); - s++; - } -#else - while (*s && (!chars || !strchr(chars, *s))) { - s++; - } -#endif - return (char*)s; -} - -/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - strncpy(dest, src, size - 1); - dest[size - 1] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user) -{ - /* Uses a fair bit of stack (use heap instead if you need to) */ -#if INI_USE_STACK - char line[INI_MAX_LINE]; - int max_line = INI_MAX_LINE; -#else - char* line; - int max_line = INI_INITIAL_ALLOC; -#endif -#if INI_ALLOW_REALLOC - char* new_line; - int offset; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - line = (char*)malloc(INI_INITIAL_ALLOC); - if (!line) { - return -2; - } -#endif - -#if INI_HANDLER_LINENO -#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) -#else -#define HANDLER(u, s, n, v) handler(u, s, n, v) -#endif - - /* Scan through stream line by line */ - while (reader(line, max_line, stream) != NULL) { -#if INI_ALLOW_REALLOC - offset = strlen(line); - while (offset == max_line - 1 && line[offset - 1] != '\n') { - max_line *= 2; - if (max_line > INI_MAX_LINE) - max_line = INI_MAX_LINE; - new_line = realloc(line, max_line); - if (!new_line) { - free(line); - return -2; - } - line = new_line; - if (reader(line + offset, max_line - offset, stream) == NULL) - break; - if (max_line >= INI_MAX_LINE) - break; - offset += strlen(line + offset); - } -#endif - - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (strchr(INI_START_COMMENT_PREFIXES, *start)) { - /* Start-of-line comment */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* Non-blank line with leading whitespace, treat as continuation - of previous name's value (as per Python configparser). */ - if (!HANDLER(user, section, prev_name, start) && !error) - error = lineno; - } -#endif - else if (*start == '[') { - /* A "[section]" line */ - end = find_chars_or_comment(start + 1, "]"); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start) { - /* Not a comment, must be a name[=:]value pair */ - end = find_chars_or_comment(start, "=:"); - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - value = end + 1; -#if INI_ALLOW_INLINE_COMMENTS - end = find_chars_or_comment(value, NULL); - if (*end) - *end = '\0'; -#endif - value = lskip(value); - rstrip(value); - - /* Valid name[=:]value pair found, call handler */ - strncpy0(prev_name, name, sizeof(prev_name)); - if (!HANDLER(user, section, name, value) && !error) - error = lineno; - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ - error = lineno; - } - } - -#if INI_STOP_ON_FIRST_ERROR - if (error) - break; -#endif - } - -#if !INI_USE_STACK - free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user) -{ - return ini_parse_stream((ini_reader)fgets, file, handler, user); -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) - return -1; - error = ini_parse_file(file, handler, user); - fclose(file); - return error; -} - -/* An ini_reader function to read the next line from a string buffer. This - is the fgets() equivalent used by ini_parse_string(). */ -static char* ini_reader_string(char* str, int num, void* stream) { - ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; - const char* ctx_ptr = ctx->ptr; - size_t ctx_num_left = ctx->num_left; - char* strp = str; - char c; - - if (ctx_num_left == 0 || num < 2) - return NULL; - - while (num > 1 && ctx_num_left != 0) { - c = *ctx_ptr++; - ctx_num_left--; - *strp++ = c; - if (c == '\n') - break; - num--; - } - - *strp = '\0'; - ctx->ptr = ctx_ptr; - ctx->num_left = ctx_num_left; - return str; -} - -/* See documentation in header file. */ -int ini_parse_string(const char* string, ini_handler handler, void* user) { - ini_parse_string_ctx ctx; - - ctx.ptr = string; - ctx.num_left = strlen(string); - return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, - user); -} diff --git a/stratosphere/fs_mitm/source/ini.h b/stratosphere/fs_mitm/source/ini.h deleted file mode 100644 index f45ba40..0000000 --- a/stratosphere/fs_mitm/source/ini.h +++ /dev/null @@ -1,130 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#ifndef __INI_H__ -#define __INI_H__ - -/* Make this header file easier to include in C++ code */ -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Nonzero if ini_handler callback should accept lineno parameter. */ -#ifndef INI_HANDLER_LINENO -#define INI_HANDLER_LINENO 0 -#endif - -/* Typedef for prototype of handler function. */ -#if INI_HANDLER_LINENO -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value, - int lineno); -#else -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value); -#endif - -/* Typedef for prototype of fgets-style reader function. */ -typedef char* (*ini_reader)(char* str, int num, void* stream); - -/* Parse given INI-style file. May have [section]s, name=value pairs - (whitespace stripped), and comments starting with ';' (semicolon). Section - is "" if name=value pair parsed before any section heading. name:value - pairs are also supported as a concession to Python's configparser. - - For each name=value pair parsed, call handler function with given user - pointer as well as section, name, and value (data only valid for duration - of handler call). Handler should return nonzero on success, zero on error. - - Returns 0 on success, line number of first error on parse error (doesn't - stop on first error), -1 on file open error, or -2 on memory allocation - error (only when INI_USE_STACK is zero). -*/ -int ini_parse(const char* filename, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - close the file when it's finished -- the caller must do that. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes an ini_reader function pointer instead of - filename. Used for implementing custom or string-based I/O (see also - ini_parse_string). */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user); - -/* Same as ini_parse(), but takes a zero-terminated string with the INI data -instead of a file. Useful for parsing INI data from a network socket or -already in memory. */ -int ini_parse_string(const char* string, ini_handler handler, void* user); - -/* Nonzero to allow multi-line value parsing, in the style of Python's - configparser. If allowed, ini_parse() will call the handler with the same - name for each subsequent line parsed. */ -#ifndef INI_ALLOW_MULTILINE -#define INI_ALLOW_MULTILINE 1 -#endif - -/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - the file. See http://code.google.com/p/inih/issues/detail?id=21 */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* Chars that begin a start-of-line comment. Per Python configparser, allow - both ; and # comments at the start of a line by default. */ -#ifndef INI_START_COMMENT_PREFIXES -#define INI_START_COMMENT_PREFIXES ";#" -#endif - -/* Nonzero to allow inline comments (with valid inline comment characters - specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match - Python 3.2+ configparser behaviour. */ -#ifndef INI_ALLOW_INLINE_COMMENTS -#define INI_ALLOW_INLINE_COMMENTS 1 -#endif -#ifndef INI_INLINE_COMMENT_PREFIXES -#define INI_INLINE_COMMENT_PREFIXES ";" -#endif - -/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ -#ifndef INI_USE_STACK -#define INI_USE_STACK 1 -#endif - -/* Maximum line length for any line in INI file (stack or heap). Note that - this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 -#endif - -/* Nonzero to allow heap line buffer to grow via realloc(), zero for a - fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is - zero. */ -#ifndef INI_ALLOW_REALLOC -#define INI_ALLOW_REALLOC 0 -#endif - -/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK - is zero. */ -#ifndef INI_INITIAL_ALLOC -#define INI_INITIAL_ALLOC 200 -#endif - -/* Stop parsing on first error (default is to keep parsing). */ -#ifndef INI_STOP_ON_FIRST_ERROR -#define INI_STOP_ON_FIRST_ERROR 0 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* __INI_H__ */ diff --git a/stratosphere/fs_mitm/source/sha256.c b/stratosphere/fs_mitm/source/sha256.c deleted file mode 100644 index 02d40a7..0000000 --- a/stratosphere/fs_mitm/source/sha256.c +++ /dev/null @@ -1,113 +0,0 @@ -/* Based on linux source code */ -/* - * sha256_base.h - core logic for SHA-256 implementations - * - * Copyright (C) 2015 Linaro Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include "sha256.h" - -#define unlikely(x) __builtin_expect(!!(x), 0) - -void sha256_block_data_order (uint32_t *ctx, const void *in, size_t num); - -int sha256_init(struct sha256_state *sctx) -{ - sctx->state[0] = SHA256_H0; - sctx->state[1] = SHA256_H1; - sctx->state[2] = SHA256_H2; - sctx->state[3] = SHA256_H3; - sctx->state[4] = SHA256_H4; - sctx->state[5] = SHA256_H5; - sctx->state[6] = SHA256_H6; - sctx->state[7] = SHA256_H7; - sctx->count = 0; - - return 0; -} - -int sha256_update(struct sha256_state *sctx, - const void *data, - size_t len) -{ - const u8 *data8 = (const u8 *)data; - unsigned int len32 = (unsigned int)len; - unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; - - sctx->count += len32; - - if (unlikely((partial + len32) >= SHA256_BLOCK_SIZE)) { - int blocks; - - if (partial) { - int p = SHA256_BLOCK_SIZE - partial; - - memcpy(sctx->buf + partial, data8, p); - data8 += p; - len32 -= p; - - sha256_block_data_order(sctx->state, sctx->buf, 1); - } - - blocks = len32 / SHA256_BLOCK_SIZE; - len32 %= SHA256_BLOCK_SIZE; - - if (blocks) { - sha256_block_data_order(sctx->state, data8, blocks); - data8 += blocks * SHA256_BLOCK_SIZE; - } - partial = 0; - } - if (len32) - memcpy(sctx->buf + partial, data8, len32); - - return 0; -} - -int sha256_finalize(struct sha256_state *sctx) -{ - const int bit_offset = SHA256_BLOCK_SIZE - sizeof(u64); - u64 *bits = (u64 *)(sctx->buf + bit_offset); - unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; - - sctx->buf[partial++] = 0x80; - if (partial > bit_offset) { - memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial); - partial = 0; - - sha256_block_data_order(sctx->state, sctx->buf, 1); - } - - memset(sctx->buf + partial, 0x0, bit_offset - partial); - *bits = __builtin_bswap64(sctx->count << 3); - sha256_block_data_order(sctx->state, sctx->buf, 1); - - return 0; -} - -int sha256_finish(struct sha256_state *sctx, void *out) -{ - unsigned int digest_size = 32; - u32 *digest = (u32 *)out; - int i; - - // Switch: misalignment shouldn't be a problem here... - for (i = 0; digest_size > 0; i++, digest_size -= sizeof(u32)) - *digest++ = __builtin_bswap32(sctx->state[i]); - - *sctx = (struct sha256_state){}; - return 0; -} - -#ifdef __cplusplus -} -#endif diff --git a/stratosphere/fs_mitm/source/sha256.h b/stratosphere/fs_mitm/source/sha256.h deleted file mode 100644 index cfb09f8..0000000 --- a/stratosphere/fs_mitm/source/sha256.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/* Based on linux source code */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#define SHA256_DIGEST_SIZE 32 -#define SHA256_BLOCK_SIZE 64 - -#define SHA256_H0 0x6a09e667UL -#define SHA256_H1 0xbb67ae85UL -#define SHA256_H2 0x3c6ef372UL -#define SHA256_H3 0xa54ff53aUL -#define SHA256_H4 0x510e527fUL -#define SHA256_H5 0x9b05688cUL -#define SHA256_H6 0x1f83d9abUL -#define SHA256_H7 0x5be0cd19UL - -struct sha256_state { - u32 state[SHA256_DIGEST_SIZE / 4]; - u64 count; - u8 buf[SHA256_BLOCK_SIZE]; -}; - -int sha256_init(struct sha256_state *sctx); -int sha256_update(struct sha256_state *sctx, const void *data, size_t len); -int sha256_finalize(struct sha256_state *sctx); -int sha256_finish(struct sha256_state *sctx, void *out); - -#ifdef __cplusplus -} -#endif diff --git a/stratosphere/fs_mitm/source/sha256_armv8.s b/stratosphere/fs_mitm/source/sha256_armv8.s deleted file mode 100644 index 0420d38..0000000 --- a/stratosphere/fs_mitm/source/sha256_armv8.s +++ /dev/null @@ -1,163 +0,0 @@ -.section .text.sha256_armv8, "ax", %progbits -.align 5 -.arch armv8-a+crypto - -# SHA256 assembly implementation for ARMv8 AArch64 (based on linux source code) - -.global sha256_block_data_order -.type sha256_block_data_order,%function -sha256_block_data_order: - -.Lsha256prolog: - - stp x29, x30, [sp,#-64]! - mov x29, sp - adr x3, .LKConstant256 - str q8, [sp, #16] - ld1 {v16.4s-v19.4s}, [x3], #64 - ld1 {v0.4s}, [x0], #16 - ld1 {v20.4s-v23.4s}, [x3], #64 - add x2, x1, x2, lsl #6 - ld1 {v1.4s}, [x0] - ld1 {v24.4s-v27.4s}, [x3], #64 - sub x0, x0, #16 - str q9, [sp, #32] - str q10, [sp, #48] - ld1 {v28.4s-v31.4s}, [x3], #64 - -.Lsha256loop: - - ld1 {v5.16b-v8.16b}, [x1], #64 - mov v2.16b, v0.16b - mov v3.16b, v1.16b - - rev32 v5.16b, v5.16b - rev32 v6.16b, v6.16b - add v9.4s, v5.4s, v16.4s - rev32 v7.16b, v7.16b - add v10.4s, v6.4s, v17.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su0 v5.4s, v6.4s - rev32 v8.16b, v8.16b - add v9.4s, v7.4s, v18.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - sha256su0 v6.4s, v7.4s - sha256su1 v5.4s, v7.4s, v8.4s - add v10.4s, v8.4s, v19.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su0 v7.4s, v8.4s - sha256su1 v6.4s, v8.4s, v5.4s - add v9.4s, v5.4s, v20.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - sha256su0 v8.4s, v5.4s - sha256su1 v7.4s, v5.4s, v6.4s - add v10.4s, v6.4s, v21.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su0 v5.4s, v6.4s - sha256su1 v8.4s, v6.4s, v7.4s - add v9.4s, v7.4s, v22.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - sha256su0 v6.4s, v7.4s - sha256su1 v5.4s, v7.4s, v8.4s - add v10.4s, v8.4s, v23.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su0 v7.4s, v8.4s - sha256su1 v6.4s, v8.4s, v5.4s - add v9.4s, v5.4s, v24.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - sha256su0 v8.4s, v5.4s - sha256su1 v7.4s, v5.4s, v6.4s - add v10.4s, v6.4s, v25.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su0 v5.4s, v6.4s - sha256su1 v8.4s, v6.4s, v7.4s - add v9.4s, v7.4s, v26.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - sha256su0 v6.4s, v7.4s - sha256su1 v5.4s, v7.4s, v8.4s - add v10.4s, v8.4s, v27.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su0 v7.4s, v8.4s - sha256su1 v6.4s, v8.4s, v5.4s - add v9.4s, v5.4s, v28.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - sha256su0 v8.4s, v5.4s - sha256su1 v7.4s, v5.4s, v6.4s - add v10.4s, v6.4s, v29.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - sha256su1 v8.4s, v6.4s, v7.4s - add v9.4s, v7.4s, v30.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - add v10.4s, v8.4s, v31.4s - mov v4.16b, v2.16b - sha256h q2, q3, v9.4s - sha256h2 q3, q4, v9.4s - mov v4.16b, v2.16b - sha256h q2, q3, v10.4s - sha256h2 q3, q4, v10.4s - cmp x1, x2 - add v1.4s, v1.4s, v3.4s - add v0.4s, v0.4s, v2.4s - b.ne .Lsha256loop - -.Lsha256epilog: - - st1 {v0.4s,v1.4s}, [x0] - ldr q10, [sp, #48] - ldr q9, [sp, #32] - ldr q8, [sp, #16] - ldr x29, [sp], #64 - ret - -.align 5 -.LKConstant256: -.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 -.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 -.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 -.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 -.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc -.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da -.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 -.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 -.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 -.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 -.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 -.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 -.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 -.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 -.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 -.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 - -.size sha256_block_data_order,.-sha256_block_data_order -.align 2 - - - diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index b3059f0..6321a42 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -224,9 +224,8 @@ if (maintenance) { BootModeService::SetMaintenanceBootForEmbeddedBoot2(); } - + /* Launch set:mitm, wait for it. */ - LaunchTitle(Boot2KnownTitleId::ams_set_mitm, FsStorageId_None, 0, NULL); WaitForMitm("set:sys"); /* Launch usb. */ diff --git a/stratosphere/set_mitm/Makefile b/stratosphere/set_mitm/Makefile deleted file mode 100644 index 2d3bf28..0000000 --- a/stratosphere/set_mitm/Makefile +++ /dev/null @@ -1,166 +0,0 @@ -#--------------------------------------------------------------------------------- -.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/set_mitm/set_mitm.json b/stratosphere/set_mitm/set_mitm.json deleted file mode 100644 index 634ac77..0000000 --- a/stratosphere/set_mitm/set_mitm.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "name": "set.mitm", - "title_id": "0x0100000000000032", - "title_id_range_min": "0x0100000000000032", - "title_id_range_max": "0x0100000000000032", - "main_thread_stack_size": "0x00004000", - "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_host": [ - "set:sys" - ], - "service_access": [ - "fatal:u", - "fsp-srv", - "spl:", - "set:sys", - "fsp-srv" - ], - "kernel_capabilities": [ - { - "type": "kernel_flags", - "value": { - "highest_thread_priority": 63, - "lowest_thread_priority": 24, - "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", - "svcCreateInterruptEvent": "0x53", - "svcReadWriteRegister": "0x4E", - "svcQueryIoMapping": "0x55", - "svcCreateDeviceAddressSpace": "0x56", - "svcAttachDeviceAddressSpace": "0x57", - "svcDetachDeviceAddressSpace": "0x58", - "svcMapDeviceAddressSpaceAligned": "0x5a", - "svcUnmapDeviceAddressSpace": "0x5c", - "svcGetSystemInfo": "0x6f", - "svcCallSecureMonitor": "0x7F" - } - }, - { - "type": "min_kernel_version", - "value": "0x0030" - } - ] -} \ No newline at end of file diff --git a/stratosphere/set_mitm/source/ini.c b/stratosphere/set_mitm/source/ini.c deleted file mode 100644 index 426430c..0000000 --- a/stratosphere/set_mitm/source/ini.c +++ /dev/null @@ -1,269 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include - -#include "ini.h" - -#if !INI_USE_STACK -#include -#endif - -#define MAX_SECTION 72 -#define MAX_NAME 72 - -/* Used by ini_parse_string() to keep track of string parsing state. */ -typedef struct { - const char* ptr; - size_t num_left; -} ini_parse_string_ctx; - -/* Strip whitespace chars off end of given string, in place. Return s. */ -static char* rstrip(char* s) -{ - char* p = s + strlen(s); - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* Return pointer to first char (of chars) or inline comment in given string, - or pointer to null at end of string if neither found. Inline comment must - be prefixed by a whitespace character to register as a comment. */ -static char* find_chars_or_comment(const char* s, const char* chars) -{ -#if INI_ALLOW_INLINE_COMMENTS - int was_space = 0; - while (*s && (!chars || !strchr(chars, *s)) && - !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { - was_space = isspace((unsigned char)(*s)); - s++; - } -#else - while (*s && (!chars || !strchr(chars, *s))) { - s++; - } -#endif - return (char*)s; -} - -/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - strncpy(dest, src, size - 1); - dest[size - 1] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user) -{ - /* Uses a fair bit of stack (use heap instead if you need to) */ -#if INI_USE_STACK - char line[INI_MAX_LINE]; - int max_line = INI_MAX_LINE; -#else - char* line; - int max_line = INI_INITIAL_ALLOC; -#endif -#if INI_ALLOW_REALLOC - char* new_line; - int offset; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - line = (char*)malloc(INI_INITIAL_ALLOC); - if (!line) { - return -2; - } -#endif - -#if INI_HANDLER_LINENO -#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) -#else -#define HANDLER(u, s, n, v) handler(u, s, n, v) -#endif - - /* Scan through stream line by line */ - while (reader(line, max_line, stream) != NULL) { -#if INI_ALLOW_REALLOC - offset = strlen(line); - while (offset == max_line - 1 && line[offset - 1] != '\n') { - max_line *= 2; - if (max_line > INI_MAX_LINE) - max_line = INI_MAX_LINE; - new_line = realloc(line, max_line); - if (!new_line) { - free(line); - return -2; - } - line = new_line; - if (reader(line + offset, max_line - offset, stream) == NULL) - break; - if (max_line >= INI_MAX_LINE) - break; - offset += strlen(line + offset); - } -#endif - - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (strchr(INI_START_COMMENT_PREFIXES, *start)) { - /* Start-of-line comment */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* Non-blank line with leading whitespace, treat as continuation - of previous name's value (as per Python configparser). */ - if (!HANDLER(user, section, prev_name, start) && !error) - error = lineno; - } -#endif - else if (*start == '[') { - /* A "[section]" line */ - end = find_chars_or_comment(start + 1, "]"); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start) { - /* Not a comment, must be a name[=:]value pair */ - end = find_chars_or_comment(start, "=:"); - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - value = end + 1; -#if INI_ALLOW_INLINE_COMMENTS - end = find_chars_or_comment(value, NULL); - if (*end) - *end = '\0'; -#endif - value = lskip(value); - rstrip(value); - - /* Valid name[=:]value pair found, call handler */ - strncpy0(prev_name, name, sizeof(prev_name)); - if (!HANDLER(user, section, name, value) && !error) - error = lineno; - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ - error = lineno; - } - } - -#if INI_STOP_ON_FIRST_ERROR - if (error) - break; -#endif - } - -#if !INI_USE_STACK - free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user) -{ - return ini_parse_stream((ini_reader)fgets, file, handler, user); -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) - return -1; - error = ini_parse_file(file, handler, user); - fclose(file); - return error; -} - -/* An ini_reader function to read the next line from a string buffer. This - is the fgets() equivalent used by ini_parse_string(). */ -static char* ini_reader_string(char* str, int num, void* stream) { - ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; - const char* ctx_ptr = ctx->ptr; - size_t ctx_num_left = ctx->num_left; - char* strp = str; - char c; - - if (ctx_num_left == 0 || num < 2) - return NULL; - - while (num > 1 && ctx_num_left != 0) { - c = *ctx_ptr++; - ctx_num_left--; - *strp++ = c; - if (c == '\n') - break; - num--; - } - - *strp = '\0'; - ctx->ptr = ctx_ptr; - ctx->num_left = ctx_num_left; - return str; -} - -/* See documentation in header file. */ -int ini_parse_string(const char* string, ini_handler handler, void* user) { - ini_parse_string_ctx ctx; - - ctx.ptr = string; - ctx.num_left = strlen(string); - return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, - user); -} diff --git a/stratosphere/set_mitm/source/ini.h b/stratosphere/set_mitm/source/ini.h deleted file mode 100644 index f45ba40..0000000 --- a/stratosphere/set_mitm/source/ini.h +++ /dev/null @@ -1,130 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#ifndef __INI_H__ -#define __INI_H__ - -/* Make this header file easier to include in C++ code */ -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Nonzero if ini_handler callback should accept lineno parameter. */ -#ifndef INI_HANDLER_LINENO -#define INI_HANDLER_LINENO 0 -#endif - -/* Typedef for prototype of handler function. */ -#if INI_HANDLER_LINENO -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value, - int lineno); -#else -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value); -#endif - -/* Typedef for prototype of fgets-style reader function. */ -typedef char* (*ini_reader)(char* str, int num, void* stream); - -/* Parse given INI-style file. May have [section]s, name=value pairs - (whitespace stripped), and comments starting with ';' (semicolon). Section - is "" if name=value pair parsed before any section heading. name:value - pairs are also supported as a concession to Python's configparser. - - For each name=value pair parsed, call handler function with given user - pointer as well as section, name, and value (data only valid for duration - of handler call). Handler should return nonzero on success, zero on error. - - Returns 0 on success, line number of first error on parse error (doesn't - stop on first error), -1 on file open error, or -2 on memory allocation - error (only when INI_USE_STACK is zero). -*/ -int ini_parse(const char* filename, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - close the file when it's finished -- the caller must do that. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes an ini_reader function pointer instead of - filename. Used for implementing custom or string-based I/O (see also - ini_parse_string). */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user); - -/* Same as ini_parse(), but takes a zero-terminated string with the INI data -instead of a file. Useful for parsing INI data from a network socket or -already in memory. */ -int ini_parse_string(const char* string, ini_handler handler, void* user); - -/* Nonzero to allow multi-line value parsing, in the style of Python's - configparser. If allowed, ini_parse() will call the handler with the same - name for each subsequent line parsed. */ -#ifndef INI_ALLOW_MULTILINE -#define INI_ALLOW_MULTILINE 1 -#endif - -/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - the file. See http://code.google.com/p/inih/issues/detail?id=21 */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* Chars that begin a start-of-line comment. Per Python configparser, allow - both ; and # comments at the start of a line by default. */ -#ifndef INI_START_COMMENT_PREFIXES -#define INI_START_COMMENT_PREFIXES ";#" -#endif - -/* Nonzero to allow inline comments (with valid inline comment characters - specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match - Python 3.2+ configparser behaviour. */ -#ifndef INI_ALLOW_INLINE_COMMENTS -#define INI_ALLOW_INLINE_COMMENTS 1 -#endif -#ifndef INI_INLINE_COMMENT_PREFIXES -#define INI_INLINE_COMMENT_PREFIXES ";" -#endif - -/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ -#ifndef INI_USE_STACK -#define INI_USE_STACK 1 -#endif - -/* Maximum line length for any line in INI file (stack or heap). Note that - this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 -#endif - -/* Nonzero to allow heap line buffer to grow via realloc(), zero for a - fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is - zero. */ -#ifndef INI_ALLOW_REALLOC -#define INI_ALLOW_REALLOC 0 -#endif - -/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK - is zero. */ -#ifndef INI_INITIAL_ALLOC -#define INI_INITIAL_ALLOC 200 -#endif - -/* Stop parsing on first error (default is to keep parsing). */ -#ifndef INI_STOP_ON_FIRST_ERROR -#define INI_STOP_ON_FIRST_ERROR 0 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* __INI_H__ */ diff --git a/stratosphere/set_mitm/source/setmitm_main.cpp b/stratosphere/set_mitm/source/setmitm_main.cpp deleted file mode 100644 index 44a5b31..0000000 --- a/stratosphere/set_mitm/source/setmitm_main.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2018 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 "setsys_mitm_service.hpp" -#include "setsys_settings_items.hpp" - -extern "C" { - extern u32 __start__; - - u32 __nx_applet_type = AppletType_None; - - #define INNER_HEAP_SIZE 0x50000 - 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); -} - - -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; - - rc = smInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); - } - - rc = setsysInitialize(); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - - CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); -} - -void __appExit(void) { - /* Cleanup services. */ - setsysExit(); - smExit(); -} - -struct SetSysManagerOptions { - static const size_t PointerBufferSize = 0x100; - static const size_t MaxDomains = 4; - static const size_t MaxDomainObjects = 0x100; -}; - -using SetMitmManager = WaitableManager; - -int main(int argc, char **argv) -{ - consoleDebugInit(debugDevice_SVC); - - /* Load settings from SD card. */ - SettingsItemManager::RefreshConfiguration(); - - /* TODO: What's a good timeout value to use here? */ - auto server_manager = new SetMitmManager(3); - - /* Create set:sys mitm. */ - AddMitmServerToManager(server_manager, "set:sys", 60); - - /* Loop forever, servicing our services. */ - server_manager->Process(); - - delete server_manager; - - return 0; -} - diff --git a/stratosphere/set_mitm/source/setsys_firmware_version.cpp b/stratosphere/set_mitm/source/setsys_firmware_version.cpp deleted file mode 100644 index b37c172..0000000 --- a/stratosphere/set_mitm/source/setsys_firmware_version.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018 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 "setsys_firmware_version.hpp" - -static HosMutex g_version_mutex; -static bool g_got_version = false; -static SetSysFirmwareVersion g_fw_version = {0}; - -Result VersionManager::GetFirmwareVersion(u64 title_id, SetSysFirmwareVersion *out) { - std::scoped_lock lock(g_version_mutex); - if (!g_got_version) { - Result rc = setsysGetFirmwareVersion(&g_fw_version); - if (R_FAILED(rc)) { - return rc; - } - - /* Modify the output firmware version. */ - { - u32 major, minor, micro; - char display_version[sizeof(g_fw_version.display_version)] = {0}; - - GetAtmosphereApiVersion(&major, &minor, µ, nullptr, nullptr); - snprintf(display_version, sizeof(display_version), "%s (AMS %u.%u.%u)", g_fw_version.display_version, major, minor, micro); - - memcpy(g_fw_version.display_version, display_version, sizeof(g_fw_version.display_version)); - } - - g_got_version = true; - } - - /* Report atmosphere string to qlaunch, maintenance and nothing else. */ - if (title_id == 0x0100000000001000ULL || title_id == 0x0100000000001015ULL) { - *out = g_fw_version; - return 0; - } else { - return setsysGetFirmwareVersion(out); - } -} \ No newline at end of file diff --git a/stratosphere/set_mitm/source/setsys_firmware_version.hpp b/stratosphere/set_mitm/source/setsys_firmware_version.hpp deleted file mode 100644 index de035ed..0000000 --- a/stratosphere/set_mitm/source/setsys_firmware_version.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018 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 VersionManager { - public: - static Result GetFirmwareVersion(u64 title_id, SetSysFirmwareVersion *out); -}; diff --git a/stratosphere/set_mitm/source/setsys_mitm_service.cpp b/stratosphere/set_mitm/source/setsys_mitm_service.cpp deleted file mode 100644 index e12e3d5..0000000 --- a/stratosphere/set_mitm/source/setsys_mitm_service.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2018 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 "setsys_mitm_service.hpp" -#include "setsys_firmware_version.hpp" -#include "setsys_settings_items.hpp" - -void SetSysMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) { - /* No commands need postprocessing. */ -} - -Result SetSysMitmService::GetFirmwareVersion(OutPointerWithServerSize out) { - Result rc = VersionManager::GetFirmwareVersion(this->title_id, out.pointer); - - /* GetFirmwareVersion sanitizes these fields. */ - if (R_SUCCEEDED(rc)) { - out.pointer->revision_major = 0; - out.pointer->revision_minor = 0; - } - - return rc; -} - -Result SetSysMitmService::GetFirmwareVersion2(OutPointerWithServerSize out) { - return VersionManager::GetFirmwareVersion(this->title_id, out.pointer); -} - -Result SetSysMitmService::GetSettingsItemValueSize(Out out_size, InPointer in_name, InPointer in_key) { - char name[SET_MAX_NAME_SIZE] = {0}; - char key[SET_MAX_NAME_SIZE] = {0}; - - Result rc = SettingsItemManager::ValidateName(in_name.pointer); - if (R_FAILED(rc)) { - return rc; - } - - rc = SettingsItemManager::ValidateKey(in_key.pointer); - if (R_FAILED(rc)) { - return rc; - } - - if (in_name.num_elements < SET_MAX_NAME_SIZE) { - strncpy(name, in_name.pointer, in_name.num_elements); - } else { - strncpy(name, in_name.pointer, SET_MAX_NAME_SIZE-1); - } - - if (in_key.num_elements < SET_MAX_NAME_SIZE) { - strncpy(key, in_key.pointer, in_key.num_elements); - } else { - strncpy(key, in_key.pointer, SET_MAX_NAME_SIZE-1); - } - - rc = SettingsItemManager::GetValueSize(name, key, out_size.GetPointer()); - if (R_FAILED(rc)) { - rc = setsysGetSettingsItemValueSize(name, key, out_size.GetPointer()); - } - - return rc; -} - -Result SetSysMitmService::GetSettingsItemValue(Out out_size, OutBuffer out_value, InPointer in_name, InPointer in_key) { - char name[SET_MAX_NAME_SIZE] = {0}; - char key[SET_MAX_NAME_SIZE] = {0}; - - Result rc = SettingsItemManager::ValidateName(in_name.pointer); - if (R_FAILED(rc)) { - return rc; - } - - rc = SettingsItemManager::ValidateKey(in_key.pointer); - if (R_FAILED(rc)) { - return rc; - } - - if (out_value.buffer == nullptr) { - return 0x19A69; - } - - if (in_name.num_elements < SET_MAX_NAME_SIZE) { - strncpy(name, in_name.pointer, in_name.num_elements); - } else { - strncpy(name, in_name.pointer, SET_MAX_NAME_SIZE-1); - } - - if (in_key.num_elements < SET_MAX_NAME_SIZE) { - strncpy(key, in_key.pointer, in_key.num_elements); - } else { - strncpy(key, in_key.pointer, SET_MAX_NAME_SIZE-1); - } - - rc = SettingsItemManager::GetValue(name, key, out_value.buffer, out_value.num_elements, out_size.GetPointer()); - - if (R_FAILED(rc)) { - rc = setsysGetSettingsItemValueFwd(this->forward_service.get(), name, key, out_value.buffer, out_value.num_elements, out_size.GetPointer()); - } - - return rc; -} - -Result SetSysMitmService::GetEdid(OutPointerWithServerSize out) { - return setsysGetEdidFwd(this->forward_service.get(), out.pointer); -} \ No newline at end of file diff --git a/stratosphere/set_mitm/source/setsys_mitm_service.hpp b/stratosphere/set_mitm/source/setsys_mitm_service.hpp deleted file mode 100644 index 36d12e0..0000000 --- a/stratosphere/set_mitm/source/setsys_mitm_service.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018 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 "setsys_shim.h" - -enum SetSysCmd : u32 { - SetSysCmd_GetFirmwareVersion = 3, - SetSysCmd_GetFirmwareVersion2 = 4, - SetSysCmd_GetSettingsItemValueSize = 37, - SetSysCmd_GetSettingsItemValue = 38, - - /* Commands for which set:sys *must* act as a passthrough. */ - /* TODO: Solve the relevant IPC detection problem. */ - SetSysCmd_GetEdid = 41, -}; - -class SetSysMitmService : public IMitmServiceObject { - public: - SetSysMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { - /* ... */ - } - - static bool ShouldMitm(u64 pid, u64 tid) { - /* Mitm everything. */ - return true; - } - - static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx); - - protected: - /* Overridden commands. */ - Result GetFirmwareVersion(OutPointerWithServerSize out); - Result GetFirmwareVersion2(OutPointerWithServerSize out); - Result GetSettingsItemValueSize(Out out_size, InPointer name, InPointer key); - Result GetSettingsItemValue(Out out_size, OutBuffer out_value, InPointer name, InPointer key); - - /* Forced passthrough commands. */ - Result GetEdid(OutPointerWithServerSize out); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - - MakeServiceCommandMeta(), - }; -}; diff --git a/stratosphere/set_mitm/source/setsys_settings_items.cpp b/stratosphere/set_mitm/source/setsys_settings_items.cpp deleted file mode 100644 index 6fbec5f..0000000 --- a/stratosphere/set_mitm/source/setsys_settings_items.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (c) 2018 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 - -#include "setsys_settings_items.hpp" -#include "ini.h" - -struct SettingsItemValue { - size_t size; - u8 *data; -}; - -std::map g_settings_items; - -static bool g_threw_fatal = false; -static HosThread g_fatal_thread; - -static void FatalThreadFunc(void *arg) { - Result rc = (Result)((uintptr_t)arg); - - svcSleepThread(5000000000ULL); - - fatalSimple(rc); -} - -static bool IsCorrectFormat(const char *str, size_t len) { - if (len > 0 && str[len - 1] == '.') { - return false; - } - - for (size_t i = 0; i < len; i++) { - const char c = *(str++); - - if ('a' <= c && c <= 'z') { - continue; - } - - if ('0' <= c && c <= '9') { - continue; - } - - if (c == '-' || c == '.' || c == '_') { - continue; - } - - return false; - } - - return true; -} - -Result SettingsItemManager::ValidateName(const char *name, size_t max_size) { - if (name == nullptr) { - return 0x19269; - } - - const size_t name_len = strnlen(name, std::min(max_size, MaxNameLength + 1)); - if (name_len == 0) { - return 0x1BA69; - } else if (name_len > MaxNameLength) { - return 0x1E269; - } - - if (!IsCorrectFormat(name, name_len)) { - return 0x20A69; - } - - return 0x0; -} - -Result SettingsItemManager::ValidateName(const char *name) { - return ValidateName(name, MaxNameLength + 1); -} - -Result SettingsItemManager::ValidateKey(const char *key, size_t max_size) { - if (key == nullptr) { - return 0x19469; - } - - const size_t key_len = strnlen(key, std::min(max_size, MaxKeyLength + 1)); - if (key_len == 0) { - return 0x1BC69; - } else if (key_len > MaxKeyLength) { - return 0x1E469; - } - - if (!IsCorrectFormat(key, key_len)) { - return 0x20C69; - } - - return 0x0; -} - -Result SettingsItemManager::ValidateKey(const char *key) { - return ValidateKey(key, MaxKeyLength + 1); -} - -static bool IsHexadecimal(const char *str) { - while (*str) { - if (isxdigit(*str)) { - str++; - } else { - return false; - } - } - return true; -} - -static char hextoi(char c) { - if ('a' <= c && c <= 'f') return c - 'a' + 0xA; - if ('A' <= c && c <= 'F') return c - 'A' + 0xA; - if ('0' <= c && c <= '9') return c - '0'; - return 0; -} - -static Result ParseValue(const char *name, const char *key, const char *val_tup) { - const char *delimiter = strchr(val_tup, '!'); - const char *value_str = delimiter + 1; - const char *type = val_tup; - - if (delimiter == NULL) { - return 0x20E69; - } - - while (isspace(*type) && type != delimiter) { - type++; - } - - size_t type_len = delimiter - type; - size_t value_len = strlen(value_str); - if (delimiter == NULL || value_len == 0 || type_len == 0) { - return 0x20E69; - } - - std::string kv = std::string(name) + "!" + std::string(key); - SettingsItemValue value; - - if (strncasecmp(type, "str", type_len) == 0 || strncasecmp(type, "string", type_len) == 0) { - /* String */ - value.size = value_len + 1; - value.data = reinterpret_cast(strdup(value_str)); - if (value.data == nullptr) { - return 0xCC69; - } - } else if (strncasecmp(type, "hex", type_len) == 0 || strncasecmp(type, "bytes", type_len) == 0) { - /* hex */ - if (value_len % 2 || !IsHexadecimal(value_str)) { - return 0x20E69; - } - value.size = value_len / 2; - u8 *data = reinterpret_cast(malloc(value.size)); - if (data == nullptr) { - return 0xCC69; - } - - memset(data, 0, value.size); - for (size_t i = 0; i < value_len; i++) { - data[i >> 1] |= hextoi(value_str[i]) << (4 * (i & 1)); - } - - value.data = data; - } else if (strncasecmp(type, "u8", type_len) == 0) { - /* u8 */ - value.size = sizeof(u8); - u8 *data = reinterpret_cast(malloc(value.size)); - if (data == nullptr) { - return 0xCC69; - } - *data = (u8)(strtoul(value_str, nullptr, 0)); - value.data = reinterpret_cast(data); - } else if (strncasecmp(type, "u16", type_len) == 0) { - /* u16 */ - value.size = sizeof(u16); - u16 *data = reinterpret_cast(malloc(value.size)); - if (data == nullptr) { - return 0xCC69; - } - *data = (u16)(strtoul(value_str, nullptr, 0)); - value.data = reinterpret_cast(data); - } else if (strncasecmp(type, "u32", type_len) == 0) { - /* u32 */ - value.size = sizeof(u32); - u32 *data = reinterpret_cast(malloc(value.size)); - if (data == nullptr) { - return 0xCC69; - } - *data = (u32)(strtoul(value_str, nullptr, 0)); - value.data = reinterpret_cast(data); - } else if (strncasecmp(type, "u64", type_len) == 0) { - /* u64 */ - value.size = sizeof(u64); - u64 *data = reinterpret_cast(malloc(value.size)); - if (data == nullptr) { - return 0xCC69; - } - *data = (u64)(strtoul(value_str, nullptr, 0)); - value.data = reinterpret_cast(data); - } else { - return 0x20E69; - } - - g_settings_items[kv] = value; - return 0x0; -} - -static int SettingsItemIniHandler(void *user, const char *name, const char *key, const char *value) { - Result rc = *(reinterpret_cast(user)); - ON_SCOPE_EXIT { *(reinterpret_cast(user)) = rc; }; - - if (R_SUCCEEDED(rc)) { - rc = SettingsItemManager::ValidateName(name); - } - if (R_SUCCEEDED(rc)) { - rc = SettingsItemManager::ValidateKey(name); - } - if (R_SUCCEEDED(rc)) { - rc = ParseValue(name, key, value); - } - - return R_SUCCEEDED(rc) ? 1 : 0; -} - -void SettingsItemManager::RefreshConfiguration() { - /* Mount the SD Card. */ - Result rc = fsInitialize(); - if (R_SUCCEEDED(rc)) { - rc = fsdevMountSdmc(); - } - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); - } - - /* When we're done, we should stop talking to FS. */ - ON_SCOPE_EXIT { - fsdevUnmountAll(); - fsExit(); - }; - - /* Open, parse config file. */ - { - FILE *config = fopen("sdmc:/atmosphere/system_settings.ini", "r"); - if (config == NULL) { - return; - } - ON_SCOPE_EXIT { fclose(config); }; - - Result rc = 0; - ini_parse_file(config, SettingsItemIniHandler, &rc); - - /* Report error if we encountered one. */ - if (R_FAILED(rc) && !g_threw_fatal) { - g_threw_fatal = true; - g_fatal_thread.Initialize(&FatalThreadFunc, reinterpret_cast(rc), 0x1000, 49); - g_fatal_thread.Start(); - } - } -} - -Result SettingsItemManager::GetValueSize(const char *name, const char *key, u64 *out_size) { - std::string kv = std::string(name) + "!" + std::string(key); - - auto it = g_settings_items.find(kv); - if (it == g_settings_items.end()) { - return 0x1669; - } - - *out_size = it->second.size; - return 0x0; -} - -Result SettingsItemManager::GetValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size) { - std::string kv = std::string(name) + "!" + std::string(key); - - auto it = g_settings_items.find(kv); - if (it == g_settings_items.end()) { - return 0x1669; - } - - size_t copy_size = it->second.size; - if (max_size < copy_size) { - copy_size = max_size; - } - *out_size = copy_size; - - memcpy(out, it->second.data, copy_size); - return 0x0; -} diff --git a/stratosphere/set_mitm/source/setsys_settings_items.hpp b/stratosphere/set_mitm/source/setsys_settings_items.hpp deleted file mode 100644 index 47abe11..0000000 --- a/stratosphere/set_mitm/source/setsys_settings_items.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018 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 SettingsItemManager { - public: - static constexpr size_t MaxNameLength = 64; - static constexpr size_t MaxKeyLength = 64; - public: - static Result ValidateName(const char *name, size_t max_size); - static Result ValidateName(const char *name); - - static Result ValidateKey(const char *key, size_t max_size); - static Result ValidateKey(const char *key); - - static void RefreshConfiguration(); - static Result GetValueSize(const char *name, const char *key, u64 *out_size); - static Result GetValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size); -}; diff --git a/stratosphere/set_mitm/source/setsys_shim.c b/stratosphere/set_mitm/source/setsys_shim.c deleted file mode 100644 index 6526494..0000000 --- a/stratosphere/set_mitm/source/setsys_shim.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2018 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 "setsys_shim.h" - -/* Command forwarders. */ -Result setsysGetEdidFwd(Service* s, SetSysEdid* out) { - IpcCommand c; - ipcInitialize(&c); - ipcAddRecvStatic(&c, out, sizeof(*out), 0); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 41; - - Result rc = serviceIpcDispatch(s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - } *resp; - - serviceIpcParse(s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - } - - return rc; -} - -Result setsysGetSettingsItemValueFwd(Service *s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out) { - char send_name[SET_MAX_NAME_SIZE]; - char send_item_key[SET_MAX_NAME_SIZE]; - - memset(send_name, 0, SET_MAX_NAME_SIZE); - memset(send_item_key, 0, SET_MAX_NAME_SIZE); - strncpy(send_name, name, SET_MAX_NAME_SIZE-1); - strncpy(send_item_key, item_key, SET_MAX_NAME_SIZE-1); - - IpcCommand c; - ipcInitialize(&c); - ipcAddSendStatic(&c, send_name, SET_MAX_NAME_SIZE, 0); - ipcAddSendStatic(&c, send_item_key, SET_MAX_NAME_SIZE, 0); - ipcAddRecvBuffer(&c, value_out, value_out_size, 0); - - struct { - u64 magic; - u64 cmd_id; - } *raw; - - raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw)); - - raw->magic = SFCI_MAGIC; - raw->cmd_id = 38; - - Result rc = serviceIpcDispatch(s); - - if (R_SUCCEEDED(rc)) { - IpcParsedCommand r; - struct { - u64 magic; - u64 result; - u64 size_out; - } *resp; - - serviceIpcParse(s, &r, sizeof(*resp)); - resp = r.Raw; - - rc = resp->result; - if (R_SUCCEEDED(rc)) { - *size_out = resp->size_out; - } - } - - return rc; -} diff --git a/stratosphere/set_mitm/source/setsys_shim.h b/stratosphere/set_mitm/source/setsys_shim.h deleted file mode 100644 index c2b5f73..0000000 --- a/stratosphere/set_mitm/source/setsys_shim.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @file setsys_shim.h - * @brief System Settings Services (set:sys) IPC wrapper. To be merged into libnx, eventually. - * @author SciresM - * @copyright libnx Authors - */ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - char edid[0x100]; -} SetSysEdid; - -/* Command forwarders. */ -Result setsysGetEdidFwd(Service* s, SetSysEdid* out); -Result setsysGetSettingsItemValueFwd(Service* s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out); - -#ifdef __cplusplus -} -#endif \ No newline at end of file