From 4e0940500d2d57d0ac49b8c42092109f1d7e45da Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 30 Jul 2026 21:35:58 +1000 Subject: [PATCH] GDBServer: Apply register writes atomically Decode and validate the complete G packet before mutating CPU state, preserve the zero and read-only BadVaddr registers, and return the protocol-defined OK response. Apply SR and CAUSE masks and side effects, and rebuild the instruction pipeline only when PC changes. --- src/core/gdb_server.cpp | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/core/gdb_server.cpp b/src/core/gdb_server.cpp index d574ea4af..c923f5e13 100644 --- a/src/core/gdb_server.cpp +++ b/src/core/gdb_server.cpp @@ -210,34 +210,49 @@ bool GDBServer::Cmd$g(ClientSocket* client, std::string_view data) /// Set general registers. bool GDBServer::Cmd$G(ClientSocket* client, std::string_view data) { - if (data.size() == NUM_GDB_REGISTERS * 8) + std::array bytes; + if (data.size() != bytes.size() * 2 || StringUtil::DecodeHex(bytes, data) != bytes.size()) { - size_t offset = 0; - - for (u32* reg : REGISTERS) - { - // Data is in host order (little endian). - const std::string_view tex_value = data.substr(offset, 8); - std::array le_value; - if (StringUtil::DecodeHex(le_value, tex_value) == 4) - { - *reg = ZeroExtend32(le_value[0]) | (ZeroExtend32(le_value[1]) << 8) | (ZeroExtend32(le_value[2]) << 16) | - (ZeroExtend32(le_value[3]) << 24); - } - else - { - ERROR_LOG("Invalid register set value: {}", tex_value); - } + ERROR_LOG("Invalid payload for 'G' command, expected {} hex digits", bytes.size() * 2); + client->SendReplyWithAck("E01"); + return true; + } - offset += 8; - } + std::array values; + for (size_t i = 0; i < values.size(); i++) + { + const size_t offset = i * sizeof(u32); + values[i] = ZeroExtend32(bytes[offset]) | (ZeroExtend32(bytes[offset + 1]) << 8) | + (ZeroExtend32(bytes[offset + 2]) << 16) | (ZeroExtend32(bytes[offset + 3]) << 24); } - else + + const u32 new_pc = values[37]; + if ((new_pc & 3u) != 0) { - ERROR_LOG("Wrong payload size for 'G' command, expected {} got {}", NUM_GDB_REGISTERS * 8, data.size()); + ERROR_LOG("Invalid PC 0x{:08X} in 'G' command", new_pc); + client->SendReplyWithAck("E01"); + return true; } - client->SendReplyWithAck(); + CPU::g_state.regs.r[0] = 0; + for (size_t i = 1; i < 32; i++) + CPU::g_state.regs.r[i] = values[i]; + + CPU::g_state.cop0_regs.sr.bits = (CPU::g_state.cop0_regs.sr.bits & ~CPU::Cop0Registers::SR::WRITE_MASK) | + (values[32] & CPU::Cop0Registers::SR::WRITE_MASK); + CPU::UpdateMemoryPointers(); + + CPU::g_state.regs.lo = values[33]; + CPU::g_state.regs.hi = values[34]; + + CPU::g_state.cop0_regs.cause.bits = (CPU::g_state.cop0_regs.cause.bits & ~CPU::Cop0Registers::CAUSE::WRITE_MASK) | + (values[36] & CPU::Cop0Registers::CAUSE::WRITE_MASK); + CPU::CheckForPendingInterrupt(); + + if (new_pc != CPU::g_state.pc) + CPU::SetPC(new_pc); + + client->SendReplyWithAck("OK"); return true; }