2022-02-13 14:38:59 -08:00

1121 lines
25 KiB
C++

// Copyright 2013 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
#include "Core/PowerPC/GDBStub.h"
#include <fmt/format.h>
#include <optional>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <WinSock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
typedef SSIZE_T ssize_t;
#define SHUT_RDWR SD_BOTH
#else
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#endif
#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/SocketContext.h"
#include "Common/StringUtil.h"
#include "Core/Core.h"
#include "Core/HW/CPU.h"
#include "Core/HW/Memmap.h"
#include "Core/Host.h"
#include "Core/PowerPC/BreakPoints.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCCache.h"
#include "Core/PowerPC/PowerPC.h"
namespace GDBStub
{
static std::optional<Common::SocketContext> s_socket_context;
#define GDB_BFR_MAX 10000
#define GDB_STUB_START '$'
#define GDB_STUB_END '#'
#define GDB_STUB_ACK '+'
#define GDB_STUB_NAK '-'
// We are treating software breakpoints and hardware breakpoints the same way
enum class BreakpointType
{
ExecuteSoft = 0,
ExecuteHard,
Write,
Read,
Access,
};
constexpr u32 NUM_BREAKPOINT_TYPES = 4;
constexpr int MACH_O_POWERPC = 18;
constexpr int MACH_O_POWERPC_750 = 9;
const s64 GDB_UPDATE_CYCLES = 100000;
static bool s_has_control = false;
static bool s_just_connected = false;
static int s_tmpsock = -1;
static int s_sock = -1;
static u8 s_cmd_bfr[GDB_BFR_MAX];
static u32 s_cmd_len;
static CoreTiming::EventType* s_update_event;
static const char* CommandBufferAsString()
{
return reinterpret_cast<const char*>(s_cmd_bfr);
}
// private helpers
static u8 Hex2char(u8 hex)
{
if (hex >= '0' && hex <= '9')
return hex - '0';
else if (hex >= 'a' && hex <= 'f')
return hex - 'a' + 0xa;
else if (hex >= 'A' && hex <= 'F')
return hex - 'A' + 0xa;
ERROR_LOG_FMT(GDB_STUB, "Invalid nibble: {} ({:02x})", static_cast<char>(hex), hex);
return 0;
}
static u8 Nibble2hex(u8 n)
{
n &= 0xf;
if (n < 0xa)
return '0' + n;
else
return 'A' + n - 0xa;
}
static void Mem2hex(u8* dst, u8* src, u32 len)
{
while (len-- > 0)
{
const u8 tmp = *src++;
*dst++ = Nibble2hex(tmp >> 4);
*dst++ = Nibble2hex(tmp);
}
}
static void Hex2mem(u8* dst, u8* src, u32 len)
{
while (len-- > 0)
{
*dst++ = (Hex2char(*src) << 4) | Hex2char(*(src + 1));
src += 2;
}
}
static void UpdateCallback(u64 userdata, s64 cycles_late)
{
ProcessCommands(false);
if (IsActive())
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
}
static u8 ReadByte()
{
u8 c = '+';
const ssize_t res = recv(s_sock, (char*)&c, 1, MSG_WAITALL);
if (res != 1)
{
ERROR_LOG_FMT(GDB_STUB, "recv failed : {}", res);
Deinit();
}
return c;
}
static u8 CalculateChecksum()
{
u32 len = s_cmd_len;
u8* ptr = s_cmd_bfr;
u8 c = 0;
while (len-- > 0)
c += *ptr++;
return c;
}
static void RemoveBreakpoint(BreakpointType type, u32 addr, u32 len)
{
if (type == BreakpointType::ExecuteHard || type == BreakpointType::ExecuteSoft)
{
while (PowerPC::breakpoints.IsAddressBreakPoint(addr))
{
PowerPC::breakpoints.Remove(addr);
INFO_LOG_FMT(GDB_STUB, "gdb: removed a breakpoint: {:08x} bytes at {:08x}", len, addr);
}
}
else
{
while (PowerPC::memchecks.GetMemCheck(addr, len) != nullptr)
{
PowerPC::memchecks.Remove(addr);
INFO_LOG_FMT(GDB_STUB, "gdb: removed a memcheck: {:08x} bytes at {:08x}", len, addr);
}
}
}
static void Nack()
{
const char nak = GDB_STUB_NAK;
const ssize_t res = send(s_sock, &nak, 1, 0);
if (res != 1)
ERROR_LOG_FMT(GDB_STUB, "send failed");
}
static void Ack()
{
const char ack = GDB_STUB_ACK;
const ssize_t res = send(s_sock, &ack, 1, 0);
if (res != 1)
ERROR_LOG_FMT(GDB_STUB, "send failed");
}
static void ReadCommand()
{
s_cmd_len = 0;
memset(s_cmd_bfr, 0, sizeof s_cmd_bfr);
u8 c = ReadByte();
if (c == '+')
{
// ignore ack
return;
}
else if (c == 0x03)
{
CPU::Break();
SendSignal(Signal::Sigtrap);
s_has_control = true;
INFO_LOG_FMT(GDB_STUB, "gdb: CPU::Break due to break command");
return;
}
else if (c != GDB_STUB_START)
{
WARN_LOG_FMT(GDB_STUB, "gdb: read invalid byte {:02x}", c);
return;
}
while ((c = ReadByte()) != GDB_STUB_END)
{
s_cmd_bfr[s_cmd_len++] = c;
if (s_cmd_len == sizeof s_cmd_bfr)
{
ERROR_LOG_FMT(GDB_STUB, "gdb: cmd_bfr overflow");
Nack();
return;
}
}
u8 chk_read = Hex2char(ReadByte()) << 4;
chk_read |= Hex2char(ReadByte());
const u8 chk_calc = CalculateChecksum();
if (chk_calc != chk_read)
{
ERROR_LOG_FMT(GDB_STUB,
"gdb: invalid checksum: calculated {:02x} and read {:02x} for ${}# (length: {})",
chk_calc, chk_read, CommandBufferAsString(), s_cmd_len);
s_cmd_len = 0;
Nack();
return;
}
DEBUG_LOG_FMT(GDB_STUB, "gdb: read command {} with a length of {}: {}",
static_cast<char>(s_cmd_bfr[0]), s_cmd_len, CommandBufferAsString());
Ack();
}
static bool IsDataAvailable()
{
struct timeval t;
fd_set _fds, *fds = &_fds;
FD_ZERO(fds);
FD_SET(s_sock, fds);
t.tv_sec = 0;
t.tv_usec = 20;
if (select(s_sock + 1, fds, nullptr, nullptr, &t) < 0)
{
ERROR_LOG_FMT(GDB_STUB, "select failed");
return false;
}
if (FD_ISSET(s_sock, fds))
return true;
return false;
}
static void SendReply(const char* reply)
{
if (!IsActive())
return;
memset(s_cmd_bfr, 0, sizeof s_cmd_bfr);
s_cmd_len = (u32)strlen(reply);
if (s_cmd_len + 4 > sizeof s_cmd_bfr)
ERROR_LOG_FMT(GDB_STUB, "cmd_bfr overflow in gdb_reply");
memcpy(s_cmd_bfr + 1, reply, s_cmd_len);
s_cmd_len++;
const u8 chk = CalculateChecksum();
s_cmd_len--;
s_cmd_bfr[0] = GDB_STUB_START;
s_cmd_bfr[s_cmd_len + 1] = GDB_STUB_END;
s_cmd_bfr[s_cmd_len + 2] = Nibble2hex(chk >> 4);
s_cmd_bfr[s_cmd_len + 3] = Nibble2hex(chk);
DEBUG_LOG_FMT(GDB_STUB, "gdb: reply (len: {}): {}", s_cmd_len, CommandBufferAsString());
const char* ptr = (const char*)s_cmd_bfr;
u32 left = s_cmd_len + 4;
while (left > 0)
{
const int n = send(s_sock, ptr, left, 0);
if (n < 0)
{
ERROR_LOG_FMT(GDB_STUB, "gdb: send failed");
return Deinit();
}
left -= n;
ptr += n;
}
}
static void WriteHostInfo()
{
return SendReply(
fmt::format("cputype:{};cpusubtype:{};ostype:unknown;vendor:unknown;endian:big;ptrsize:4",
MACH_O_POWERPC, MACH_O_POWERPC_750)
.c_str());
}
static void HandleQuery()
{
DEBUG_LOG_FMT(GDB_STUB, "gdb: query '{}'", CommandBufferAsString());
if (!strncmp((const char*)(s_cmd_bfr), "qAttached", strlen("qAttached")))
return SendReply("1");
if (!strcmp((const char*)(s_cmd_bfr), "qC"))
return SendReply("QC1");
if (!strcmp((const char*)(s_cmd_bfr), "qfThreadInfo"))
return SendReply("m1");
else if (!strcmp((const char*)(s_cmd_bfr), "qsThreadInfo"))
return SendReply("l");
else if (!strncmp((const char*)(s_cmd_bfr), "qThreadExtraInfo", strlen("qThreadExtraInfo")))
return SendReply("00");
else if (!strncmp((const char*)(s_cmd_bfr), "qHostInfo", strlen("qHostInfo")))
return WriteHostInfo();
else if (!strncmp((const char*)(s_cmd_bfr), "qSupported", strlen("qSupported")))
return SendReply("swbreak+;hwbreak+");
SendReply("");
}
static void HandleSetThread()
{
if (memcmp(s_cmd_bfr, "Hg-1", 4) == 0 || memcmp(s_cmd_bfr, "Hc-1", 4) == 0 ||
memcmp(s_cmd_bfr, "Hg0", 3) == 0 || memcmp(s_cmd_bfr, "Hc0", 3) == 0 ||
memcmp(s_cmd_bfr, "Hg1", 3) == 0 || memcmp(s_cmd_bfr, "Hc1", 3) == 0)
return SendReply("OK");
SendReply("E01");
}
static void HandleIsThreadAlive()
{
if (memcmp(s_cmd_bfr, "T1", 2) == 0 || memcmp(s_cmd_bfr, "T-1", 3) == 0)
return SendReply("OK");
SendReply("E01");
}
static void wbe32hex(u8* p, u32 v)
{
u32 i;
for (i = 0; i < 8; i++)
p[i] = Nibble2hex(v >> (28 - 4 * i));
}
static void wbe64hex(u8* p, u64 v)
{
u32 i;
for (i = 0; i < 16; i++)
p[i] = Nibble2hex(v >> (60 - 4 * i));
}
static u32 re32hex(u8* p)
{
u32 i;
u32 res = 0;
for (i = 0; i < 8; i++)
res = (res << 4) | Hex2char(p[i]);
return res;
}
static u64 re64hex(u8* p)
{
u32 i;
u64 res = 0;
for (i = 0; i < 16; i++)
res = (res << 4) | Hex2char(p[i]);
return res;
}
static void ReadRegister()
{
static u8 reply[64];
u32 id;
memset(reply, 0, sizeof reply);
id = Hex2char(s_cmd_bfr[1]);
if (s_cmd_bfr[2] != '\0')
{
id <<= 4;
id |= Hex2char(s_cmd_bfr[2]);
}
if (id < 32)
{
wbe32hex(reply, GPR(id));
}
else if (id >= 32 && id < 64)
{
wbe64hex(reply, rPS(id - 32).PS0AsU64());
}
else if (id >= 71 && id < 87)
{
wbe32hex(reply, PowerPC::ppcState.sr[id - 71]);
}
else if (id >= 88 && id < 104)
{
wbe32hex(reply, PowerPC::ppcState.spr[SPR_IBAT0U + id - 88]);
}
else
{
switch (id)
{
case 64:
wbe32hex(reply, PC);
break;
case 65:
wbe32hex(reply, MSR.Hex);
break;
case 66:
wbe32hex(reply, PowerPC::ppcState.cr.Get());
break;
case 67:
wbe32hex(reply, LR);
break;
case 68:
wbe32hex(reply, CTR);
break;
case 69:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_XER]);
break;
case 70:
wbe32hex(reply, FPSCR.Hex);
break;
case 87:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_PVR]);
break;
case 104:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SDR]);
break;
case 105:
wbe64hex(reply, PowerPC::ppcState.spr[SPR_ASR]);
break;
case 106:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_DAR]);
break;
case 107:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_DSISR]);
break;
case 108:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SPRG0]);
break;
case 109:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SPRG1]);
break;
case 110:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SPRG2]);
break;
case 111:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SPRG3]);
break;
case 112:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SRR0]);
break;
case 113:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SRR1]);
break;
case 114:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_TL]);
break;
case 115:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_TU]);
break;
case 116:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_DEC]);
break;
case 117:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_DABR]);
break;
case 118:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_EAR]);
break;
case 119:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_HID0]);
break;
case 120:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_HID1]);
break;
case 121:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_IABR]);
break;
case 122:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_DABR]);
break;
case 124:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_UMMCR0]);
break;
case 125:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_UPMC1]);
break;
case 126:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_UPMC2]);
break;
case 127:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_USIA]);
break;
case 128:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_UMMCR1]);
break;
case 129:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_UPMC3]);
break;
case 130:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_UPMC4]);
break;
case 131:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_MMCR0]);
break;
case 132:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_PMC1]);
break;
case 133:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_PMC2]);
break;
case 134:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_SIA]);
break;
case 135:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_MMCR1]);
break;
case 136:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_PMC3]);
break;
case 137:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_PMC4]);
break;
case 138:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_L2CR]);
break;
case 139:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_ICTC]);
break;
case 140:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_THRM1]);
break;
case 141:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_THRM2]);
break;
case 142:
wbe32hex(reply, PowerPC::ppcState.spr[SPR_THRM3]);
break;
default:
return SendReply("E01");
break;
}
}
SendReply((char*)reply);
}
static void ReadRegisters()
{
static u8 bfr[GDB_BFR_MAX - 4];
u8* bufptr = bfr;
u32 i;
memset(bfr, 0, sizeof bfr);
for (i = 0; i < 32; i++)
{
wbe32hex(bufptr + i * 8, GPR(i));
}
bufptr += 32 * 8;
SendReply((char*)bfr);
}
static void WriteRegisters()
{
u32 i;
u8* bufptr = s_cmd_bfr;
for (i = 0; i < 32; i++)
{
GPR(i) = re32hex(bufptr + i * 8);
}
bufptr += 32 * 8;
SendReply("OK");
}
static void WriteRegister()
{
u32 id;
u8* bufptr = s_cmd_bfr + 3;
id = Hex2char(s_cmd_bfr[1]);
if (s_cmd_bfr[2] != '=')
{
++bufptr;
id <<= 4;
id |= Hex2char(s_cmd_bfr[2]);
}
if (id < 32)
{
GPR(id) = re32hex(bufptr);
}
else if (id >= 32 && id < 64)
{
rPS(id - 32).SetPS0(re64hex(bufptr));
}
else if (id >= 71 && id < 87)
{
PowerPC::ppcState.sr[id - 71] = re32hex(bufptr);
}
else if (id >= 88 && id < 104)
{
PowerPC::ppcState.spr[SPR_IBAT0U + id - 88] = re32hex(bufptr);
}
else
{
switch (id)
{
case 64:
PC = re32hex(bufptr);
break;
case 65:
MSR.Hex = re32hex(bufptr);
break;
case 66:
PowerPC::ppcState.cr.Set(re32hex(bufptr));
break;
case 67:
LR = re32hex(bufptr);
break;
case 68:
CTR = re32hex(bufptr);
break;
case 69:
PowerPC::ppcState.spr[SPR_XER] = re32hex(bufptr);
break;
case 70:
FPSCR.Hex = re32hex(bufptr);
break;
case 87:
PowerPC::ppcState.spr[SPR_PVR] = re32hex(bufptr);
break;
case 104:
PowerPC::ppcState.spr[SPR_SDR] = re32hex(bufptr);
break;
case 105:
PowerPC::ppcState.spr[SPR_ASR] = re64hex(bufptr);
break;
case 106:
PowerPC::ppcState.spr[SPR_DAR] = re32hex(bufptr);
break;
case 107:
PowerPC::ppcState.spr[SPR_DSISR] = re32hex(bufptr);
break;
case 108:
PowerPC::ppcState.spr[SPR_SPRG0] = re32hex(bufptr);
break;
case 109:
PowerPC::ppcState.spr[SPR_SPRG1] = re32hex(bufptr);
break;
case 110:
PowerPC::ppcState.spr[SPR_SPRG2] = re32hex(bufptr);
break;
case 111:
PowerPC::ppcState.spr[SPR_SPRG3] = re32hex(bufptr);
break;
case 112:
PowerPC::ppcState.spr[SPR_SRR0] = re32hex(bufptr);
break;
case 113:
PowerPC::ppcState.spr[SPR_SRR1] = re32hex(bufptr);
break;
case 114:
PowerPC::ppcState.spr[SPR_TL] = re32hex(bufptr);
break;
case 115:
PowerPC::ppcState.spr[SPR_TU] = re32hex(bufptr);
break;
case 116:
PowerPC::ppcState.spr[SPR_DEC] = re32hex(bufptr);
break;
case 117:
PowerPC::ppcState.spr[SPR_DABR] = re32hex(bufptr);
break;
case 118:
PowerPC::ppcState.spr[SPR_EAR] = re32hex(bufptr);
break;
case 119:
PowerPC::ppcState.spr[SPR_HID0] = re32hex(bufptr);
break;
case 120:
PowerPC::ppcState.spr[SPR_HID1] = re32hex(bufptr);
break;
case 121:
PowerPC::ppcState.spr[SPR_IABR] = re32hex(bufptr);
break;
case 122:
PowerPC::ppcState.spr[SPR_DABR] = re32hex(bufptr);
break;
case 124:
PowerPC::ppcState.spr[SPR_UMMCR0] = re32hex(bufptr);
break;
case 125:
PowerPC::ppcState.spr[SPR_UPMC1] = re32hex(bufptr);
break;
case 126:
PowerPC::ppcState.spr[SPR_UPMC2] = re32hex(bufptr);
break;
case 127:
PowerPC::ppcState.spr[SPR_USIA] = re32hex(bufptr);
break;
case 128:
PowerPC::ppcState.spr[SPR_UMMCR1] = re32hex(bufptr);
break;
case 129:
PowerPC::ppcState.spr[SPR_UPMC3] = re32hex(bufptr);
break;
case 130:
PowerPC::ppcState.spr[SPR_UPMC4] = re32hex(bufptr);
break;
case 131:
PowerPC::ppcState.spr[SPR_MMCR0] = re32hex(bufptr);
break;
case 132:
PowerPC::ppcState.spr[SPR_PMC1] = re32hex(bufptr);
break;
case 133:
PowerPC::ppcState.spr[SPR_PMC2] = re32hex(bufptr);
break;
case 134:
PowerPC::ppcState.spr[SPR_SIA] = re32hex(bufptr);
break;
case 135:
PowerPC::ppcState.spr[SPR_MMCR1] = re32hex(bufptr);
break;
case 136:
PowerPC::ppcState.spr[SPR_PMC3] = re32hex(bufptr);
break;
case 137:
PowerPC::ppcState.spr[SPR_PMC4] = re32hex(bufptr);
break;
case 138:
PowerPC::ppcState.spr[SPR_L2CR] = re32hex(bufptr);
break;
case 139:
PowerPC::ppcState.spr[SPR_ICTC] = re32hex(bufptr);
break;
case 140:
PowerPC::ppcState.spr[SPR_THRM1] = re32hex(bufptr);
break;
case 141:
PowerPC::ppcState.spr[SPR_THRM2] = re32hex(bufptr);
break;
case 142:
PowerPC::ppcState.spr[SPR_THRM3] = re32hex(bufptr);
break;
default:
return SendReply("E01");
break;
}
}
SendReply("OK");
}
static void ReadMemory()
{
static u8 reply[GDB_BFR_MAX - 4];
u32 addr, len;
u32 i;
i = 1;
addr = 0;
while (s_cmd_bfr[i] != ',')
addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]);
i++;
len = 0;
while (i < s_cmd_len)
len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
INFO_LOG_FMT(GDB_STUB, "gdb: read memory: {:08x} bytes from {:08x}", len, addr);
if (len * 2 > sizeof reply)
SendReply("E01");
if (!PowerPC::HostIsRAMAddress(addr))
return SendReply("E00");
u8* data = Memory::GetPointer(addr);
Mem2hex(reply, data, len);
reply[len * 2] = '\0';
SendReply((char*)reply);
}
static void WriteMemory()
{
u32 addr, len;
u32 i;
i = 1;
addr = 0;
while (s_cmd_bfr[i] != ',')
addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]);
i++;
len = 0;
while (s_cmd_bfr[i] != ':')
len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
INFO_LOG_FMT(GDB_STUB, "gdb: write memory: {:08x} bytes to {:08x}", len, addr);
if (!PowerPC::HostIsRAMAddress(addr))
return SendReply("E00");
u8* dst = Memory::GetPointer(addr);
Hex2mem(dst, s_cmd_bfr + i + 1, len);
SendReply("OK");
}
static void Step()
{
CPU::EnableStepping(true);
Core::CallOnStateChangedCallbacks(Core::State::Paused);
}
static bool AddBreakpoint(BreakpointType type, u32 addr, u32 len)
{
if (type == BreakpointType::ExecuteHard || type == BreakpointType::ExecuteSoft)
{
PowerPC::breakpoints.Add(addr);
INFO_LOG_FMT(GDB_STUB, "gdb: added {} breakpoint: {:08x} bytes at {:08x}",
static_cast<int>(type), len, addr);
}
else
{
TMemCheck new_memcheck;
new_memcheck.start_address = addr;
new_memcheck.end_address = addr + len - 1;
new_memcheck.is_ranged = (len > 1);
new_memcheck.is_break_on_read =
(type == BreakpointType::Read || type == BreakpointType::Access);
new_memcheck.is_break_on_write =
(type == BreakpointType::Write || type == BreakpointType::Access);
new_memcheck.break_on_hit = true;
new_memcheck.log_on_hit = false;
new_memcheck.is_enabled = true;
PowerPC::memchecks.Add(new_memcheck);
INFO_LOG_FMT(GDB_STUB, "gdb: added {} memcheck: {:08x} bytes at {:08x}", static_cast<int>(type),
len, addr);
}
return true;
}
static void HandleAddBreakpoint()
{
u32 type;
u32 i, addr = 0, len = 0;
type = Hex2char(s_cmd_bfr[1]);
if (type > NUM_BREAKPOINT_TYPES)
return SendReply("E01");
i = 3;
while (s_cmd_bfr[i] != ',')
addr = addr << 4 | Hex2char(s_cmd_bfr[i++]);
i++;
while (i < s_cmd_len)
len = len << 4 | Hex2char(s_cmd_bfr[i++]);
if (!AddBreakpoint(static_cast<BreakpointType>(type), addr, len))
return SendReply("E02");
SendReply("OK");
}
static void HandleRemoveBreakpoint()
{
u32 type, addr, len, i;
type = Hex2char(s_cmd_bfr[1]);
if (type > NUM_BREAKPOINT_TYPES)
return SendReply("E01");
addr = 0;
len = 0;
i = 3;
while (s_cmd_bfr[i] != ',')
addr = (addr << 4) | Hex2char(s_cmd_bfr[i++]);
i++;
while (i < s_cmd_len)
len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
RemoveBreakpoint(static_cast<BreakpointType>(type), addr, len);
SendReply("OK");
}
void ProcessCommands(bool loop_until_continue)
{
s_just_connected = false;
while (IsActive())
{
if (CPU::GetState() == CPU::State::PowerDown)
{
Deinit();
INFO_LOG_FMT(GDB_STUB, "killed by power down");
return;
}
if (!IsDataAvailable())
{
if (loop_until_continue)
continue;
else
return;
}
ReadCommand();
// No more commands
if (s_cmd_len == 0)
continue;
switch (s_cmd_bfr[0])
{
case 'q':
HandleQuery();
break;
case 'H':
HandleSetThread();
break;
case 'T':
HandleIsThreadAlive();
break;
case '?':
SendSignal(Signal::Sigterm);
break;
case 'k':
Deinit();
INFO_LOG_FMT(GDB_STUB, "killed by gdb");
return;
case 'g':
ReadRegisters();
break;
case 'G':
WriteRegisters();
break;
case 'p':
ReadRegister();
break;
case 'P':
WriteRegister();
break;
case 'm':
ReadMemory();
break;
case 'M':
WriteMemory();
PowerPC::ppcState.iCache.Reset();
Host_UpdateDisasmDialog();
break;
case 's':
Step();
return;
case 'C':
case 'c':
CPU::Continue();
s_has_control = false;
return;
case 'z':
HandleRemoveBreakpoint();
break;
case 'Z':
HandleAddBreakpoint();
break;
default:
SendReply("");
break;
}
}
}
// exported functions
static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t server_addrlen,
sockaddr* client_addr, socklen_t* client_addrlen);
#ifndef _WIN32
void InitLocal(const char* socket)
{
unlink(socket);
sockaddr_un addr = {};
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, socket);
InitGeneric(PF_LOCAL, (const sockaddr*)&addr, sizeof(addr), NULL, NULL);
}
#endif
void Init(u32 port)
{
sockaddr_in saddr_server = {};
sockaddr_in saddr_client;
saddr_server.sin_family = AF_INET;
saddr_server.sin_port = htons(port);
saddr_server.sin_addr.s_addr = INADDR_ANY;
socklen_t client_addrlen = sizeof(saddr_client);
InitGeneric(PF_INET, (const sockaddr*)&saddr_server, sizeof(saddr_server),
(sockaddr*)&saddr_client, &client_addrlen);
saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
}
static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t server_addrlen,
sockaddr* client_addr, socklen_t* client_addrlen)
{
s_socket_context.emplace();
s_tmpsock = socket(domain, SOCK_STREAM, 0);
if (s_tmpsock == -1)
ERROR_LOG_FMT(GDB_STUB, "Failed to create gdb socket");
int on = 1;
if (setsockopt(s_tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof on) < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to setsockopt");
if (bind(s_tmpsock, server_addr, server_addrlen) < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to bind gdb socket");
if (listen(s_tmpsock, 1) < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to listen to gdb socket");
INFO_LOG_FMT(GDB_STUB, "Waiting for gdb to connect...");
s_sock = accept(s_tmpsock, client_addr, client_addrlen);
if (s_sock < 0)
ERROR_LOG_FMT(GDB_STUB, "Failed to accept gdb client");
INFO_LOG_FMT(GDB_STUB, "Client connected.");
s_just_connected = true;
#ifdef _WIN32
closesocket(s_tmpsock);
#else
close(s_tmpsock);
#endif
s_tmpsock = -1;
s_update_event = CoreTiming::RegisterEvent("GDBStubUpdate", UpdateCallback);
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
s_has_control = true;
}
void Deinit()
{
if (s_tmpsock != -1)
{
shutdown(s_tmpsock, SHUT_RDWR);
s_tmpsock = -1;
}
if (s_sock != -1)
{
shutdown(s_sock, SHUT_RDWR);
s_sock = -1;
}
s_socket_context.reset();
s_has_control = false;
}
bool IsActive()
{
return s_tmpsock != -1 || s_sock != -1;
}
bool HasControl()
{
return s_has_control;
}
void TakeControl()
{
s_has_control = true;
}
bool JustConnected()
{
return s_just_connected;
}
void SendSignal(Signal signal)
{
char bfr[128] = {};
fmt::format_to(bfr, "T{:02x}{:02x}:{:08x};{:02x}:{:08x};", static_cast<u8>(signal), 64, PC, 1,
GPR(1));
SendReply(bfr);
}
} // namespace GDBStub