mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-24 03:06:17 +00:00
dmnt: add basic gdb packet receive logic
This commit is contained in:
parent
e96d9950d2
commit
6da88a436f
11 changed files with 752 additions and 8 deletions
|
@ -24,11 +24,10 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr size_t NumLogBuffers = 16;
|
constexpr size_t NumLogBuffers = 0x200;
|
||||||
constexpr size_t LogBufferSize = 0x100;
|
constexpr size_t LogBufferSize = 0x100;
|
||||||
|
|
||||||
/* TODO: This shouldn't be so high priority. Come up with a better number? */
|
constexpr inline auto LogThreadPriority = os::HighestThreadPriority - 2;
|
||||||
constexpr inline auto LogThreadPriority = 10;
|
|
||||||
|
|
||||||
constinit os::MessageQueueType g_buf_mq;
|
constinit os::MessageQueueType g_buf_mq;
|
||||||
constinit os::MessageQueueType g_req_mq;
|
constinit os::MessageQueueType g_req_mq;
|
||||||
|
|
|
@ -20,8 +20,13 @@ namespace ams::dmnt {
|
||||||
|
|
||||||
void InitializeDebugLog();
|
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_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__)
|
||||||
|
|
||||||
}
|
}
|
194
stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp
Normal file
194
stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<u8>(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<u8>(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<u8>(csum_high) << 4) | (static_cast<u8>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp
Normal file
37
stratosphere/dmnt.gen2/source/dmnt2_gdb_packet_io.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
114
stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp
Normal file
114
stratosphere/dmnt.gen2/source/dmnt2_gdb_server.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<HtcsSession> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
stratosphere/dmnt.gen2/source/dmnt2_gdb_server.hpp
Normal file
23
stratosphere/dmnt.gen2/source/dmnt2_gdb_server.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::dmnt {
|
||||||
|
|
||||||
|
void InitializeGdbServer();
|
||||||
|
|
||||||
|
}
|
146
stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp
Normal file
146
stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp
Normal file
49
stratosphere/dmnt.gen2/source/dmnt2_htcs_receive_buffer.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
127
stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp
Normal file
127
stratosphere/dmnt.gen2/source/dmnt2_htcs_session.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<char> 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<int>(htcs::GetLastError()));
|
||||||
|
m_valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invalidate our receive buffer. */
|
||||||
|
m_receive_buffer.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp
Normal file
50
stratosphere/dmnt.gen2/source/dmnt2_htcs_session.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<char> GetChar();
|
||||||
|
ssize_t PutChar(char c);
|
||||||
|
ssize_t PutString(const char *str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void ReceiveThreadEntry(void *arg) {
|
||||||
|
static_cast<HtcsSession *>(arg)->ReceiveThreadFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReceiveThreadFunction();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "dmnt2_debug_log.hpp"
|
#include "dmnt2_debug_log.hpp"
|
||||||
|
#include "dmnt2_gdb_server.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern u32 __start__;
|
extern u32 __start__;
|
||||||
|
@ -143,13 +144,12 @@ int main(int argc, char **argv)
|
||||||
/* Initialize debug log thread. */
|
/* Initialize debug log thread. */
|
||||||
dmnt::InitializeDebugLog();
|
dmnt::InitializeDebugLog();
|
||||||
|
|
||||||
/* TODO: Start GdbServer. */
|
/* Start GdbServer. */
|
||||||
|
dmnt::InitializeGdbServer();
|
||||||
|
|
||||||
/* TODO */
|
/* TODO */
|
||||||
u64 i = 0;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
AMS_DMNT2_DEBUG_LOG("Log #%llu\n", i++);
|
os::SleepThread(TimeSpan::FromDays(1));
|
||||||
os::SleepThread(TimeSpan::FromSeconds(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue