dmnt: enable experimental standalone usage of gdbstub, while starlink is in dev

This commit is contained in:
Michael Scire 2021-10-31 15:47:08 -07:00
parent a73e0e8f16
commit 29ffa4b7fd
22 changed files with 464 additions and 68 deletions

View File

@ -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

View File

@ -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<typename F>
NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
template<s64 RetryMilliSeconds = 100>
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) {

View File

@ -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);

View File

@ -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;
}
};
}

View File

@ -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);

View File

@ -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);

View File

@ -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<int>(domain), static_cast<int>(type), static_cast<int>(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());

View File

@ -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);
}

View File

@ -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 */

View File

@ -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",

View File

@ -16,7 +16,8 @@
#include <stratosphere.hpp>
#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
}

View File

@ -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;

View File

@ -15,7 +15,7 @@
*/
#pragma once
#include <stratosphere.hpp>
#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);
};
}

View File

@ -15,6 +15,7 @@
*/
#include <stratosphere.hpp>
#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);
}
}
}

View File

@ -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};

View File

@ -16,15 +16,22 @@
#include <stratosphere.hpp>
#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();

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u16>(ListenPort_GdbServer)); break;
case PortName_GdbDebugLog: addr.sin_port = socket::InetHtons(static_cast<u16>(ListenPort_GdbDebugLog)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return socket::Bind(desc, reinterpret_cast<socket::SockAddr *>(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<socket::SockAddr *>(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<socket::MsgFlag>(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<socket::MsgFlag>(flags));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
s32 GetLastError() {
switch (g_socket_mode) {
case SocketMode_Htcs: return htcs::GetLastError();
case SocketMode_Tcp: return static_cast<s32>(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();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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();
}

View File

@ -14,11 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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);

View File

@ -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; }

View File

@ -14,12 +14,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<char> HtcsSession::GetChar() {
util::optional<char> 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<int>(htcs::GetLastError()));
if (!transport::IsLastErrorEAgain()) {
AMS_DMNT2_GDB_LOG_INFO("Session %d invalid, res=%ld, err=%d\n", m_socket, res, static_cast<int>(transport::GetLastError()));
m_valid = false;
break;
}

View File

@ -15,20 +15,20 @@
*/
#pragma once
#include <stratosphere.hpp>
#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<HtcsSession *>(arg)->ReceiveThreadFunction();
static_cast<TransportSession *>(arg)->ReceiveThreadFunction();
}
void ReceiveThreadFunction();