2024-05-26 14:10:39 +00:00
|
|
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
|
2022-12-04 11:03:45 +00:00
|
|
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
|
|
|
2024-05-26 14:10:39 +00:00
|
|
|
#include "gdb_server.h"
|
2020-12-17 17:29:10 +00:00
|
|
|
#include "bus.h"
|
|
|
|
#include "cpu_core.h"
|
2024-05-05 10:21:54 +00:00
|
|
|
#include "cpu_core_private.h"
|
2020-12-17 17:29:10 +00:00
|
|
|
#include "system.h"
|
2023-08-23 12:06:48 +00:00
|
|
|
|
|
|
|
#include "common/log.h"
|
2023-09-20 14:32:39 +00:00
|
|
|
#include "common/small_string.h"
|
2023-08-23 12:06:48 +00:00
|
|
|
#include "common/string_util.h"
|
|
|
|
|
2024-05-26 14:10:39 +00:00
|
|
|
#include "util/sockets.h"
|
|
|
|
|
2020-12-17 17:29:10 +00:00
|
|
|
#include <functional>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <map>
|
|
|
|
#include <optional>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
2023-08-23 12:06:48 +00:00
|
|
|
|
2020-12-17 17:29:10 +00:00
|
|
|
Log_SetChannel(GDBProtocol);
|
|
|
|
|
2024-05-26 14:10:39 +00:00
|
|
|
namespace GDBProtocol {
|
|
|
|
static bool IsPacketInterrupt(std::string_view data);
|
|
|
|
static bool IsPacketContinue(std::string_view data);
|
|
|
|
|
|
|
|
static bool IsPacketComplete(std::string_view data);
|
|
|
|
static std::string ProcessPacket(std::string_view data);
|
|
|
|
} // namespace GDBProtocol
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
namespace GDBProtocol {
|
2020-12-17 17:29:10 +00:00
|
|
|
|
|
|
|
static u8* GetMemoryPointer(PhysicalMemoryAddress address, u32 length)
|
|
|
|
{
|
|
|
|
auto region = Bus::GetMemoryRegionForAddress(address);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (region)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
u8* data = GetMemoryRegionPointer(*region);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (data && (address + length <= GetMemoryRegionEnd(*region)))
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
return data + (address - GetMemoryRegionStart(*region));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
static u8 ComputeChecksum(std::string_view str)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
u8 checksum = 0;
|
2024-05-05 10:21:54 +00:00
|
|
|
for (char c : str)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
checksum = (checksum + c) % 256;
|
|
|
|
}
|
|
|
|
return checksum;
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string_view> DeserializePacket(std::string_view in)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
if ((in.size() < 4) || (in[0] != '$') || (in[in.size() - 3] != '#'))
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2024-05-05 10:21:54 +00:00
|
|
|
std::string_view data = in.substr(1, in.size() - 4);
|
2020-12-17 17:29:10 +00:00
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
u8 packetChecksum = StringUtil::FromChars<u8>(in.substr(in.size() - 2, 2), 16).value_or(0);
|
2020-12-17 17:29:10 +00:00
|
|
|
u8 computedChecksum = ComputeChecksum(data);
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
if (packetChecksum == computedChecksum)
|
|
|
|
{
|
|
|
|
return {data};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
2024-05-05 10:21:54 +00:00
|
|
|
else
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::string SerializePacket(std::string_view in)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
std::stringstream ss;
|
2023-12-13 11:06:15 +00:00
|
|
|
ss << '$' << in << '#' << TinyString::from_format("{:02x}", ComputeChecksum(in));
|
2020-12-17 17:29:10 +00:00
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// List of GDB remote protocol registers for MIPS III (excluding FP).
|
2024-05-05 10:21:54 +00:00
|
|
|
static const std::array<u32*, 38> REGISTERS{
|
2020-12-17 17:29:10 +00:00
|
|
|
&CPU::g_state.regs.r[0],
|
|
|
|
&CPU::g_state.regs.r[1],
|
|
|
|
&CPU::g_state.regs.r[2],
|
|
|
|
&CPU::g_state.regs.r[3],
|
|
|
|
&CPU::g_state.regs.r[4],
|
|
|
|
&CPU::g_state.regs.r[5],
|
|
|
|
&CPU::g_state.regs.r[6],
|
|
|
|
&CPU::g_state.regs.r[7],
|
|
|
|
&CPU::g_state.regs.r[8],
|
|
|
|
&CPU::g_state.regs.r[9],
|
|
|
|
&CPU::g_state.regs.r[10],
|
|
|
|
&CPU::g_state.regs.r[11],
|
|
|
|
&CPU::g_state.regs.r[12],
|
|
|
|
&CPU::g_state.regs.r[13],
|
|
|
|
&CPU::g_state.regs.r[14],
|
|
|
|
&CPU::g_state.regs.r[15],
|
|
|
|
&CPU::g_state.regs.r[16],
|
|
|
|
&CPU::g_state.regs.r[17],
|
|
|
|
&CPU::g_state.regs.r[18],
|
|
|
|
&CPU::g_state.regs.r[19],
|
|
|
|
&CPU::g_state.regs.r[20],
|
|
|
|
&CPU::g_state.regs.r[21],
|
|
|
|
&CPU::g_state.regs.r[22],
|
|
|
|
&CPU::g_state.regs.r[23],
|
|
|
|
&CPU::g_state.regs.r[24],
|
|
|
|
&CPU::g_state.regs.r[25],
|
|
|
|
&CPU::g_state.regs.r[26],
|
|
|
|
&CPU::g_state.regs.r[27],
|
|
|
|
&CPU::g_state.regs.r[28],
|
|
|
|
&CPU::g_state.regs.r[29],
|
|
|
|
&CPU::g_state.regs.r[30],
|
|
|
|
&CPU::g_state.regs.r[31],
|
|
|
|
|
|
|
|
&CPU::g_state.cop0_regs.sr.bits,
|
|
|
|
&CPU::g_state.regs.lo,
|
|
|
|
&CPU::g_state.regs.hi,
|
|
|
|
&CPU::g_state.cop0_regs.BadVaddr,
|
|
|
|
&CPU::g_state.cop0_regs.cause.bits,
|
2023-08-15 13:12:21 +00:00
|
|
|
&CPU::g_state.pc,
|
2020-12-17 17:29:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/// Number of registers in GDB remote protocol for MIPS III.
|
|
|
|
constexpr int NUM_GDB_REGISTERS = 73;
|
|
|
|
|
|
|
|
/// Get stop reason.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$_questionMark(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
return {"S02"};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get general registers.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$g(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
for (u32* reg : REGISTERS)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
// Data is in host order (little endian).
|
|
|
|
ss << StringUtil::EncodeHex(reinterpret_cast<u8*>(reg), 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pad with dummy data (FP registers stuff).
|
2024-05-05 10:21:54 +00:00
|
|
|
for (int i = 0; i < NUM_GDB_REGISTERS - static_cast<int>(REGISTERS.size()); i++)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
ss << "00000000";
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
return {ss.str()};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set general registers.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$G(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
if (data.size() == NUM_GDB_REGISTERS * 8)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
int offset = 0;
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
for (u32* reg : REGISTERS)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
// Data is in host order (little endian).
|
2024-05-05 10:21:54 +00:00
|
|
|
auto value = StringUtil::DecodeHex({data.data() + offset, 8});
|
|
|
|
if (value)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
*reg = *reinterpret_cast<u32*>(&(*value)[0]);
|
|
|
|
}
|
|
|
|
offset += 8;
|
|
|
|
}
|
|
|
|
}
|
2024-05-05 10:21:54 +00:00
|
|
|
else
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Wrong payload size for 'G' command, expected {} got {}", NUM_GDB_REGISTERS * 8, data.size());
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
return {""};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get memory.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$m(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
std::stringstream ss{std::string{data}};
|
|
|
|
std::string dataAddress, dataLength;
|
|
|
|
|
|
|
|
std::getline(ss, dataAddress, ',');
|
|
|
|
std::getline(ss, dataLength, '\0');
|
|
|
|
|
|
|
|
auto address = StringUtil::FromChars<VirtualMemoryAddress>(dataAddress, 16);
|
|
|
|
auto length = StringUtil::FromChars<u32>(dataLength, 16);
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
if (address && length)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
PhysicalMemoryAddress phys_addr = *address & CPU::PHYSICAL_MEMORY_ADDRESS_MASK;
|
|
|
|
u32 phys_length = *length;
|
|
|
|
|
|
|
|
u8* ptr_data = GetMemoryPointer(phys_addr, phys_length);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (ptr_data)
|
|
|
|
{
|
|
|
|
return {StringUtil::EncodeHex(ptr_data, phys_length)};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
}
|
2024-05-05 10:21:54 +00:00
|
|
|
return {"E00"};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set memory.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$M(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
std::stringstream ss{std::string{data}};
|
|
|
|
std::string dataAddress, dataLength, dataPayload;
|
|
|
|
|
|
|
|
std::getline(ss, dataAddress, ',');
|
|
|
|
std::getline(ss, dataLength, ':');
|
|
|
|
std::getline(ss, dataPayload, '\0');
|
|
|
|
|
|
|
|
auto address = StringUtil::FromChars<VirtualMemoryAddress>(dataAddress, 16);
|
|
|
|
auto length = StringUtil::FromChars<u32>(dataLength, 16);
|
|
|
|
auto payload = StringUtil::DecodeHex(dataPayload);
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
if (address && length && payload && (payload->size() == *length))
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
u32 phys_addr = *address & CPU::PHYSICAL_MEMORY_ADDRESS_MASK;
|
|
|
|
u32 phys_length = *length;
|
|
|
|
|
|
|
|
u8* ptr_data = GetMemoryPointer(phys_addr, phys_length);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (ptr_data)
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
memcpy(ptr_data, payload->data(), phys_length);
|
2024-05-05 10:21:54 +00:00
|
|
|
return {"OK"};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
return {"E00"};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove hardware breakpoint.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$z1(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
auto address = StringUtil::FromChars<VirtualMemoryAddress>(data, 16);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (address)
|
|
|
|
{
|
2024-02-27 13:56:35 +00:00
|
|
|
CPU::RemoveBreakpoint(CPU::BreakpointType::Execute, *address);
|
2024-05-05 10:21:54 +00:00
|
|
|
return {"OK"};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
2024-05-05 10:21:54 +00:00
|
|
|
else
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert hardware breakpoint.
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$Z1(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
auto address = StringUtil::FromChars<VirtualMemoryAddress>(data, 16);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (address)
|
|
|
|
{
|
2024-02-27 13:56:35 +00:00
|
|
|
CPU::AddBreakpoint(CPU::BreakpointType::Execute, *address, false);
|
2024-05-05 10:21:54 +00:00
|
|
|
return {"OK"};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
2024-05-05 10:21:54 +00:00
|
|
|
else
|
|
|
|
{
|
2020-12-17 17:29:10 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$vMustReplyEmpty(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
return {""};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
static std::optional<std::string> Cmd$qSupported(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
return {""};
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// List of all GDB remote protocol packets supported by us.
|
2024-05-05 10:21:54 +00:00
|
|
|
static const std::map<const char*, std::function<std::optional<std::string>(std::string_view)>> COMMANDS{
|
|
|
|
{"?", Cmd$_questionMark},
|
|
|
|
{"g", Cmd$g},
|
|
|
|
{"G", Cmd$G},
|
|
|
|
{"m", Cmd$m},
|
|
|
|
{"M", Cmd$M},
|
|
|
|
{"z0,", Cmd$z1},
|
|
|
|
{"Z0,", Cmd$Z1},
|
|
|
|
{"z1,", Cmd$z1},
|
|
|
|
{"Z1,", Cmd$Z1},
|
|
|
|
{"vMustReplyEmpty", Cmd$vMustReplyEmpty},
|
|
|
|
{"qSupported", Cmd$qSupported},
|
2020-12-17 17:29:10 +00:00
|
|
|
};
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
bool IsPacketInterrupt(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
return (data.size() >= 1) && (data[data.size() - 1] == '\003');
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
bool IsPacketContinue(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
return (data.size() >= 5) && (data.substr(data.size() - 5) == "$c#63");
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
bool IsPacketComplete(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
2024-05-05 10:21:54 +00:00
|
|
|
return ((data.size() == 1) && (data[0] == '\003')) || ((data.size() > 3) && (*(data.end() - 3) == '#'));
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
std::string ProcessPacket(std::string_view data)
|
2020-12-17 17:29:10 +00:00
|
|
|
{
|
|
|
|
std::string_view trimmedData = data;
|
|
|
|
|
|
|
|
// Eat ACKs.
|
2024-05-05 10:21:54 +00:00
|
|
|
while (!trimmedData.empty() && (trimmedData[0] == '+' || trimmedData[0] == '-'))
|
|
|
|
{
|
|
|
|
if (trimmedData[0] == '-')
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Received negative ack");
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
trimmedData = trimmedData.substr(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate packet.
|
|
|
|
auto packet = DeserializePacket(trimmedData);
|
2024-05-05 10:21:54 +00:00
|
|
|
if (!packet)
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Malformed packet '{}'", trimmedData);
|
2020-12-17 17:29:10 +00:00
|
|
|
return "-";
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
std::optional<std::string> reply = {""};
|
2020-12-17 17:29:10 +00:00
|
|
|
|
|
|
|
// Try to invoke packet command.
|
|
|
|
bool processed = false;
|
2024-05-05 10:21:54 +00:00
|
|
|
for (const auto& command : COMMANDS)
|
|
|
|
{
|
|
|
|
if (packet->starts_with(command.first))
|
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
DEBUG_LOG("Processing command '{}'", command.first);
|
2020-12-17 17:29:10 +00:00
|
|
|
|
|
|
|
// Invoke command, remove command name from payload.
|
|
|
|
reply = command.second(packet->substr(strlen(command.first)));
|
|
|
|
processed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
if (!processed)
|
2024-05-23 10:55:28 +00:00
|
|
|
WARNING_LOG("Failed to process packet '{}'", trimmedData);
|
2024-05-23 10:20:16 +00:00
|
|
|
|
2024-05-05 10:21:54 +00:00
|
|
|
return reply ? "+" + SerializePacket(*reply) : "+";
|
2020-12-17 17:29:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace GDBProtocol
|
2024-05-26 14:10:39 +00:00
|
|
|
|
|
|
|
namespace GDBServer {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class ClientSocket final : public BufferedStreamSocket
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ClientSocket(SocketMultiplexer& multiplexer, SocketDescriptor descriptor);
|
|
|
|
~ClientSocket() override;
|
|
|
|
|
|
|
|
void OnSystemPaused();
|
|
|
|
void OnSystemResumed();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void OnConnected() override;
|
|
|
|
void OnDisconnected(const Error& error) override;
|
|
|
|
void OnRead() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void SendPacket(std::string_view sv);
|
|
|
|
|
|
|
|
bool m_seen_resume = false;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
static std::shared_ptr<ListenSocket> s_gdb_listen_socket;
|
|
|
|
static std::vector<std::shared_ptr<ClientSocket>> s_gdb_clients;
|
|
|
|
} // namespace GDBServer
|
|
|
|
|
|
|
|
GDBServer::ClientSocket::ClientSocket(SocketMultiplexer& multiplexer, SocketDescriptor descriptor)
|
|
|
|
: BufferedStreamSocket(multiplexer, descriptor, 65536, 65536)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
GDBServer::ClientSocket::~ClientSocket() = default;
|
|
|
|
|
|
|
|
void GDBServer::ClientSocket::OnConnected()
|
|
|
|
{
|
|
|
|
INFO_LOG("Client {} connected.", GetRemoteAddress().ToString());
|
|
|
|
|
|
|
|
m_seen_resume = System::IsPaused();
|
|
|
|
System::PauseSystem(true);
|
|
|
|
|
|
|
|
s_gdb_clients.push_back(std::static_pointer_cast<ClientSocket>(shared_from_this()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::ClientSocket::OnDisconnected(const Error& error)
|
|
|
|
{
|
|
|
|
INFO_LOG("Client {} disconnected: {}", GetRemoteAddress().ToString(), error.GetDescription());
|
|
|
|
|
|
|
|
const auto iter = std::find_if(s_gdb_clients.begin(), s_gdb_clients.end(),
|
|
|
|
[this](const std::shared_ptr<ClientSocket>& rhs) { return (rhs.get() == this); });
|
|
|
|
if (iter == s_gdb_clients.end())
|
|
|
|
{
|
|
|
|
ERROR_LOG("Unknown GDB client disconnected? This should never happen.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
s_gdb_clients.erase(iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::ClientSocket::OnRead()
|
|
|
|
{
|
|
|
|
const std::span<const u8> buffer = AcquireReadBuffer();
|
|
|
|
if (buffer.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
size_t buffer_offset = 0;
|
|
|
|
while (buffer_offset < buffer.size())
|
|
|
|
{
|
|
|
|
size_t current_packet_size = 1;
|
|
|
|
bool packet_complete = false;
|
|
|
|
for (; (buffer_offset + current_packet_size) <= buffer.size(); current_packet_size++)
|
|
|
|
{
|
|
|
|
const std::string_view current_packet(reinterpret_cast<const char*>(buffer.data() + buffer_offset),
|
|
|
|
current_packet_size);
|
|
|
|
|
|
|
|
if (GDBProtocol::IsPacketInterrupt(current_packet))
|
|
|
|
{
|
|
|
|
DEV_LOG("{} > Interrupt request", GetRemoteAddress().ToString());
|
|
|
|
System::PauseSystem(true);
|
|
|
|
packet_complete = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (GDBProtocol::IsPacketContinue(current_packet))
|
|
|
|
{
|
|
|
|
DEV_LOG("{} > Continue request", GetRemoteAddress().ToString());
|
|
|
|
System::PauseSystem(false);
|
|
|
|
packet_complete = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (GDBProtocol::IsPacketComplete(current_packet))
|
|
|
|
{
|
|
|
|
// TODO: Make this not copy.
|
|
|
|
DEV_LOG("{} > {}", GetRemoteAddress().ToString(), current_packet);
|
|
|
|
SendPacket(GDBProtocol::ProcessPacket(current_packet));
|
|
|
|
packet_complete = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!packet_complete)
|
|
|
|
{
|
|
|
|
WARNING_LOG("Incomplete packet, got {} bytes.", buffer.size() - buffer_offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buffer_offset += current_packet_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseReadBuffer(buffer_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::ClientSocket::SendPacket(std::string_view sv)
|
|
|
|
{
|
|
|
|
if (sv.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
WARNING_LOG("Write: {}", sv);
|
|
|
|
if (size_t written = Write(sv.data(), sv.length()); written != sv.length())
|
|
|
|
ERROR_LOG("Only wrote {} of {} bytes.", written, sv.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::ClientSocket::OnSystemPaused()
|
|
|
|
{
|
|
|
|
if (!m_seen_resume)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_seen_resume = false;
|
|
|
|
|
|
|
|
// Generate a stop reply packet, insert '?' command to generate it.
|
|
|
|
SendPacket(GDBProtocol::ProcessPacket("$?#3f"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::ClientSocket::OnSystemResumed()
|
|
|
|
{
|
|
|
|
m_seen_resume = true;
|
|
|
|
|
|
|
|
// Send ack, in case GDB sent a continue request.
|
|
|
|
SendPacket("+");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GDBServer::Initialize(u16 port)
|
|
|
|
{
|
|
|
|
Error error;
|
|
|
|
Assert(!s_gdb_listen_socket);
|
|
|
|
|
|
|
|
const std::optional<SocketAddress> address =
|
|
|
|
SocketAddress::Parse(SocketAddress::Type::IPv4, "127.0.0.1", port, &error);
|
|
|
|
if (!address.has_value())
|
|
|
|
{
|
|
|
|
ERROR_LOG("Failed to parse address: {}", error.GetDescription());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SocketMultiplexer* multiplexer = System::GetSocketMultiplexer();
|
|
|
|
if (!multiplexer)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
s_gdb_listen_socket = multiplexer->CreateListenSocket<ClientSocket>(address.value(), &error);
|
|
|
|
if (!s_gdb_listen_socket)
|
|
|
|
{
|
|
|
|
ERROR_LOG("Failed to create listen socket: {}", error.GetDescription());
|
|
|
|
System::ReleaseSocketMultiplexer();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
INFO_LOG("GDB server is now listening on {}.", address->ToString());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GDBServer::HasAnyClients()
|
|
|
|
{
|
|
|
|
return !s_gdb_clients.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::Shutdown()
|
|
|
|
{
|
|
|
|
if (!s_gdb_listen_socket)
|
|
|
|
return;
|
|
|
|
|
|
|
|
INFO_LOG("Disconnecting {} GDB clients...", s_gdb_clients.size());
|
|
|
|
while (!s_gdb_clients.empty())
|
|
|
|
{
|
|
|
|
// maintain a reference so we don't delete while in scope
|
|
|
|
std::shared_ptr<ClientSocket> client = s_gdb_clients.back();
|
|
|
|
client->Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
INFO_LOG("Stopping GDB server.");
|
2024-07-06 11:37:01 +00:00
|
|
|
s_gdb_listen_socket->Close();
|
2024-05-26 14:10:39 +00:00
|
|
|
s_gdb_listen_socket.reset();
|
|
|
|
System::ReleaseSocketMultiplexer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::OnSystemPaused()
|
|
|
|
{
|
|
|
|
for (auto& it : s_gdb_clients)
|
|
|
|
it->OnSystemPaused();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GDBServer::OnSystemResumed()
|
|
|
|
{
|
|
|
|
for (auto& it : s_gdb_clients)
|
|
|
|
it->OnSystemResumed();
|
|
|
|
}
|