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.
pull/3782/head
Stenzek 2 months ago
parent aea3746414
commit 4e0940500d
No known key found for this signature in database

@ -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<u8, NUM_GDB_REGISTERS * sizeof(u32)> 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<u8, 4> 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<u32, NUM_GDB_REGISTERS> 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;
}

Loading…
Cancel
Save