From 208ed712f42cfd277405a22663197dc1c5e84cfe Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 6 Jun 2022 12:56:01 -0400 Subject: core/debugger: memory breakpoint support --- src/core/debugger/gdbstub.cpp | 139 +++++++++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 21 deletions(-) (limited to 'src/core/debugger/gdbstub.cpp') diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 52e76f659..f5e9a303d 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -112,6 +112,23 @@ void GDBStub::Stopped(Kernel::KThread* thread) { SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); } +void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) { + const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)}; + + switch (watch.type) { + case Kernel::DebugWatchpointType::Read: + SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address)); + break; + case Kernel::DebugWatchpointType::Write: + SendReply(fmt::format("{}watch:{:x};", status, watch.start_address)); + break; + case Kernel::DebugWatchpointType::ReadOrWrite: + default: + SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address)); + break; + } +} + std::vector GDBStub::ClientData(std::span data) { std::vector actions; current_command.insert(current_command.end(), data.begin(), data.end()); @@ -278,41 +295,121 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector(strtoll(command.data() + addr_sep, nullptr, 16))}; + case 'Z': + HandleBreakpointInsert(command); + break; + case 'z': + HandleBreakpointRemove(command); + break; + default: + SendReply(GDB_STUB_REPLY_EMPTY); + break; + } +} - if (system.Memory().IsValidVirtualAddress(addr)) { - replaced_instructions[addr] = system.Memory().Read32(addr); - system.Memory().Write32(addr, arch->BreakpointInstruction()); - system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); +enum class BreakpointType { + Software = 0, + Hardware = 1, + WriteWatch = 2, + ReadWatch = 3, + AccessWatch = 4, +}; + +void GDBStub::HandleBreakpointInsert(std::string_view command) { + const auto type{static_cast(strtoll(command.data(), nullptr, 16))}; + const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; + const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - + command.begin() + 1}; + const size_t addr{static_cast(strtoll(command.data() + addr_sep, nullptr, 16))}; + const size_t size{static_cast(strtoll(command.data() + size_sep, nullptr, 16))}; + + if (!system.Memory().IsValidVirtualAddressRange(addr, size)) { + SendReply(GDB_STUB_REPLY_ERR); + return; + } - SendReply(GDB_STUB_REPLY_OK); - } else { - SendReply(GDB_STUB_REPLY_ERR); - } + bool success{}; + + switch (type) { + case BreakpointType::Software: + replaced_instructions[addr] = system.Memory().Read32(addr); + system.Memory().Write32(addr, arch->BreakpointInstruction()); + system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); + success = true; + break; + case BreakpointType::WriteWatch: + success = system.CurrentProcess()->InsertWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Write); break; + case BreakpointType::ReadWatch: + success = system.CurrentProcess()->InsertWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Read); + break; + case BreakpointType::AccessWatch: + success = system.CurrentProcess()->InsertWatchpoint( + system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + break; + case BreakpointType::Hardware: + default: + SendReply(GDB_STUB_REPLY_EMPTY); + return; + } + + if (success) { + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_ERR); } - case 'z': { - const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; - const size_t addr{static_cast(strtoll(command.data() + addr_sep, nullptr, 16))}; +} + +void GDBStub::HandleBreakpointRemove(std::string_view command) { + const auto type{static_cast(strtoll(command.data(), nullptr, 16))}; + const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; + const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - + command.begin() + 1}; + const size_t addr{static_cast(strtoll(command.data() + addr_sep, nullptr, 16))}; + const size_t size{static_cast(strtoll(command.data() + size_sep, nullptr, 16))}; + + if (!system.Memory().IsValidVirtualAddressRange(addr, size)) { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + bool success{}; + switch (type) { + case BreakpointType::Software: { const auto orig_insn{replaced_instructions.find(addr)}; - if (system.Memory().IsValidVirtualAddress(addr) && - orig_insn != replaced_instructions.end()) { + if (orig_insn != replaced_instructions.end()) { system.Memory().Write32(addr, orig_insn->second); system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); replaced_instructions.erase(addr); - - SendReply(GDB_STUB_REPLY_OK); - } else { - SendReply(GDB_STUB_REPLY_ERR); + success = true; } break; } + case BreakpointType::WriteWatch: + success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Write); + break; + case BreakpointType::ReadWatch: + success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Read); + break; + case BreakpointType::AccessWatch: + success = system.CurrentProcess()->RemoveWatchpoint( + system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + break; + case BreakpointType::Hardware: default: SendReply(GDB_STUB_REPLY_EMPTY); - break; + return; + } + + if (success) { + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_ERR); } } -- cgit v1.2.3