From 29ffa4b7fd62be1b31af244f3b2bf13e188041f0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 31 Oct 2021 15:47:08 -0700 Subject: [PATCH] dmnt: enable experimental standalone usage of gdbstub, while starlink is in dev --- Makefile | 2 + .../fssystem/fssystem_utility.hpp | 10 +- .../stratosphere/socket/socket_api.hpp | 1 + .../socket/socket_system_config.hpp | 38 ++++ .../source/boot2/boot2_api.cpp | 9 + .../source/socket/impl/socket_api.hpp | 1 + .../socket/impl/socket_api.os.horizon.cpp | 18 ++ .../source/socket/socket_api.cpp | 4 + .../source/set_mitm/settings_sd_kvs.cpp | 6 + stratosphere/dmnt.gen2/dmnt.gen2.json | 2 +- .../dmnt.gen2/source/dmnt2_debug_log.cpp | 68 +++++- .../dmnt.gen2/source/dmnt2_gdb_packet_io.cpp | 4 +- .../dmnt.gen2/source/dmnt2_gdb_packet_io.hpp | 6 +- .../dmnt.gen2/source/dmnt2_gdb_server.cpp | 19 +- .../source/dmnt2_gdb_server_impl.hpp | 2 +- stratosphere/dmnt.gen2/source/dmnt2_main.cpp | 37 +++- .../source/dmnt2_transport_layer.cpp | 196 ++++++++++++++++++ .../source/dmnt2_transport_layer.hpp | 42 ++++ ...cpp => dmnt2_transport_receive_buffer.cpp} | 14 +- ...hpp => dmnt2_transport_receive_buffer.hpp} | 4 +- ...ession.cpp => dmnt2_transport_session.cpp} | 35 ++-- ...ession.hpp => dmnt2_transport_session.hpp} | 14 +- 22 files changed, 464 insertions(+), 68 deletions(-) create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_transport_layer.cpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_transport_layer.hpp rename stratosphere/dmnt.gen2/source/{dmnt2_htcs_receive_buffer.cpp => dmnt2_transport_receive_buffer.cpp} (89%) rename stratosphere/dmnt.gen2/source/{dmnt2_htcs_receive_buffer.hpp => dmnt2_transport_receive_buffer.hpp} (86%) rename stratosphere/dmnt.gen2/source/{dmnt2_htcs_session.cpp => dmnt2_transport_session.cpp} (75%) rename stratosphere/dmnt.gen2/source/{dmnt2_htcs_session.hpp => dmnt2_transport_session.hpp} (77%) diff --git a/Makefile b/Makefile index a6d747a5d..80b4e1e1d 100644 --- a/Makefile +++ b/Makefile @@ -112,6 +112,7 @@ dist-no-debug: all mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042 mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420 mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240 + mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609 mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623 cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp @@ -125,6 +126,7 @@ dist-no-debug: all cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp cp stratosphere/LogManager/LogManager.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp cp stratosphere/htc/htc.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240/exefs.nsp + cp stratosphere/dmnt.gen2/dmnt.gen2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609/exefs.nsp cp stratosphere/TioServer/TioServer.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623/exefs.nsp @build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs rm -r atmosphere-$(AMSVER)/stratosphere_romfs diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp index 0255d6277..827f094f9 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp @@ -118,12 +118,12 @@ namespace ams::fssystem { /* Copy API. */ Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size); - NX_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) { + ALWAYS_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) { return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size); } Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size); - NX_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) { + ALWAYS_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) { return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size); } @@ -148,11 +148,11 @@ namespace ams::fssystem { Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); - template - NX_INLINE Result RetryFinitelyForTargetLocked(F f) { + template + ALWAYS_INLINE Result RetryFinitelyForTargetLocked(auto f) { /* Retry up to 10 times, 100ms between retries. */ constexpr s32 MaxRetryCount = 10; - constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(100); + constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(RetryMilliSeconds); s32 remaining_retries = MaxRetryCount; while (true) { diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp index 30b5d2d22..07acdf968 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_api.hpp @@ -43,6 +43,7 @@ namespace ams::socket { s32 Shutdown(s32 desc, ShutdownMethod how); + s32 Socket(Family domain, Type type, Protocol protocol); s32 SocketExempt(Family domain, Type type, Protocol protocol); s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp index b13018ad8..c6bac93ef 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_system_config.hpp @@ -57,4 +57,42 @@ namespace ams::socket { } }; + class SystemConfigLightDefault : public Config { + public: + static constexpr size_t DefaultTcpInitialSendBufferSize = 16_KB; + static constexpr size_t DefaultTcpInitialReceiveBufferSize = 16_KB; + static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 0_KB; + static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 0_KB; + static constexpr size_t DefaultUdpSendBufferSize = 9_KB; + static constexpr size_t DefaultUdpReceiveBufferSize = 42240; + static constexpr auto DefaultSocketBufferEfficiency = 2; + static constexpr auto DefaultConcurrency = 2; + static constexpr size_t DefaultAllocatorPoolSize = 64_KB; + + static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] { + constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax)); + constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax)); + + return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize); + }(); + + static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] { + constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize); + constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize); + + return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize); + }(); + public: + constexpr SystemConfigLightDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency) + : Config(mp, mp_sz, ap, + DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize, + DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax, + DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize, + DefaultSocketBufferEfficiency, c) + { + /* Mark as system. */ + m_system = true; + } + }; + } diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index 17c90292a..a40e95f81 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -188,6 +188,12 @@ namespace ams::boot2 { return enable_htc != 0; } + bool IsStandaloneGdbstubEnabled() { + u8 enable_gdbstub = 0; + settings::fwdbg::GetSettingsItemValue(std::addressof(enable_gdbstub), sizeof(enable_gdbstub), "atmosphere", "enable_standalone_gdbstub"); + return enable_gdbstub != 0; + } + bool IsAtmosphereLogManagerEnabled() { /* If htc is enabled, ams log manager is enabled. */ if (IsHtcEnabled()) { @@ -403,6 +409,9 @@ namespace ams::boot2 { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0); LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0); LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0); + } else if (IsStandaloneGdbstubEnabled()) { + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0); + LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0); } else { LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0); LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0); diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.hpp b/libraries/libstratosphere/source/socket/impl/socket_api.hpp index f80e46759..f1dcc5512 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.hpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.hpp @@ -41,6 +41,7 @@ namespace ams::socket::impl { s32 Shutdown(s32 desc, ShutdownMethod how); + s32 Socket(Family domain, Type type, Protocol protocol); s32 SocketExempt(Family domain, Type type, Protocol protocol); s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp index db145ba69..299d762d6 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp @@ -206,6 +206,8 @@ namespace ams::socket::impl { /* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */ + g_initialized = true; + return ResultSuccess(); } @@ -422,6 +424,22 @@ namespace ams::socket::impl { return result; } + s32 Socket(Family domain, Type type, Protocol protocol) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSocket(static_cast(domain), static_cast(type), static_cast(protocol)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + s32 SocketExempt(Family domain, Type type, Protocol protocol) { /* Check pre-conditions. */ AMS_ABORT_UNLESS(IsInitialized()); diff --git a/libraries/libstratosphere/source/socket/socket_api.cpp b/libraries/libstratosphere/source/socket/socket_api.cpp index 30a84a649..bd1949d68 100644 --- a/libraries/libstratosphere/source/socket/socket_api.cpp +++ b/libraries/libstratosphere/source/socket/socket_api.cpp @@ -74,6 +74,10 @@ namespace ams::socket { return impl::Shutdown(desc, how); } + s32 Socket(Family domain, Type type, Protocol protocol) { + return impl::Socket(domain, type, protocol); + } + s32 SocketExempt(Family domain, Type type, Protocol protocol) { return impl::SocketExempt(domain, type, protocol); } diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index d9f370f31..3696ddbcd 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -378,6 +378,12 @@ namespace ams::settings::fwdbg { /* 0 = Disabled, 1 = Enabled */ R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0")); + /* Controls whether atmosphere's dmnt.gen2 gdbstub should run as a standalone via sockets. */ + /* Note that this setting is ignored (and treated as 0) when htc is enabled. */ + /* Note that this setting may disappear in the future. */ + /* 0 = Disabled, 1 = Enabled */ + R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_standalone_gdbstub", "u8!0x0")); + /* Controls whether atmosphere's log manager is enabled. */ /* Note that this setting is ignored (and treated as 1) when htc is enabled. */ /* 0 = Disabled, 1 = Enabled */ diff --git a/stratosphere/dmnt.gen2/dmnt.gen2.json b/stratosphere/dmnt.gen2/dmnt.gen2.json index 510de6223..2d5100ff0 100644 --- a/stratosphere/dmnt.gen2/dmnt.gen2.json +++ b/stratosphere/dmnt.gen2/dmnt.gen2.json @@ -15,7 +15,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs"], + "service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs", "bsd:s"], "service_host": [], "kernel_capabilities": [{ "type": "kernel_flags", diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp index 734754d0b..0d217f96c 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp @@ -16,7 +16,8 @@ #include #include "dmnt2_debug_log.hpp" -#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG +// TODO: This should be converted to use log manager. */ +//#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG #if defined(AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG) @@ -164,15 +165,78 @@ namespace ams::dmnt { namespace ams::dmnt { + //#define AMS_DMNT2_ENABLE_SD_CARD_DEBUG_LOG + + #if defined(AMS_DMNT2_ENABLE_SD_CARD_DEBUG_LOG) + + namespace { + + alignas(0x40) constinit u8 g_buffer[os::MemoryPageSize * 4]; + constinit lmem::HeapHandle g_debug_log_heap; + constinit fs::FileHandle g_debug_log_file; + + constinit os::SdkMutex g_fs_mutex; + constinit s64 g_fs_offset = 0; + + void *Allocate(size_t size) { + return lmem::AllocateFromExpHeap(g_debug_log_heap, size); + } + + void Deallocate(void *p, size_t size) { + AMS_UNUSED(size); + return lmem::FreeToExpHeap(g_debug_log_heap, p); + } + + } void InitializeDebugLog() { - /* Do nothing. */ + g_debug_log_heap = lmem::CreateExpHeap(g_buffer, sizeof(g_buffer), lmem::CreateOption_ThreadSafe); + + fs::SetAllocator(Allocate, Deallocate); + fs::InitializeForSystem(); + fs::SetEnabledAutoAbort(false); + + R_ABORT_UNLESS(fs::MountSdCard("sdmc")); + + fs::DeleteFile("sdmc:/dmnt2.log"); + R_ABORT_UNLESS(fs::CreateFile("sdmc:/dmnt2.log", 0)); + R_ABORT_UNLESS(fs::OpenFile(std::addressof(g_debug_log_file), "sdmc:/dmnt2.log", fs::OpenMode_Write | fs::OpenMode_AllowAppend)); } void DebugLog(const char *prefix, const char *fmt, ...) { /* Do nothing. */ + char buffer[0x200]; + { + const auto prefix_len = std::strlen(prefix); + std::memcpy(buffer, prefix, prefix_len); + + std::va_list vl; + va_start(vl, fmt); + util::VSNPrintf(buffer + prefix_len, sizeof(buffer) - prefix_len, fmt, vl); + va_end(vl); + } + + const auto len = std::strlen(buffer); + + std::scoped_lock lk(g_fs_mutex); + R_ABORT_UNLESS(fs::WriteFile(g_debug_log_file, g_fs_offset, buffer, len, fs::WriteOption::Flush)); + g_fs_offset += len; } + #else + + void InitializeDebugLog() { + /* ... */ + } + + void DebugLog(const char *prefix, const char *fmt, ...) { + AMS_UNUSED(prefix, fmt); + } + + #endif + + + } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp index 7eb4a8ffa..da1e0272a 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp @@ -41,7 +41,7 @@ namespace ams::dmnt { } - void GdbPacketIo::SendPacket(bool *out_break, const char *src, HtcsSession *session) { + void GdbPacketIo::SendPacket(bool *out_break, const char *src, TransportSession *session) { /* Default to not breaked. */ *out_break = false; @@ -99,7 +99,7 @@ namespace ams::dmnt { } } - char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session) { + char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, TransportSession *session) { /* Default to not breaked. */ *out_break = false; diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp index 9cac7e546..00ca5700c 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp @@ -15,7 +15,7 @@ */ #pragma once #include -#include "dmnt2_htcs_session.hpp" +#include "dmnt2_transport_session.hpp" namespace ams::dmnt { @@ -30,8 +30,8 @@ namespace ams::dmnt { void SetNoAck() { m_no_ack = true; } - void SendPacket(bool *out_break, const char *src, HtcsSession *session); - char *ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session); + void SendPacket(bool *out_break, const char *src, TransportSession *session); + char *ReceivePacket(bool *out_break, char *dst, size_t size, TransportSession *session); }; } \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp index 1428ce036..82572bab9 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp @@ -15,6 +15,7 @@ */ #include #include "dmnt2_debug_log.hpp" +#include "dmnt2_transport_layer.hpp" #include "dmnt2_gdb_server.hpp" #include "dmnt2_gdb_server_impl.hpp" @@ -36,34 +37,28 @@ namespace ams::dmnt { while (true) { /* Get a socket. */ int fd; - while ((fd = htcs::Socket()) == -1) { + while ((fd = transport::Socket()) == -1) { os::SleepThread(TimeSpan::FromSeconds(1)); } /* Ensure we cleanup the socket when we're done with it. */ ON_SCOPE_EXIT { - htcs::Close(fd); + transport::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@$gdb"); - /* Bind. */ - if (htcs::Bind(fd, std::addressof(addr)) == -1) { + if (transport::Bind(fd, transport::PortName_GdbServer) == -1) { continue; } /* Listen on our port. */ - while (htcs::Listen(fd, 0) == 0) { + while (transport::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) { + if (client_fd = transport::Accept(fd); client_fd < 0) { break; } @@ -77,7 +72,7 @@ namespace ams::dmnt { } /* Close the client socket. */ - htcs::Close(client_fd); + transport::Close(client_fd); } } } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp index 868552a96..0e5de4770 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.hpp @@ -31,7 +31,7 @@ namespace ams::dmnt { }; private: int m_socket; - HtcsSession m_session; + TransportSession m_session; GdbPacketIo m_packet_io; char *m_receive_packet{nullptr}; char *m_reply_packet{nullptr}; diff --git a/stratosphere/dmnt.gen2/source/dmnt2_main.cpp b/stratosphere/dmnt.gen2/source/dmnt2_main.cpp index 6336cc23f..ed8edb747 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_main.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_main.cpp @@ -16,15 +16,22 @@ #include #include "dmnt2_debug_log.hpp" #include "dmnt2_gdb_server.hpp" +#include "dmnt2_transport_layer.hpp" namespace ams { - namespace dmnt { + namespace { - namespace { - - alignas(0x40) constinit u8 g_htcs_buffer[4_KB]; + bool IsHtcEnabled() { + u8 enable_htc = 0; + settings::fwdbg::GetSettingsItemValue(std::addressof(enable_htc), sizeof(enable_htc), "atmosphere", "enable_htc"); + return enable_htc != 0; + } + bool IsStandaloneGdbstubEnabled() { + u8 enable_gdbstub = 0; + settings::fwdbg::GetSettingsItemValue(std::addressof(enable_gdbstub), sizeof(enable_gdbstub), "atmosphere", "enable_standalone_gdbstub"); + return enable_gdbstub != 0; } } @@ -50,11 +57,23 @@ namespace ams { os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(dmnt, Main)); AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(dmnt, Main)); - /* 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); + bool use_htcs = false, use_tcp = false; + { + R_ABORT_UNLESS(::setsysInitialize()); + ON_SCOPE_EXIT { ::setsysExit(); }; + + use_htcs = IsHtcEnabled(); + use_tcp = IsStandaloneGdbstubEnabled(); + } + + /* Initialize transport layer. */ + if (use_htcs) { + dmnt::transport::InitializeByHtcs(); + } else if (use_tcp) { + dmnt::transport::InitializeByTcp(); + } else { + return; + } /* Initialize debug log thread. */ dmnt::InitializeDebugLog(); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_transport_layer.cpp b/stratosphere/dmnt.gen2/source/dmnt2_transport_layer.cpp new file mode 100644 index 000000000..b6d473b4c --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_transport_layer.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 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_transport_layer.hpp" + +namespace ams::dmnt::transport { + + namespace { + + enum SocketMode { + SocketMode_Invalid, + SocketMode_Htcs, + SocketMode_Tcp, + }; + + constexpr inline const u16 ListenPort_GdbServer = 22225; + constexpr inline const u16 ListenPort_GdbDebugLog = 22227; + + constinit os::SdkMutex g_socket_init_mutex; + constinit SocketMode g_socket_mode = SocketMode_Invalid; + + constexpr inline size_t RequiredAlignment = std::max(os::ThreadStackAlignment, os::MemoryPageSize); + + using SocketConfigType = socket::SystemConfigLightDefault; + + /* TODO: If we ever use resolvers, increase this. */ + constexpr inline size_t SocketAllocatorSize = 4_KB; + constexpr inline size_t SocketMemoryPoolSize = util::AlignUp(SocketConfigType::PerTcpSocketWorstCaseMemoryPoolSize + SocketConfigType::PerUdpSocketWorstCaseMemoryPoolSize, os::MemoryPageSize); + + constexpr inline size_t SocketRequiredSize = util::AlignUp(SocketMemoryPoolSize + SocketAllocatorSize, os::MemoryPageSize); + + /* Declare the memory pool. */ + alignas(RequiredAlignment) constinit u8 g_socket_memory[SocketRequiredSize]; + + constexpr inline const SocketConfigType SocketConfig(g_socket_memory, SocketRequiredSize, SocketAllocatorSize, 2); + + } + + void InitializeByHtcs() { + std::scoped_lock lk(g_socket_init_mutex); + AMS_ABORT_UNLESS(g_socket_mode == SocketMode_Invalid); + + constexpr auto HtcsSocketCountMax = 8; + const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax); + AMS_ABORT_UNLESS(sizeof(g_socket_memory) >= buffer_size); + htcs::InitializeForSystem(g_socket_memory, sizeof(g_socket_memory), HtcsSocketCountMax); + + g_socket_mode = SocketMode_Htcs; + } + + void InitializeByTcp() { + std::scoped_lock lk(g_socket_init_mutex); + AMS_ABORT_UNLESS(g_socket_mode == SocketMode_Invalid); + + R_ABORT_UNLESS(socket::Initialize(SocketConfig)); + + g_socket_mode = SocketMode_Tcp; + } + + s32 Socket() { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::Socket(); + case SocketMode_Tcp: return socket::Socket(socket::Family::Af_Inet, socket::Type::Sock_Stream, socket::Protocol::IpProto_Tcp); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + s32 Close(s32 desc) { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::Close(desc); + case SocketMode_Tcp: return socket::Close(desc); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + s32 Bind(s32 desc, PortName port_name) { + switch (g_socket_mode) { + case SocketMode_Htcs: + { + htcs::SockAddrHtcs addr; + addr.family = htcs::HTCS_AF_HTCS; + addr.peer_name = htcs::GetPeerNameAny(); + switch (port_name) { + case PortName_GdbServer: std::strcpy(addr.port_name.name, "iywys@$gdb"); break; + case PortName_GdbDebugLog: std::strcpy(addr.port_name.name, "iywys@$dmnt2_log"); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return htcs::Bind(desc, std::addressof(addr)); + } + break; + case SocketMode_Tcp: + { + socket::SockAddrIn addr = {}; + addr.sin_family = socket::Family::Af_Inet; + addr.sin_addr.s_addr = socket::InAddr_Any; + + switch (port_name){ + case PortName_GdbServer: addr.sin_port = socket::InetHtons(static_cast(ListenPort_GdbServer)); break; + case PortName_GdbDebugLog: addr.sin_port = socket::InetHtons(static_cast(ListenPort_GdbDebugLog)); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return socket::Bind(desc, reinterpret_cast(std::addressof(addr)), sizeof(addr)); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + s32 Listen(s32 desc, s32 backlog_count) { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::Listen(desc, backlog_count); + case SocketMode_Tcp: return socket::Listen(desc, backlog_count); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + s32 Accept(s32 desc) { + switch (g_socket_mode) { + case SocketMode_Htcs: + { + htcs::SockAddrHtcs addr; + addr.family = htcs::HTCS_AF_HTCS; + addr.peer_name = htcs::GetPeerNameAny(); + addr.port_name.name[0] = '\x00'; + + return htcs::Accept(desc, std::addressof(addr)); + } + break; + case SocketMode_Tcp: + { + socket::SockAddrIn addr = {}; + socket::SockLenT addr_len = sizeof(addr); + + return socket::Accept(desc, reinterpret_cast(std::addressof(addr)), std::addressof(addr_len)); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + s32 Shutdown(s32 desc) { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::Shutdown(desc, htcs::HTCS_SHUT_RDWR); + case SocketMode_Tcp: return socket::Shutdown(desc, socket::ShutdownMethod::Shut_RdWr); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags) { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::Recv(desc, buffer, buffer_size, flags); + case SocketMode_Tcp: return socket::Recv(desc, buffer, buffer_size, static_cast(flags)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::Send(desc, buffer, buffer_size, flags); + case SocketMode_Tcp: return socket::Send(desc, buffer, buffer_size, static_cast(flags)); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + s32 GetLastError() { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::GetLastError(); + case SocketMode_Tcp: return static_cast(socket::GetLastError()); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + bool IsLastErrorEAgain() { + switch (g_socket_mode) { + case SocketMode_Htcs: return htcs::GetLastError() == htcs::HTCS_EAGAIN; + case SocketMode_Tcp: return socket::GetLastError() == socket::Errno::EAgain; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/stratosphere/dmnt.gen2/source/dmnt2_transport_layer.hpp b/stratosphere/dmnt.gen2/source/dmnt2_transport_layer.hpp new file mode 100644 index 000000000..364b7283a --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_transport_layer.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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::transport { + + void InitializeByHtcs(); + void InitializeByTcp(); + + enum PortName { + PortName_GdbServer, + PortName_GdbDebugLog, + }; + + s32 Socket(); + s32 Close(s32 desc); + s32 Bind(s32 desc, PortName port_name); + s32 Listen(s32 desc, s32 backlog_count); + s32 Accept(s32 desc); + s32 Shutdown(s32 desc); + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags); + + s32 GetLastError(); + bool IsLastErrorEAgain(); + +} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp b/stratosphere/dmnt.gen2/source/dmnt2_transport_receive_buffer.cpp similarity index 89% rename from stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp rename to stratosphere/dmnt.gen2/source/dmnt2_transport_receive_buffer.cpp index 5ba81b602..93ebffe68 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_transport_receive_buffer.cpp @@ -14,11 +14,11 @@ * along with this program. If not, see . */ #include -#include "dmnt2_htcs_receive_buffer.hpp" +#include "dmnt2_transport_receive_buffer.hpp" namespace ams::dmnt { - ssize_t HtcsReceiveBuffer::Read(void *dst, size_t size) { + ssize_t TransportReceiveBuffer::Read(void *dst, size_t size) { /* Acquire exclusive access to ourselves. */ std::scoped_lock lk(m_mutex); @@ -50,7 +50,7 @@ namespace ams::dmnt { return readable; } - ssize_t HtcsReceiveBuffer::Write(const void *src, size_t size) { + ssize_t TransportReceiveBuffer::Write(const void *src, size_t size) { /* Acquire exclusive access to ourselves. */ std::scoped_lock lk(m_mutex); @@ -71,7 +71,7 @@ namespace ams::dmnt { return size; } - bool HtcsReceiveBuffer::WaitToBeReadable() { + bool TransportReceiveBuffer::WaitToBeReadable() { /* Check if we're already readable. */ { std::scoped_lock lk(m_mutex); @@ -91,7 +91,7 @@ namespace ams::dmnt { return this->IsValid(); } - bool HtcsReceiveBuffer::WaitToBeReadable(TimeSpan timeout) { + bool TransportReceiveBuffer::WaitToBeReadable(TimeSpan timeout) { /* Check if we're already readable. */ { std::scoped_lock lk(m_mutex); @@ -111,7 +111,7 @@ namespace ams::dmnt { return res && this->IsValid(); } - bool HtcsReceiveBuffer::WaitToBeWritable() { + bool TransportReceiveBuffer::WaitToBeWritable() { /* Check if we're already writable. */ { std::scoped_lock lk(m_mutex); @@ -131,7 +131,7 @@ namespace ams::dmnt { return this->IsValid(); } - void HtcsReceiveBuffer::Invalidate() { + void TransportReceiveBuffer::Invalidate() { /* Acquire exclusive access to ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp b/stratosphere/dmnt.gen2/source/dmnt2_transport_receive_buffer.hpp similarity index 86% rename from stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp rename to stratosphere/dmnt.gen2/source/dmnt2_transport_receive_buffer.hpp index 3901ac487..363ff79a5 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_transport_receive_buffer.hpp @@ -18,7 +18,7 @@ namespace ams::dmnt { - class HtcsReceiveBuffer { + class TransportReceiveBuffer { public: static constexpr size_t ReceiveBufferSize = 4_KB; private: @@ -30,7 +30,7 @@ namespace ams::dmnt { size_t m_offset; bool m_valid; public: - HtcsReceiveBuffer() : m_readable_event(os::EventClearMode_ManualClear), m_writable_event(os::EventClearMode_ManualClear), m_mutex(), m_readable_size(), m_offset(), m_valid(true) { /* ... */ } + TransportReceiveBuffer() : m_readable_event(os::EventClearMode_ManualClear), m_writable_event(os::EventClearMode_ManualClear), m_mutex(), m_readable_size(), m_offset(), m_valid(true) { /* ... */ } ALWAYS_INLINE bool IsReadable() const { return m_readable_size != 0; } ALWAYS_INLINE bool IsWritable() const { return m_readable_size == 0; } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp b/stratosphere/dmnt.gen2/source/dmnt2_transport_session.cpp similarity index 75% rename from stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp rename to stratosphere/dmnt.gen2/source/dmnt2_transport_session.cpp index 48bd658fe..a500f7175 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_transport_session.cpp @@ -14,12 +14,13 @@ * along with this program. If not, see . */ #include -#include "dmnt2_htcs_session.hpp" +#include "dmnt2_transport_layer.hpp" +#include "dmnt2_transport_session.hpp" #include "dmnt2_debug_log.hpp" namespace ams::dmnt { - HtcsSession::HtcsSession(int fd) : m_socket(fd), m_valid(true) { + TransportSession::TransportSession(int fd) : m_socket(fd), m_valid(true) { /* Create our thread. */ R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, sizeof(m_receive_thread_stack), os::HighestThreadPriority - 1)); @@ -30,7 +31,7 @@ namespace ams::dmnt { AMS_DMNT2_GDB_LOG_INFO("Created Session %d\n", m_socket); } - HtcsSession::~HtcsSession() { + TransportSession::~TransportSession() { /* Note that we connected. */ AMS_DMNT2_GDB_LOG_INFO("Closing Session %d\n", m_socket); @@ -38,25 +39,25 @@ namespace ams::dmnt { m_receive_buffer.Invalidate(); /* Shutdown our socket. */ - htcs::Shutdown(m_socket, htcs::HTCS_SHUT_RDWR); + transport::Shutdown(m_socket); /* Wait for our thread. */ os::WaitThread(std::addressof(m_receive_thread)); os::DestroyThread(std::addressof(m_receive_thread)); /* Close our socket. */ - htcs::Close(m_socket); + transport::Close(m_socket); } - bool HtcsSession::WaitToBeReadable() { + bool TransportSession::WaitToBeReadable() { return m_receive_buffer.WaitToBeReadable(); } - bool HtcsSession::WaitToBeReadable(TimeSpan timeout) { + bool TransportSession::WaitToBeReadable(TimeSpan timeout) { return m_receive_buffer.WaitToBeReadable(timeout); } - util::optional HtcsSession::GetChar() { + util::optional TransportSession::GetChar() { /* Wait for us to have data. */ m_receive_buffer.WaitToBeReadable(); @@ -69,9 +70,9 @@ namespace ams::dmnt { } } - ssize_t HtcsSession::PutChar(char c) { + ssize_t TransportSession::PutChar(char c) { /* Send the character. */ - const auto sent = htcs::Send(m_socket, std::addressof(c), sizeof(c), 0); + const auto sent = transport::Send(m_socket, std::addressof(c), sizeof(c), 0); if (sent < 0) { m_valid = false; } @@ -79,13 +80,13 @@ namespace ams::dmnt { return sent; } - ssize_t HtcsSession::PutString(const char *str) { + ssize_t TransportSession::PutString(const char *str) { /* Repeatedly send until all is sent. */ const size_t len = std::strlen(str); size_t remaining = len; while (remaining > 0) { - const auto sent = htcs::Send(m_socket, str, remaining, 0); + const auto sent = transport::Send(m_socket, str, remaining, 0); if (sent >= 0) { remaining -= sent; str += sent; @@ -98,22 +99,22 @@ namespace ams::dmnt { return len; } - void HtcsSession::ReceiveThreadFunction() { + void TransportSession::ReceiveThreadFunction() { /* Create temporary buffer. */ - u8 buffer[HtcsReceiveBuffer::ReceiveBufferSize]; + u8 buffer[TransportReceiveBuffer::ReceiveBufferSize]; /* Loop receiving data. */ while (true) { /* Receive data. */ - const auto res = htcs::Recv(m_socket, buffer, sizeof(buffer), 0); + const auto res = transport::Recv(m_socket, buffer, sizeof(buffer), 0); if (res > 0) { /* Write the data to our buffer. */ m_receive_buffer.WaitToBeWritable(); m_receive_buffer.Write(buffer, res); } else { /* Otherwise, if we got an error other than "try again", we're done. */ - if (htcs::GetLastError() != htcs::HTCS_EAGAIN) { - AMS_DMNT2_GDB_LOG_INFO("Session %d invalid, res=%ld, err=%d\n", m_socket, res, static_cast(htcs::GetLastError())); + if (!transport::IsLastErrorEAgain()) { + AMS_DMNT2_GDB_LOG_INFO("Session %d invalid, res=%ld, err=%d\n", m_socket, res, static_cast(transport::GetLastError())); m_valid = false; break; } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp b/stratosphere/dmnt.gen2/source/dmnt2_transport_session.hpp similarity index 77% rename from stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp rename to stratosphere/dmnt.gen2/source/dmnt2_transport_session.hpp index b109afbe7..e19fec389 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_transport_session.hpp @@ -15,20 +15,20 @@ */ #pragma once #include -#include "dmnt2_htcs_receive_buffer.hpp" +#include "dmnt2_transport_receive_buffer.hpp" namespace ams::dmnt { - class HtcsSession { + class TransportSession { private: - alignas(os::ThreadStackAlignment) u8 m_receive_thread_stack[util::AlignUp(os::MemoryPageSize + HtcsReceiveBuffer::ReceiveBufferSize, os::ThreadStackAlignment)]; - HtcsReceiveBuffer m_receive_buffer; + alignas(os::ThreadStackAlignment) u8 m_receive_thread_stack[util::AlignUp(os::MemoryPageSize + TransportReceiveBuffer::ReceiveBufferSize, os::ThreadStackAlignment)]; + TransportReceiveBuffer m_receive_buffer; os::ThreadType m_receive_thread; int m_socket; bool m_valid; public: - HtcsSession(int fd); - ~HtcsSession(); + TransportSession(int fd); + ~TransportSession(); ALWAYS_INLINE bool IsValid() const { return m_valid; } @@ -41,7 +41,7 @@ namespace ams::dmnt { private: static void ReceiveThreadEntry(void *arg) { - static_cast(arg)->ReceiveThreadFunction(); + static_cast(arg)->ReceiveThreadFunction(); } void ReceiveThreadFunction();