From 6da88a436fce0d487ca3bb702cec901bf2f41e97 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 16 Jul 2021 00:31:17 -0700 Subject: [PATCH] dmnt: add basic gdb packet receive logic --- .../dmnt.gen2/source/dmnt2_debug_log.cpp | 5 +- .../dmnt.gen2/source/dmnt2_debug_log.hpp | 7 +- .../dmnt.gen2/source/dmnt2_gdb_packet_io.cpp | 194 ++++++++++++++++++ .../dmnt.gen2/source/dmnt2_gdb_packet_io.hpp | 37 ++++ .../dmnt.gen2/source/dmnt2_gdb_server.cpp | 114 ++++++++++ .../dmnt.gen2/source/dmnt2_gdb_server.hpp | 23 +++ .../source/dmnt2_htcs_receive_buffer.cpp | 146 +++++++++++++ .../source/dmnt2_htcs_receive_buffer.hpp | 49 +++++ .../dmnt.gen2/source/dmnt2_htcs_session.cpp | 127 ++++++++++++ .../dmnt.gen2/source/dmnt2_htcs_session.hpp | 50 +++++ stratosphere/dmnt.gen2/source/dmnt2_main.cpp | 8 +- 11 files changed, 752 insertions(+), 8 deletions(-) create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_gdb_server.hpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp create mode 100644 stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp index 3ec85eddd..3cca92900 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.cpp @@ -24,11 +24,10 @@ namespace ams::dmnt { namespace { - constexpr size_t NumLogBuffers = 16; + constexpr size_t NumLogBuffers = 0x200; constexpr size_t LogBufferSize = 0x100; - /* TODO: This shouldn't be so high priority. Come up with a better number? */ - constexpr inline auto LogThreadPriority = 10; + constexpr inline auto LogThreadPriority = os::HighestThreadPriority - 2; constinit os::MessageQueueType g_buf_mq; constinit os::MessageQueueType g_req_mq; diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp index ed8aa2a79..3a28da752 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_log.hpp @@ -20,8 +20,13 @@ namespace ams::dmnt { void InitializeDebugLog(); - void DebugLog(const char *prefix, const char *fmt, ...); + void DebugLog(const char *prefix, const char *fmt, ...) __attribute((format(printf, 2, 3))); #define AMS_DMNT2_DEBUG_LOG(fmt, ...) ::ams::dmnt::DebugLog("[dmnt2] ", fmt, ## __VA_ARGS__) + #define AMS_DMNT2_GDB_LOG_INFO(fmt, ...) ::ams::dmnt::DebugLog("[gdb-i] ", fmt, ## __VA_ARGS__) + #define AMS_DMNT2_GDB_LOG_WARN(fmt, ...) ::ams::dmnt::DebugLog("[gdb-w] ", fmt, ## __VA_ARGS__) + #define AMS_DMNT2_GDB_LOG_ERROR(fmt, ...) ::ams::dmnt::DebugLog("[gdb-e] ", fmt, ## __VA_ARGS__) + #define AMS_DMNT2_GDB_LOG_DEBUG(fmt, ...) ::ams::dmnt::DebugLog("[gdb-d] ", fmt, ## __VA_ARGS__) + } \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp new file mode 100644 index 000000000..070ecfae0 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp @@ -0,0 +1,194 @@ +/* + * 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_gdb_packet_io.hpp" +#include "dmnt2_debug_log.hpp" + +namespace ams::dmnt { + + namespace { + + constexpr char BreakCharacter = '\x03'; /* ctrl-c */ + + constexpr int DecodeHex(char c) { + if ('a' <= c && c <= 'f') { + return 10 + (c - 'a'); + } else if ('A' <= c && c <= 'F') { + return 10 + (c - 'A'); + } else if ('0' <= c && c <= '9') { + return 0 + (c - '0'); + } else { + return -1; + } + } + + constexpr char EncodeHex(u8 v) { + return "0123456789abcdef"[v & 0xF]; + } + + } + + void GdbPacketIo::SendPacket(bool *out_break, const char *src, HtcsSession *session) { + /* Default to not breaked. */ + *out_break = false; + + /* Send a packet. */ + while (true) { + std::scoped_lock lk(m_mutex); + + size_t len = 0; + u8 checksum = 0; + + while (src[len] != 0) { + checksum += static_cast(src[len++]); + } + + char buffer[1 + GdbPacketBufferSize + 4]; + buffer[0] = '$'; + std::memcpy(buffer + 1, src, len); + buffer[1 + len] = '#'; + buffer[2 + len] = EncodeHex(checksum >> 4); + buffer[3 + len] = EncodeHex(checksum >> 0); + buffer[4 + len] = 0; + + if (session->PutString(buffer) < 0) { + /* Log (truncated) copy of packet. */ + AMS_DMNT2_GDB_LOG_ERROR("Failed to send packet %s\n", buffer); + return; + } + + if (m_no_ack) { + return; + } + + /* Check to see if we need to retransmit. */ + bool retransmit = false; + do { + if (const auto char_holder = session->GetChar(); char_holder) { + switch (*char_holder) { + case BreakCharacter: + *out_break = true; + return; + case '+': + return; + case '-': + retransmit = true; + break; + default: + break; + } + } else { + /* Log (truncated) copy of packet. */ + AMS_DMNT2_GDB_LOG_ERROR("Failed to receive ack for %s\n", buffer); + return; + } + } while (!retransmit); + } + } + + char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session) { + /* Default to not breaked. */ + *out_break = false; + + /* Receive a packet. */ + while (true) { + /* Wait for data to be available. */ + if (!session->WaitToBeReadable()) { + return nullptr; + } + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Verify that we still have data immediately available. */ + if (!session->WaitToBeReadable(0)) { + continue; + } + + /* Prepare to parse. */ + enum class State { + Initial, + PacketData, + ChecksumHigh, + ChecksumLow + }; + + State state = State::Initial; + u8 checksum = 0; + int csum_high = -1, csum_low = -1; + size_t count = 0; + + /* Read characters. */ + while (true) { + if (const auto char_holder = session->GetChar(); char_holder) { + const auto c = *char_holder; + + switch (state) { + case State::Initial: + if (c == '$') { + state = State::PacketData; + } else if (c == BreakCharacter) { + *out_break = true; + return nullptr; + } + break; + case State::PacketData: + /* TODO: Escaped characters. */ + if (c == '#') { + dst[count] = 0; + state = State::ChecksumHigh; + } else { + AMS_ABORT_UNLESS(count < size - 1); + checksum += static_cast(c); + dst[count++] = c; + } + break; + case State::ChecksumHigh: + csum_high = DecodeHex(c); + state = State::ChecksumLow; + break; + case State::ChecksumLow: + csum_low = DecodeHex(c); + + if (m_no_ack) { + return dst; + } else { + const u8 expectsum = (static_cast(csum_high) << 4) | (static_cast(csum_low) << 0); + + if (csum_high < 0 || csum_low < 0 || checksum != expectsum) { + /* Request retransmission. */ + state = State::Initial; + checksum = 0; + csum_high = -1; + csum_low = -1; + count = 0; + session->PutChar('-'); + } else { + session->PutChar('+'); + return dst; + } + } + + break; + } + } else { + return nullptr; + } + } + } + } + +} diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp new file mode 100644 index 000000000..daf23b737 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp @@ -0,0 +1,37 @@ +/* + * 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 +#include "dmnt2_htcs_session.hpp" + +namespace ams::dmnt { + + static constexpr size_t GdbPacketBufferSize = 16_KB; + + class GdbPacketIo { + private: + os::SdkMutex m_mutex; + bool m_no_ack; + public: + GdbPacketIo() : m_mutex(), m_no_ack(false) { /* ... */ } + + 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); + }; + +} \ 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 new file mode 100644 index 000000000..17b0c3df9 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp @@ -0,0 +1,114 @@ +/* + * 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_gdb_packet_io.hpp" +#include "dmnt2_gdb_server.hpp" +#include "dmnt2_debug_log.hpp" + +namespace ams::dmnt { + + namespace { + + constexpr size_t ServerThreadStackSize = util::AlignUp(3 * GdbPacketBufferSize + os::MemoryPageSize, os::ThreadStackAlignment); + + alignas(os::ThreadStackAlignment) constinit u8 g_server_thread_stack[ServerThreadStackSize]; + constinit os::ThreadType g_server_thread; + + constinit util::TypedStorage g_session; + + void OnClientSocketAccepted(int fd) { + /* Create htcs session for the socket. */ + util::ConstructAt(g_session, fd); + ON_SCOPE_EXIT { util::DestroyAt(g_session); }; + + HtcsSession *session = util::GetPointer(g_session); + + /* Create packet io handler. */ + GdbPacketIo packet_io; + + /* Process packets. */ + while (session->IsValid()) { + /* Receive a packet. */ + bool do_break = false; + char recv_buf[GdbPacketBufferSize]; + char *packet = packet_io.ReceivePacket(std::addressof(do_break), recv_buf, sizeof(recv_buf), session); + + if (!do_break && packet != nullptr) { + AMS_DMNT2_GDB_LOG_DEBUG("Received Packet %s\n", packet); + + /* TODO: Process packets. */ + packet_io.SendPacket(std::addressof(do_break), "OK", session); + } + } + } + + void GdbServerThreadFunction(void *) { + /* Loop forever, servicing our gdb 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@$gdb"); + + /* 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. */ + OnClientSocketAccepted(client_fd); + + /* Close the client socket. */ + htcs::Close(client_fd); + } + } + } + } + + + + } + + void InitializeGdbServer() { + /* Create and start gdb server thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_server_thread), GdbServerThreadFunction, nullptr, g_server_thread_stack, sizeof(g_server_thread_stack), os::HighestThreadPriority - 1)); + os::StartThread(std::addressof(g_server_thread)); + } + +} diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.hpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.hpp new file mode 100644 index 000000000..10f83194c --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server.hpp @@ -0,0 +1,23 @@ +/* + * 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 InitializeGdbServer(); + +} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp b/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp new file mode 100644 index 000000000..b5b109f16 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp @@ -0,0 +1,146 @@ +/* + * 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_htcs_receive_buffer.hpp" + +namespace ams::dmnt { + + ssize_t HtcsReceiveBuffer::Read(void *dst, size_t size) { + /* Acquire exclusive access to ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that we're readable and valid. */ + if (!(this->IsValid() && this->IsReadable())) { + return -1; + } + + /* Check that we have data to read. */ + const size_t readable = std::min(size, m_readable_size); + if (readable <= 0) { + return -1; + } + + /* Copy the data. */ + std::memcpy(dst, m_buffer + m_offset, readable); + + /* Advance our pointers. */ + m_readable_size -= readable; + m_offset += readable; + + /* Handle the case where we're done consuming. */ + if (m_readable_size == 0) { + m_offset = 0; + m_readable_event.Clear(); + m_writable_event.Signal(); + } + + return readable; + } + + ssize_t HtcsReceiveBuffer::Write(const void *src, size_t size) { + /* Acquire exclusive access to ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that we're readable and valid. */ + if (!(this->IsValid() && this->IsWritable())) { + return -1; + } + + /* Copy the data to our buffer. */ + std::memcpy(m_buffer, src, size); + + /* Set our fields. */ + m_readable_size = size; + m_offset = 0; + m_writable_event.Clear(); + m_readable_event.Signal(); + + return size; + } + + bool HtcsReceiveBuffer::WaitToBeReadable() { + /* Check if we're already readable. */ + { + std::scoped_lock lk(m_mutex); + + if (this->IsReadable()) { + return true; + } else if (!this->IsValid()) { + return false; + } else { + m_readable_event.Clear(); + } + } + + /* Wait for us to be readable. */ + m_readable_event.Wait(); + + return this->IsValid(); + } + + bool HtcsReceiveBuffer::WaitToBeReadable(TimeSpan timeout) { + /* Check if we're already readable. */ + { + std::scoped_lock lk(m_mutex); + + if (this->IsReadable()) { + return true; + } else if (!this->IsValid()) { + return false; + } else { + m_readable_event.Clear(); + } + } + + /* Wait for us to be readable. */ + const bool res = m_readable_event.TimedWait(timeout); + + return res && this->IsValid(); + } + + bool HtcsReceiveBuffer::WaitToBeWritable() { + /* Check if we're already writable. */ + { + std::scoped_lock lk(m_mutex); + + if (this->IsWritable()) { + return true; + } else if (!this->IsValid()) { + return false; + } else { + m_writable_event.Clear(); + } + } + + /* Wait for us to be writable. */ + m_writable_event.Wait(); + + return this->IsValid(); + } + + void HtcsReceiveBuffer::Invalidate() { + /* Acquire exclusive access to ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Set ourselves as invalid. */ + m_valid = false; + + /* Signal our events. */ + m_readable_event.Signal(); + m_writable_event.Signal(); + } + +} diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp b/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp new file mode 100644 index 000000000..c86c191c7 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp @@ -0,0 +1,49 @@ +/* + * 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 { + + class HtcsReceiveBuffer { + public: + static constexpr size_t ReceiveBufferSize = 4_KB; + private: + u8 m_buffer[ReceiveBufferSize]; + os::Event m_readable_event; + os::Event m_writable_event; + os::SdkMutex m_mutex; + size_t m_readable_size; + 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) { /* ... */ } + + ALWAYS_INLINE bool IsReadable() const { return m_readable_size != 0; } + ALWAYS_INLINE bool IsWritable() const { return m_readable_size == 0; } + ALWAYS_INLINE bool IsValid() const { return m_valid; } + + ssize_t Read(void *dst, size_t size); + ssize_t Write(const void *src, size_t size); + + bool WaitToBeReadable(); + bool WaitToBeReadable(TimeSpan timeout); + bool WaitToBeWritable(); + + void Invalidate(); + }; + +} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp b/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp new file mode 100644 index 000000000..d8f260721 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp @@ -0,0 +1,127 @@ +/* + * 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_htcs_session.hpp" +#include "dmnt2_debug_log.hpp" + +namespace ams::dmnt { + + HtcsSession::HtcsSession(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)); + + /* Start our thread. */ + os::StartThread(std::addressof(m_receive_thread)); + + /* Note that we connected. */ + AMS_DMNT2_GDB_LOG_INFO("Created Session %d\n", m_socket); + } + + HtcsSession::~HtcsSession() { + /* Note that we connected. */ + AMS_DMNT2_GDB_LOG_INFO("Closing Session %d\n", m_socket); + + /* Invalidate our receive buffer. */ + m_receive_buffer.Invalidate(); + + /* Shutdown our socket. */ + htcs::Shutdown(m_socket, htcs::HTCS_SHUT_RDWR); + + /* 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); + } + + bool HtcsSession::WaitToBeReadable() { + return m_receive_buffer.WaitToBeReadable(); + } + + bool HtcsSession::WaitToBeReadable(TimeSpan timeout) { + return m_receive_buffer.WaitToBeReadable(timeout); + } + + util::optional HtcsSession::GetChar() { + /* Wait for us to have data. */ + m_receive_buffer.WaitToBeReadable(); + + /* Get our data. */ + char c; + if (m_receive_buffer.Read(std::addressof(c), sizeof(c)) > 0) { + return c; + } else { + return util::nullopt; + } + } + + ssize_t HtcsSession::PutChar(char c) { + /* Send the character. */ + const auto sent = htcs::Send(m_socket, std::addressof(c), sizeof(c), 0); + if (sent < 0) { + m_valid = false; + } + + return sent; + } + + ssize_t HtcsSession::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); + if (sent >= 0) { + remaining -= sent; + str += sent; + } else { + m_valid = false; + return sent; + } + } + + return len; + } + + void HtcsSession::ReceiveThreadFunction() { + /* Create temporary buffer. */ + u8 buffer[HtcsReceiveBuffer::ReceiveBufferSize]; + + /* Loop receiving data. */ + while (true) { + /* Receive data. */ + const auto res = htcs::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())); + m_valid = false; + break; + } + } + } + + /* Invalidate our receive buffer. */ + m_receive_buffer.Invalidate(); + } + +} diff --git a/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp b/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp new file mode 100644 index 000000000..eac627959 --- /dev/null +++ b/stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp @@ -0,0 +1,50 @@ +/* + * 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 +#include "dmnt2_htcs_receive_buffer.hpp" + +namespace ams::dmnt { + + class HtcsSession { + private: + alignas(os::ThreadStackAlignment) u8 m_receive_thread_stack[util::AlignUp(os::MemoryPageSize + HtcsReceiveBuffer::ReceiveBufferSize, os::ThreadStackAlignment)]; + HtcsReceiveBuffer m_receive_buffer; + os::ThreadType m_receive_thread; + int m_socket; + bool m_valid; + public: + HtcsSession(int fd); + ~HtcsSession(); + + ALWAYS_INLINE bool IsValid() const { return m_valid; } + + bool WaitToBeReadable(); + bool WaitToBeReadable(TimeSpan timeout); + + util::optional GetChar(); + ssize_t PutChar(char c); + ssize_t PutString(const char *str); + + private: + static void ReceiveThreadEntry(void *arg) { + static_cast(arg)->ReceiveThreadFunction(); + } + + void ReceiveThreadFunction(); + }; + +} \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_main.cpp b/stratosphere/dmnt.gen2/source/dmnt2_main.cpp index a2e037f16..bcfae3223 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_main.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_main.cpp @@ -15,6 +15,7 @@ */ #include #include "dmnt2_debug_log.hpp" +#include "dmnt2_gdb_server.hpp" extern "C" { extern u32 __start__; @@ -143,13 +144,12 @@ int main(int argc, char **argv) /* Initialize debug log thread. */ dmnt::InitializeDebugLog(); - /* TODO: Start GdbServer. */ + /* Start GdbServer. */ + dmnt::InitializeGdbServer(); /* TODO */ - u64 i = 0; while (true) { - AMS_DMNT2_DEBUG_LOG("Log #%llu\n", i++); - os::SleepThread(TimeSpan::FromSeconds(1)); + os::SleepThread(TimeSpan::FromDays(1)); } return 0;