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;