From 7bae23d79d61853b48261b322e8112ea4edf3b1c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 11 Mar 2025 21:50:26 +1000 Subject: [PATCH] GDBServer: Implement memory breakpoints --- src/core/gdb_server.cpp | 92 ++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/src/core/gdb_server.cpp b/src/core/gdb_server.cpp index 64b8fa775..e345bca3f 100644 --- a/src/core/gdb_server.cpp +++ b/src/core/gdb_server.cpp @@ -53,8 +53,8 @@ static bool Cmd$G(ClientSocket* client, std::string_view data); static bool Cmd$m(ClientSocket* client, std::string_view data); static bool Cmd$M(ClientSocket* client, std::string_view data); static bool Cmd$s(ClientSocket* client, std::string_view data); -static bool Cmd$z1(ClientSocket* client, std::string_view data); -static bool Cmd$Z1(ClientSocket* client, std::string_view data); +template +static bool Cmd$z(ClientSocket* client, std::string_view data); static bool Cmd$vMustReplyEmpty(ClientSocket* client, std::string_view data); static bool Cmd$qSupported(ClientSocket* client, std::string_view data); @@ -122,10 +122,8 @@ static constexpr std::pair}, + {"Z", Cmd$z}, {"vMustReplyEmpty", Cmd$vMustReplyEmpty}, {"qSupported", Cmd$qSupported}, }; @@ -286,40 +284,76 @@ bool GDBServer::Cmd$s(ClientSocket* client, std::string_view data) return true; } -/// Remove hardware breakpoint. -bool GDBServer::Cmd$z1(ClientSocket* client, std::string_view data) +/// Remove hardware breakpoint (z). +/// Insert hardware breakpoint (Z). +template +bool GDBServer::Cmd$z(ClientSocket* client, std::string_view data) { - const std::optional address = StringUtil::FromChars(data, 16); - if (address.has_value()) + std::string_view caret = data; + std::optional bptype; + std::optional bpaddr; + + // type,addr + if (!(bptype = StringUtil::FromChars(caret, 10, &caret)) || caret.empty() || caret[0] != ',' || + !(bpaddr = StringUtil::FromChars(caret.substr(1), 16)).has_value()) { - CPU::RemoveBreakpoint(CPU::BreakpointType::Execute, *address); + ERROR_LOG("Invalid {} hw breakpoint packet: {}", add_breakpoint ? "add" : "remove", data); + return false; + } + + if (bptype.value() == 0 || bptype.value() == 1) // software/hardware breakpoint + { + if constexpr (add_breakpoint) + CPU::AddBreakpoint(CPU::BreakpointType::Execute, bpaddr.value()); + else + CPU::RemoveBreakpoint(CPU::BreakpointType::Execute, bpaddr.value()); + client->SendReplyWithAck("OK"); + return true; + } + else if (bptype.value() == 2) // write breakpoint + { + if constexpr (add_breakpoint) + CPU::AddBreakpoint(CPU::BreakpointType::Write, bpaddr.value()); + else + CPU::RemoveBreakpoint(CPU::BreakpointType::Write, bpaddr.value()); + + client->SendReplyWithAck("OK"); + return true; + } + else if (bptype.value() == 3) // read breakpoint + { + if constexpr (add_breakpoint) + CPU::AddBreakpoint(CPU::BreakpointType::Read, bpaddr.value()); + else + CPU::RemoveBreakpoint(CPU::BreakpointType::Read, bpaddr.value()); + client->SendReplyWithAck("OK"); + return true; + } + else if (bptype.value() == 4) // read+write breakpoint + { + if constexpr (add_breakpoint) + { + CPU::AddBreakpoint(CPU::BreakpointType::Read, bpaddr.value()); + CPU::AddBreakpoint(CPU::BreakpointType::Write, bpaddr.value()); + } + else + { + CPU::RemoveBreakpoint(CPU::BreakpointType::Read, bpaddr.value()); + CPU::RemoveBreakpoint(CPU::BreakpointType::Write, bpaddr.value()); + } + client->SendReplyWithAck("OK"); return true; } else { - ERROR_LOG("Invalid address to remove hw breakpoint: ", data); - client->SendReplyWithAck(); + ERROR_LOG("Unknown breakpoint type {}", bptype.value()); return false; } } -/// Insert hardware breakpoint. -bool GDBServer::Cmd$Z1(ClientSocket* client, std::string_view data) -{ - const std::optional address = StringUtil::FromChars(data, 16); - if (address) - { - CPU::AddBreakpoint(CPU::BreakpointType::Execute, *address, false); - client->SendReplyWithAck("OK"); - return true; - } - else - { - ERROR_LOG("Invalid address to insert hw breakpoint: ", data); - return false; - } -} +template bool GDBServer::Cmd$z(ClientSocket* client, std::string_view data); +template bool GDBServer::Cmd$z(ClientSocket* client, std::string_view data); bool GDBServer::Cmd$vMustReplyEmpty(ClientSocket* client, std::string_view data) {