diff --git a/Makefile b/Makefile index c7cb1e5b9..97c78053d 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ dist: all mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034 mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032 cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin + mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin @@ -69,6 +70,7 @@ dist: all cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag + cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; rm -r atmosphere-$(AMSVER) mkdir out diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 2e718b3ab..7bbb09435 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal +MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) diff --git a/stratosphere/dmnt/Makefile b/stratosphere/dmnt/Makefile new file mode 100644 index 000000000..71c014dc5 --- /dev/null +++ b/stratosphere/dmnt/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)\" + +#--------------------------------------------------------------------------------- +# 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/dmnt/dmnt.json b/stratosphere/dmnt/dmnt.json new file mode 100644 index 000000000..0be7e94ed --- /dev/null +++ b/stratosphere/dmnt/dmnt.json @@ -0,0 +1,117 @@ +{ + "name": "dmnt", + "title_id": "0x010000000000000d", + "title_id_range_min": "0x010000000000000d", + "title_id_range_max": "0x010000000000000d", + "main_thread_stack_size": "0x00004000", + "main_thread_priority": 39, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 1, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": [ + "pm:dmnt", + "ldr:dmnt", + "ro:dmnt", + "ns:dev", + "spl:", + "lr", + "htc", + "bsd:s", + "sfdnsres", + "bsdcfg", + "set", + "fsp-srv", + "fatal:u" + ], + "service_host": [ + "dmnt:-" + ], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "lowest_cpu_id": 0, + "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", + "svcDebugActiveProcess": "0x60", + "svcBreakDebugProcess": "0x61", + "svcTerminateDebugProcess": "0x62", + "svcGetDebugEvent": "0x63", + "svcContinueDebugEvent": "0x64", + "svcGetProcessList": "0x65", + "svcGetThreadList": "0x66", + "svcGetDebugThreadContext": "0x67", + "svcSetDebugThreadContext": "0x68", + "svcQueryDebugProcessMemory": "0x69", + "svcReadDebugProcessMemory": "0x6a", + "svcWriteDebugProcessMemory": "0x6b", + "svcSetHardwareBreakPoint": "0x6c", + "svcGetDebugThreadParam": "0x6d" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp new file mode 100644 index 000000000..fc4917faa --- /dev/null +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -0,0 +1,140 @@ +/* + * 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 "dmnt_service.hpp" + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x80000 + 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 = pmdmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = ldrDmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + /* + if (kernelAbove300()) { + rc = roDmntInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + } + */ + + rc = nsdevInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = lrInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = setInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = fsInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + rc = fsdevMountSdmc(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); +} + +void __appExit(void) { + /* Cleanup services. */ + fsdevUnmountAll(); + fsExit(); + setExit(); + lrExit(); + nsdevExit(); + /* if (kernelAbove300()) { roDmntExit(); } */ + ldrDmntExit(); + pmdmntExit(); + smExit(); +} + +int main(int argc, char **argv) +{ + consoleDebugInit(debugDevice_SVC); + + /* Nintendo uses four threads. */ + auto server_manager = new WaitableManager(4); + + /* Create services. */ + server_manager->AddWaitable(new ServiceServer("dmnt:-", 4)); + + /* Loop forever, servicing our services. */ + server_manager->Process(); + + delete server_manager; + + return 0; +} + diff --git a/stratosphere/dmnt/source/dmnt_service.hpp b/stratosphere/dmnt/source/dmnt_service.hpp new file mode 100644 index 000000000..ecb2f8f50 --- /dev/null +++ b/stratosphere/dmnt/source/dmnt_service.hpp @@ -0,0 +1,150 @@ +/* + * 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 DmntCmd { + DebugMonitor_Cmd_BreakDebugProcess = 0, + DebugMonitor_Cmd_TerminateDebugProcess = 1, + DebugMonitor_Cmd_CloseHandle = 2, + DebugMonitor_Cmd_LoadImage = 3, + DebugMonitor_Cmd_GetProcessId = 4, + DebugMonitor_Cmd_GetProcessHandle = 5, + DebugMonitor_Cmd_WaitSynchronization = 6, + DebugMonitor_Cmd_GetDebugEvent = 7, + DebugMonitor_Cmd_GetProcessModuleInfo = 8, + DebugMonitor_Cmd_GetProcessList = 9, + DebugMonitor_Cmd_GetThreadList = 10, + DebugMonitor_Cmd_GetDebugThreadContext = 11, + DebugMonitor_Cmd_ContinueDebugEvent = 12, + DebugMonitor_Cmd_ReadDebugProcessMemory = 13, + DebugMonitor_Cmd_WriteDebugProcessMemory = 14, + DebugMonitor_Cmd_SetDebugThreadContext = 15, + DebugMonitor_Cmd_GetDebugThreadParam = 16, + DebugMonitor_Cmd_InitializeThreadInfo = 17, + DebugMonitor_Cmd_SetHardwareBreakPoint = 18, + DebugMonitor_Cmd_QueryDebugProcessMemory = 19, + DebugMonitor_Cmd_GetProcessMemoryDetails = 20, + DebugMonitor_Cmd_AttachByProgramId = 21, + DebugMonitor_Cmd_AttachOnLaunch = 22, + DebugMonitor_Cmd_GetDebugMonitorProcessId = 23, + DebugMonitor_Cmd_GetJitDebugProcessList = 25, + DebugMonitor_Cmd_CreateCoreDump = 26, + DebugMonitor_Cmd_GetAllDebugThreadInfo = 27, + DebugMonitor_Cmd_TargetIO_FileOpen = 29, + DebugMonitor_Cmd_TargetIO_FileClose = 30, + DebugMonitor_Cmd_TargetIO_FileRead = 31, + DebugMonitor_Cmd_TargetIO_FileWrite = 32, + DebugMonitor_Cmd_TargetIO_FileSetAttributes = 33, + DebugMonitor_Cmd_TargetIO_FileGetInformation = 34, + DebugMonitor_Cmd_TargetIO_FileSetTime = 35, + DebugMonitor_Cmd_TargetIO_FileSetSize = 36, + DebugMonitor_Cmd_TargetIO_FileDelete = 37, + DebugMonitor_Cmd_TargetIO_FileMove = 38, + DebugMonitor_Cmd_TargetIO_DirectoryCreate = 39, + DebugMonitor_Cmd_TargetIO_DirectoryDelete = 40, + DebugMonitor_Cmd_TargetIO_DirectoryRename = 41, + DebugMonitor_Cmd_TargetIO_DirectoryGetCount = 42, + DebugMonitor_Cmd_TargetIO_DirectoryOpen = 43, + DebugMonitor_Cmd_TargetIO_DirectoryGetNext = 44, + DebugMonitor_Cmd_TargetIO_DirectoryClose = 45, + DebugMonitor_Cmd_TargetIO_GetFreeSpace = 46, + DebugMonitor_Cmd_TargetIO_GetVolumeInformation = 47, + DebugMonitor_Cmd_InitiateCoreDump = 48, + DebugMonitor_Cmd_ContinueCoreDump = 49, + DebugMonitor_Cmd_AddTTYToCoreDump = 50, + DebugMonitor_Cmd_AddImageToCoreDump = 51, + DebugMonitor_Cmd_CloseCoreDump = 52, + DebugMonitor_Cmd_CancelAttach = 53, +}; + +class DebugMonitorService final : public IServiceObject { + private: + Result BreakDebugProcess(Handle debug_hnd); + Result TerminateDebugProcess(Handle debug_hnd); + Result CloseHandle(Handle debug_hnd); + Result GetProcessId(Out out_pid, Handle hnd); + Result GetProcessHandle(Out out_hnd, u64 pid); + Result WaitSynchronization(Handle hnd, u64 ns); + + Result TargetIO_FileOpen(OutBuffer out_hnd, InBuffer path, int open_mode, u32 create_mode); + Result TargetIO_FileClose(InBuffer hnd); + Result TargetIO_FileRead(InBuffer hnd, OutBuffer out_data, Out out_read, u64 offset); + Result TargetIO_FileWrite(InBuffer hnd, InBuffer data, Out out_written, u64 offset); + Result TargetIO_FileSetAttributes(InBuffer path, InBuffer attributes); + Result TargetIO_FileGetInformation(InBuffer path, OutBuffer out_info, Out is_directory); + Result TargetIO_FileSetTime(InBuffer path, u64 create, u64 access, u64 modify); + Result TargetIO_FileSetSize(InBuffer path, u64 size); + Result TargetIO_FileDelete(InBuffer path); + Result TargetIO_FileMove(InBuffer path0, InBuffer path1); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + //MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + // MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/dmnt/source/dmnt_service_debug.cpp b/stratosphere/dmnt/source/dmnt_service_debug.cpp new file mode 100644 index 000000000..2c384a910 --- /dev/null +++ b/stratosphere/dmnt/source/dmnt_service_debug.cpp @@ -0,0 +1,52 @@ +/* + * 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 "dmnt_service.hpp" + +Result DebugMonitorService::BreakDebugProcess(Handle debug_hnd) { + /* Nintendo discards the output of this command, but we will return it. */ + return svcBreakDebugProcess(debug_hnd); +} + +Result DebugMonitorService::TerminateDebugProcess(Handle debug_hnd) { + /* Nintendo discards the output of this command, but we will return it. */ + return svcTerminateDebugProcess(debug_hnd); +} + +Result DebugMonitorService::CloseHandle(Handle debug_hnd) { + /* Nintendo discards the output of this command, but we will return it. */ + /* This command is, entertainingly, also pretty unsafe in general... */ + return svcCloseHandle(debug_hnd); +} + +Result DebugMonitorService::GetProcessId(Out out_pid, Handle hnd) { + /* Nintendo discards the output of this command, but we will return it. */ + return svcGetProcessId(out_pid.GetPointer(), hnd); +} + +Result DebugMonitorService::GetProcessHandle(Out out_hnd, u64 pid) { + Result rc = svcDebugActiveProcess(out_hnd.GetPointer(), pid); + if (rc == 0xF401) { + rc = 0x4B7; + } + return rc; +} + +Result DebugMonitorService::WaitSynchronization(Handle hnd, u64 ns) { + /* Nintendo discards the output of this command, but we will return it. */ + return svcWaitSynchronizationSingle(hnd, ns); +} diff --git a/stratosphere/dmnt/source/dmnt_service_target_io.cpp b/stratosphere/dmnt/source/dmnt_service_target_io.cpp new file mode 100644 index 000000000..ef56cfdbd --- /dev/null +++ b/stratosphere/dmnt/source/dmnt_service_target_io.cpp @@ -0,0 +1,299 @@ +/* + * 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 "dmnt_service.hpp" + +enum TIOCreateOption : u32 { + TIOCreateOption_CreateNew = 1, + TIOCreateOption_CreateAlways = 2, + TIOCreateOption_OpenExisting = 3, + TIOCreateOption_OpenAlways = 4, + TIOCreateOption_ResetSize = 5, +}; + +/* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */ +static bool g_sd_initialized = false; +static HosMutex g_sd_lock; +static FsFileSystem g_sd_fs; + +static HosMutex g_file_handle_lock; +static u64 g_cur_fd = 0; +static std::unordered_map g_file_handles; + +static Result EnsureSdInitialized() { + std::scoped_lock lk(g_sd_lock); + if (g_sd_initialized) { + return 0; + } + + Result rc = fsMountSdcard(&g_sd_fs); + if (R_SUCCEEDED(rc)) { + g_sd_initialized = true; + } + return rc; +} + +static u64 GetFileHandle(FsFile f) { + std::scoped_lock lk(g_file_handle_lock); + + u64 fd = g_cur_fd++; + g_file_handles[fd] = f; + return fd; +} + +static Result GetFileByHandle(FsFile *out, u64 handle) { + std::scoped_lock lk(g_file_handle_lock); + if (g_file_handles.find(handle) != g_file_handles.end()) { + *out = g_file_handles[handle]; + return 0; + } + return 0x2EE202; +} + +static Result CloseFileByHandle(u64 handle) { + std::scoped_lock lk(g_file_handle_lock); + if (g_file_handles.find(handle) != g_file_handles.end()) { + fsFileClose(&g_file_handles[handle]); + g_file_handles.erase(handle); + return 0; + } + return 0x2EE202; +} + +static void FixPath(char *dst, size_t dst_size, InBuffer &path) { + dst[dst_size - 1] = 0; + strncpy(dst, "/", dst_size - 1); + + const char *src = path.buffer; + size_t src_idx = 0; + size_t dst_idx = 1; + while (src_idx < path.num_elements && (src[src_idx] == '/' || src[src_idx] == '\\')) { + src_idx++; + } + + while (src_idx < path.num_elements && dst_idx < dst_size - 1 && src[src_idx] != 0) { + if (src[src_idx] == '\\') { + dst[dst_idx] = '/'; + } else { + dst[dst_idx] = src[src_idx]; + } + + src_idx++; + dst_idx++; + } + + if (dst_idx < dst_size) { + dst[dst_idx] = 0; + } +} + +Result DebugMonitorService::TargetIO_FileOpen(OutBuffer out_hnd, InBuffer path, int open_mode, u32 create_mode) { + if (out_hnd.num_elements != 1) { + return 0xF601; + } + + Result rc = EnsureSdInitialized(); + if (R_FAILED(rc)) { + return rc; + } + + char fs_path[FS_MAX_PATH]; + FixPath(fs_path, sizeof(fs_path), path); + + if (create_mode == TIOCreateOption_CreateAlways) { + fsFsDeleteFile(&g_sd_fs, fs_path); + rc = fsFsCreateFile(&g_sd_fs, fs_path, 0, 0); + } else if (create_mode == TIOCreateOption_CreateNew) { + rc = fsFsCreateFile(&g_sd_fs, fs_path, 0, 0); + } + + if (R_FAILED(rc)) { + return rc; + } + + FsFile f; + rc = fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f); + if (R_FAILED(rc)) { + if (create_mode == TIOCreateOption_OpenAlways) { + fsFsCreateFile(&g_sd_fs, fs_path, 0, 0); + rc = fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f); + } + } + + if (R_SUCCEEDED(rc)) { + if (create_mode == TIOCreateOption_ResetSize) { + rc = fsFileSetSize(&f, 0); + } + if (R_SUCCEEDED(rc)) { + out_hnd[0] = GetFileHandle(f); + } else { + fsFileClose(&f); + } + } + + return rc; +} + +Result DebugMonitorService::TargetIO_FileClose(InBuffer hnd) { + if (hnd.num_elements != 1) { + return 0xF601; + } + + return CloseFileByHandle(hnd[0]); +} + +Result DebugMonitorService::TargetIO_FileRead(InBuffer hnd, OutBuffer out_data, Out out_read, u64 offset) { + if (hnd.num_elements != 1) { + return 0xF601; + } + + FsFile f; + Result rc = GetFileByHandle(&f, hnd[0]); + if (R_FAILED(rc)) { + return rc; + } + + size_t read = 0; + rc = fsFileRead(&f, offset, out_data.buffer, out_data.num_elements, &read); + out_read.SetValue(static_cast(read)); + return rc; +} + +Result DebugMonitorService::TargetIO_FileWrite(InBuffer hnd, InBuffer data, Out out_written, u64 offset) { + if (hnd.num_elements != 1) { + return 0xF601; + } + + FsFile f; + Result rc = GetFileByHandle(&f, hnd[0]); + if (R_FAILED(rc)) { + return rc; + } + + rc = fsFileWrite(&f, offset, data.buffer, data.num_elements); + if (R_SUCCEEDED(rc)) { + out_written.SetValue(data.num_elements); + } + + return rc; +} + +Result DebugMonitorService::TargetIO_FileSetAttributes(InBuffer path, InBuffer attributes) { + /* I don't really know why this command exists, Horizon doesn't allow you to set any attributes. */ + /* N just returns 0x0 unconditionally here. */ + return 0x0; +} + +Result DebugMonitorService::TargetIO_FileGetInformation(InBuffer path, OutBuffer out_info, Out is_directory) { + if (out_info.num_elements != 4) { + return 0xF601; + } + + Result rc = EnsureSdInitialized(); + if (R_FAILED(rc)) { + return rc; + } + + char fs_path[FS_MAX_PATH]; + FixPath(fs_path, sizeof(fs_path), path); + + for (size_t i = 0; i < out_info.num_elements; i++) { + out_info[i] = 0; + } + is_directory.SetValue(0); + + FsFile f; + rc = fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_READ, &f); + if (R_SUCCEEDED(rc)) { + ON_SCOPE_EXIT { fsFileClose(&f); }; + + /* N doesn't check this return code. */ + fsFileGetSize(&f, &out_info[0]); + + /* TODO: N does not call fsFsGetFileTimestampRaw here, but we possibly could. */ + } else { + FsDir dir; + rc = fsFsOpenDirectory(&g_sd_fs, fs_path, FS_DIROPEN_FILE | FS_DIROPEN_DIRECTORY, &dir); + if (R_SUCCEEDED(rc)) { + fsDirClose(&dir); + is_directory.SetValue(1); + } + } + + return rc; +} + +Result DebugMonitorService::TargetIO_FileSetTime(InBuffer path, u64 create, u64 access, u64 modify) { + /* This is another function that doesn't really need to exist, because Horizon doesn't let you set anything. */ + return 0x0; +} + +Result DebugMonitorService::TargetIO_FileSetSize(InBuffer input, u64 size) { + /* Why does this function take in a path and not a file handle? */ + + /* We will try to be better than N, here. N only treats input as a path. */ + if (input.num_elements == sizeof(u64)) { + FsFile f; + if (R_SUCCEEDED(GetFileByHandle(&f, reinterpret_cast(input.buffer)[0]))) { + return fsFileSetSize(&f, size); + } + } + + Result rc = EnsureSdInitialized(); + if (R_FAILED(rc)) { + return rc; + } + + char fs_path[FS_MAX_PATH]; + FixPath(fs_path, sizeof(fs_path), input); + + FsFile f; + rc = fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_WRITE, &f); + if (R_SUCCEEDED(rc)) { + rc = fsFileSetSize(&f, size); + fsFileClose(&f); + } + + return rc; +} + +Result DebugMonitorService::TargetIO_FileDelete(InBuffer path) { + Result rc = EnsureSdInitialized(); + if (R_FAILED(rc)) { + return rc; + } + + char fs_path[FS_MAX_PATH]; + FixPath(fs_path, sizeof(fs_path), path); + + return fsFsDeleteFile(&g_sd_fs, fs_path); +} + +Result DebugMonitorService::TargetIO_FileMove(InBuffer path0, InBuffer path1) { + Result rc = EnsureSdInitialized(); + if (R_FAILED(rc)) { + return rc; + } + + char fs_path0[FS_MAX_PATH]; + char fs_path1[FS_MAX_PATH]; + FixPath(fs_path0, sizeof(fs_path0), path0); + FixPath(fs_path1, sizeof(fs_path1), path1); + + return fsFsRenameFile(&g_sd_fs, fs_path0, fs_path1); +} diff --git a/stratosphere/pm/source/pm_boot2.cpp b/stratosphere/pm/source/pm_boot2.cpp index cb13a42ac..6541ae62f 100644 --- a/stratosphere/pm/source/pm_boot2.cpp +++ b/stratosphere/pm/source/pm_boot2.cpp @@ -41,6 +41,8 @@ static bool HasLaunchedTitle(Boot2KnownTitleId title_id) { return std::find(g_boot2_titles.begin(), g_boot2_titles.end(), title_id) != g_boot2_titles.end(); } +static std::vector g_launched_titles; + static bool IsHexadecimal(const char *str) { while (*str) { if (isxdigit(*str)) { @@ -52,8 +54,20 @@ static bool IsHexadecimal(const char *str) { return true; } +static bool HasLaunchedTitle(Boot2KnownTitleId title_id) { + return std::find(g_launched_titles.begin(), g_launched_titles.end(), title_id) != g_launched_titles.end(); +} + +static void SetLaunchedTitle(Boot2KnownTitleId title_id) { + g_launched_titles.push_back(title_id); +} + +static void ClearLaunchedTitles() { + g_launched_titles.clear(); +} + static void LaunchTitle(Boot2KnownTitleId title_id, FsStorageId storage_id, u32 launch_flags, u64 *pid) { - u64 local_pid; + u64 local_pid = 0; /* Don't launch a title twice during boot2. */ if (HasLaunchedTitle(title_id)) { @@ -64,15 +78,15 @@ static void LaunchTitle(Boot2KnownTitleId title_id, FsStorageId storage_id, u32 switch (rc) { case 0xCE01: /* Out of resource! */ - /* TODO: Panic(). */ + std::abort(); break; case 0xDE01: /* Out of memory! */ - /* TODO: Panic(). */ + std::abort(); break; case 0xD001: /* Limit Reached! */ - /* TODO: Panic(). */ + std::abort(); break; default: /* We don't care about other issues. */ @@ -235,8 +249,9 @@ void EmbeddedBoot2::Main() { /* Launch usb. */ LaunchTitle(Boot2KnownTitleId::usb, FsStorageId_NandSystem, 0, NULL); - /* Launch tma. */ - LaunchTitle(Boot2KnownTitleId::tma, FsStorageId_NandSystem, 0, NULL); + + /* Launch Atmosphere dmnt, using FsStorageId_None to force SD card boot. */ + LaunchTitle(Boot2KnownTitleId::dmnt, FsStorageId_None, 0, NULL); /* Launch default programs. */ for (auto &launch_program : g_additional_launch_programs) { @@ -251,7 +266,10 @@ void EmbeddedBoot2::Main() { if (titles_dir != NULL) { while ((ent = readdir(titles_dir)) != NULL) { if (strlen(ent->d_name) == 0x10 && IsHexadecimal(ent->d_name)) { - u64 title_id = strtoul(ent->d_name, NULL, 16); + Boot2KnownTitleId title_id = (Boot2KnownTitleId)strtoul(ent->d_name, NULL, 16); + if (HasLaunchedTitle(title_id)) { + continue; + } char title_path[FS_MAX_PATH] = {0}; strcpy(title_path, "sdmc:/atmosphere/titles/"); strcat(title_path, ent->d_name); @@ -259,7 +277,7 @@ void EmbeddedBoot2::Main() { FILE *f_flag = fopen(title_path, "rb"); if (f_flag != NULL) { fclose(f_flag); - LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL); + LaunchTitle(title_id, FsStorageId_None, 0, NULL); } else { /* TODO: Deprecate this in the future. */ memset(title_path, 0, FS_MAX_PATH); @@ -269,13 +287,16 @@ void EmbeddedBoot2::Main() { f_flag = fopen(title_path, "rb"); if (f_flag != NULL) { fclose(f_flag); - LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL); + LaunchTitle(title_id, FsStorageId_None, 0, NULL); } } } } closedir(titles_dir); } + + /* Free the memory used to track what boot2 launches. */ + ClearLaunchedTitles(); /* We no longer need the SD card. */ fsdevUnmountAll();