From db7268de2e803f6dd7e9b4f09cde5496be7b1fef Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 15 Jul 2021 21:38:00 -0700 Subject: [PATCH] dmnt2: add logging logic, for use with gdbstub development --- stratosphere/Makefile | 2 +- stratosphere/dmnt.gen2/Makefile | 113 +++++++++++ stratosphere/dmnt.gen2/dmnt.gen2.json | 103 ++++++++++ .../dmnt.gen2/source/dmnt2_debug_log.cpp | 180 ++++++++++++++++++ .../dmnt.gen2/source/dmnt2_debug_log.hpp | 27 +++ stratosphere/dmnt.gen2/source/dmnt2_main.cpp | 156 +++++++++++++++ 6 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 stratosphere/dmnt.gen2/Makefile create mode 100644 stratosphere/dmnt.gen2/dmnt.gen2.json create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_main.cpp diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 4bb30a81a..082453c83 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader ncm pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2 erpt pgl jpegdec LogManager cs htc TioServer +MODULES := loader ncm pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2 erpt pgl jpegdec LogManager cs htc TioServer dmnt.gen2 SUBFOLDERS := $(MODULES) diff --git a/stratosphere/dmnt.gen2/Makefile b/stratosphere/dmnt.gen2/Makefile new file mode 100644 index 000000000..aab86a63a --- /dev/null +++ b/stratosphere/dmnt.gen2/Makefile @@ -0,0 +1,113 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# 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 := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),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.gen2/dmnt.gen2.json b/stratosphere/dmnt.gen2/dmnt.gen2.json new file mode 100644 index 000000000..b6dc58e4e --- /dev/null +++ b/stratosphere/dmnt.gen2/dmnt.gen2.json @@ -0,0 +1,103 @@ +{ + "name": "dmnt.gen2", + "title_id": "0x010000000000D609", + "title_id_range_min": "0x010000000000D609", + "title_id_range_max": "0x010000000000D609", + "main_thread_stack_size": "0x00001000", + "main_thread_priority": 39, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 3, + "disable_device_address_space_merge": true, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs"], + "service_host": [], + "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", + "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", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 0 + }] +} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp new file mode 100644 index 000000000..3ec85eddd --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018-2020 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 "dmnt2_debug_log.hpp" + +#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG + +#if defined(AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG) + +namespace ams::dmnt { + + namespace { + + constexpr size_t NumLogBuffers = 16; + constexpr size_t LogBufferSize = 0x100; + + /* TODO: This shouldn't be so high priority. Come up with a better number? */ + constexpr inline auto LogThreadPriority = 10; + + constinit os::MessageQueueType g_buf_mq; + constinit os::MessageQueueType g_req_mq; + + alignas(os::ThreadStackAlignment) u8 g_log_thread_stack[os::MemoryPageSize]; + + constinit char g_log_buffers[NumLogBuffers][LogBufferSize]; + + constinit uintptr_t g_buf_mq_storage[NumLogBuffers]; + constinit uintptr_t g_req_mq_storage[NumLogBuffers]; + + constinit os::ThreadType g_log_thread; + + util::optional GetLogBuffer() { + /* Try to get a log buffer. */ + uintptr_t address; + if (os::TryReceiveMessageQueue(std::addressof(address), std::addressof(g_buf_mq))) { + return reinterpret_cast(address); + } else { + return util::nullopt; + } + } + + /* TODO: This is a hack because we don't have a proper LogManager system module. */ + /* Eventually, this should be refactored to use normal logging. */ + void DebugLogThreadFunction(void *) { + /* Loop forever, servicing our log server. */ + while (true) { + /* Get a socket. */ + int fd; + while ((fd = htcs::Socket()) == -1) { + os::SleepThread(TimeSpan::FromSeconds(1)); + } + + /* Ensure we cleanup the socket when we're done with it. */ + ON_SCOPE_EXIT { + htcs::Close(fd); + os::SleepThread(TimeSpan::FromSeconds(1)); + }; + + /* Create a sock addr for our server. */ + htcs::SockAddrHtcs addr; + addr.family = htcs::HTCS_AF_HTCS; + addr.peer_name = htcs::GetPeerNameAny(); + std::strcpy(addr.port_name.name, "iywys@$dmnt2_log"); + + /* Bind. */ + if (htcs::Bind(fd, std::addressof(addr)) == -1) { + continue; + } + + /* Listen on our port. */ + while (htcs::Listen(fd, 0) == 0) { + /* Continue accepting clients, so long as we can. */ + int client_fd; + while (true) { + /* Try to accept a client. */ + if (client_fd = htcs::Accept(fd, std::addressof(addr)); client_fd < 0) { + break; + } + + /* Handle the client. */ + while (true) { + /* Receive a log request. */ + uintptr_t log_buffer_address; + os::ReceiveMessageQueue(std::addressof(log_buffer_address), std::addressof(g_req_mq)); + + /* Ensure we manage our request properly. */ + ON_SCOPE_EXIT { os::SendMessageQueue(std::addressof(g_buf_mq), log_buffer_address); }; + + /* Send the data. */ + const char * const log_buffer = reinterpret_cast(log_buffer_address); + if (htcs::Send(client_fd, log_buffer, std::strlen(log_buffer), 0) < 0) { + break; + } + } + + /* Close the client socket. */ + htcs::Close(client_fd); + } + } + } + } + + + } + + + void InitializeDebugLog() { + /* Initialize logging message queues. */ + os::InitializeMessageQueue(std::addressof(g_buf_mq), g_buf_mq_storage, NumLogBuffers); + os::InitializeMessageQueue(std::addressof(g_req_mq), g_req_mq_storage, NumLogBuffers); + + /* Initially make all log buffers available. */ + for (size_t i = 0; i < NumLogBuffers; ++i) { + os::SendMessageQueue(std::addressof(g_buf_mq), reinterpret_cast(g_log_buffers[i])); + } + + /* Create and start the debug log thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_log_thread), DebugLogThreadFunction, nullptr, g_log_thread_stack, sizeof(g_log_thread_stack), LogThreadPriority)); + + os::StartThread(std::addressof(g_log_thread)); + } + + void DebugLog(const char *prefix, const char *fmt, ...) { + /* Try to get a log buffer. */ + const auto log_buffer_holder = GetLogBuffer(); + if (!log_buffer_holder) { + return; + } + + + /* Format into the log buffer. */ + char * const log_buffer = *log_buffer_holder; + { + const auto prefix_len = std::strlen(prefix); + std::memcpy(log_buffer, prefix, prefix_len); + + { + std::va_list vl; + va_start(vl, fmt); + util::VSNPrintf(log_buffer + prefix_len, LogBufferSize - prefix_len, fmt, vl); + va_end(vl); + } + } + + /* Request to log. */ + os::SendMessageQueue(std::addressof(g_req_mq), reinterpret_cast(log_buffer)); + } + +} + +#else + +namespace ams::dmnt { + + + void InitializeDebugLog() { + /* Do nothing. */ + } + + void DebugLog(const char *prefix, const char *fmt, ...) { + /* Do nothing. */ + } + +} + + +#endif diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp new file mode 100644 index 000000000..ed8aa2a79 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 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 + +namespace ams::dmnt { + + void InitializeDebugLog(); + + void DebugLog(const char *prefix, const char *fmt, ...); + + #define AMS_DMNT2_DEBUG_LOG(fmt, ...) ::ams::dmnt::DebugLog("[dmnt2] ", fmt, ## __VA_ARGS__) + +} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_main.cpp b/stratosphere/dmnt.gen2/source/dmnt2_main.cpp new file mode 100644 index 000000000..a2e037f16 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_main.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-2020 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 "dmnt2_debug_log.hpp" + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; + + #define INNER_HEAP_SIZE 0x0 + 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_alloc(size_t size); + void *__libnx_aligned_alloc(size_t alignment, size_t size); + void __libnx_free(void *mem); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::DmntGen2; + +} + +using namespace ams; + +#define AMS_DMNT2_SERVER_USE_FATAL_ERROR 1 + +#if AMS_DMNT2_SERVER_USE_FATAL_ERROR + +extern "C" { + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +#endif + +namespace ams::dmnt { + + namespace { + + alignas(0x40) constinit u8 g_htcs_buffer[4_KB]; + + } + +} + +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) { + hos::InitializeForStratosphere(); + + R_ABORT_UNLESS(sm::Initialize()); + + /* TODO */ + + ams::CheckApiVersion(); +} + +void __appExit(void) { + /* TODO */ +} + +namespace ams { + + void *Malloc(size_t size) { + AMS_ABORT("ams::Malloc was called"); + } + + void Free(void *ptr) { + AMS_ABORT("ams::Free was called"); + } + +} + +void *operator new(size_t size) { + AMS_ABORT("operator new(size_t) was called"); +} + +void operator delete(void *p) { + AMS_ABORT("operator delete(void *) was called"); +} + +void *__libnx_alloc(size_t size) { + AMS_ABORT("__libnx_alloc was called"); +} + +void *__libnx_aligned_alloc(size_t alignment, size_t size) { + AMS_ABORT("__libnx_aligned_alloc was called"); +} + +void __libnx_free(void *mem) { + AMS_ABORT("__libnx_free was called"); +} + +int main(int argc, char **argv) +{ + /* TODO ThreadName */ + + /* Initialize htcs. */ + constexpr auto HtcsSocketCountMax = 8; + const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax); + AMS_ABORT_UNLESS(sizeof(dmnt::g_htcs_buffer) >= buffer_size); + htcs::InitializeForSystem(dmnt::g_htcs_buffer, buffer_size, HtcsSocketCountMax); + + /* Initialize debug log thread. */ + dmnt::InitializeDebugLog(); + + /* TODO: Start GdbServer. */ + + /* TODO */ + u64 i = 0; + while (true) { + AMS_DMNT2_DEBUG_LOG("Log #%llu\n", i++); + os::SleepThread(TimeSpan::FromSeconds(1)); + } + + return 0; +}