Duckstation/src/core/gdb_protocol.cpp

348 lines
8.2 KiB
C++
Raw Normal View History

// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
2020-12-17 17:29:10 +00:00
#include "gdb_protocol.h"
#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"
#include "common/small_string.h"
2023-08-23 12:06:48 +00:00
#include "common/string_util.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-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;
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,
&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)
{
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)
{
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-05 10:21:54 +00:00
return reply ? "+" + SerializePacket(*reply) : "+";
2020-12-17 17:29:10 +00:00
}
} // namespace GDBProtocol