diff --git a/CMakeLists.txt b/CMakeLists.txt index ccaf46e8..621a0daf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,16 +129,14 @@ add_executable(Bloom src/DebugServers/GdbRsp/GdbDebugServerConfig.cpp src/DebugServers/GdbRsp/Connection.cpp src/DebugServers/GdbRsp/DebugSession.cpp - src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp + src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp - src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp - src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp @@ -146,6 +144,10 @@ add_executable(Bloom # AVR GDB Server src/DebugServers/GdbRsp/AvrGdb/AvrGdbRsp.cpp src/DebugServers/GdbRsp/AvrGdb/TargetDescriptor.cpp + src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.cpp + src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.cpp + src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.cpp + # Insight src/Insight/Insight.cpp src/Insight/InsightWorker/InsightWorker.cpp diff --git a/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.cpp b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.cpp new file mode 100644 index 00000000..6c440b42 --- /dev/null +++ b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.cpp @@ -0,0 +1,17 @@ +#include "AbstractMemoryAccessPacket.hpp" + +#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/Signal.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" + +namespace Bloom::DebugServers::Gdb::CommandPackets +{ + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; + + +} diff --git a/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.hpp b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.hpp new file mode 100644 index 00000000..3c6ef84e --- /dev/null +++ b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include "src/DebugServers/GdbRsp/CommandPackets/CommandPacket.hpp" + +#include "src/Targets/TargetMemory.hpp" + +namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets +{ + /** + * The ReadMemory class implements a structure for "m" packets. Upon receiving these packets, the server is + * expected to read memory from the target and send it the client. + */ + class AbstractMemoryAccessPacket: public Bloom::DebugServers::Gdb::CommandPackets::CommandPacket + { + public: + explicit AbstractMemoryAccessPacket(const std::vector& rawPacket): CommandPacket(rawPacket) {}; + + protected: + /** + * The mask used by the AVR GDB client to encode the memory type into memory addresses. + */ + static constexpr std::uint32_t AVR_GDB_MEMORY_ADDRESS_MASK = 0xFE0000U; + + /** + * avr-gdb uses the most significant 15 bits in memory addresses to indicate the type of memory being + * addressed. + * + * @param address + * @return + */ + Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) { + if ((address & AbstractMemoryAccessPacket::AVR_GDB_MEMORY_ADDRESS_MASK) != 0U) { + return Targets::TargetMemoryType::RAM; + } + + return Targets::TargetMemoryType::FLASH; + } + + /** + * Strips the most significant 15 bits from a GDB memory address. + * + * @param address + * @return + */ + std::uint32_t removeMemoryTypeIndicatorFromGdbAddress(std::uint32_t address) { + return (address & AbstractMemoryAccessPacket::AVR_GDB_MEMORY_ADDRESS_MASK) != 0U + ? (address & ~(AbstractMemoryAccessPacket::AVR_GDB_MEMORY_ADDRESS_MASK)) + : address; + } + }; +} diff --git a/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.cpp b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.cpp new file mode 100644 index 00000000..0addb77a --- /dev/null +++ b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.cpp @@ -0,0 +1,70 @@ +#include "ReadMemory.hpp" + +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ResponsePacket.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" + +namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets +{ + using ResponsePackets::ErrorResponsePacket; + using ResponsePackets::ResponsePacket; + + using Exceptions::Exception; + + void ReadMemory::init() { + if (this->data.size() < 4) { + throw Exception("Invalid packet length"); + } + + auto packetString = QString::fromLocal8Bit( + reinterpret_cast(this->data.data() + 1), + static_cast(this->data.size() - 1) + ); + + /* + * The read memory ('m') packet consists of two segments, an address and a number of bytes to read. + * These are separated by a comma character. + */ + auto packetSegments = packetString.split(","); + + if (packetSegments.size() != 2) { + throw Exception( + "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) + ); + } + + bool conversionStatus = false; + this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to parse start address from read memory packet data"); + } + + this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16); + + if (!conversionStatus) { + throw Exception("Failed to parse read length from read memory packet data"); + } + } + + void ReadMemory::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling ReadMemory packet"); + + try { + auto memoryType = this->getMemoryTypeFromGdbAddress(this->startAddress); + auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(this->startAddress); + auto memoryBuffer = targetControllerConsole.readMemory(memoryType, startAddress, this->bytes); + + auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer); + debugSession.connection.writePacket( + ResponsePacket(std::vector(hexMemoryBuffer.begin(), hexMemoryBuffer.end())) + ); + + } catch (const Exception& exception) { + Logger::error("Failed to read memory from target - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } +} diff --git a/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.hpp b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.hpp similarity index 73% rename from src/DebugServers/GdbRsp/CommandPackets/ReadMemory.hpp rename to src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.hpp index a517b2a3..f9658b3a 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.hpp +++ b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.hpp @@ -3,15 +3,15 @@ #include #include -#include "CommandPacket.hpp" +#include "AbstractMemoryAccessPacket.hpp" -namespace Bloom::DebugServers::Gdb::CommandPackets +namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets { /** * The ReadMemory class implements a structure for "m" packets. Upon receiving these packets, the server is * expected to read memory from the target and send it the client. */ - class ReadMemory: public CommandPacket + class ReadMemory: public AbstractMemoryAccessPacket { public: /** @@ -28,13 +28,13 @@ namespace Bloom::DebugServers::Gdb::CommandPackets */ std::uint32_t bytes = 0; - explicit ReadMemory(const std::vector& rawPacket): CommandPacket(rawPacket) { + explicit ReadMemory(const std::vector& rawPacket): AbstractMemoryAccessPacket(rawPacket) { init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; - private: + protected: void init(); }; } diff --git a/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.cpp similarity index 58% rename from src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp rename to src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.cpp index 06439a57..48491dc9 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp +++ b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.cpp @@ -1,14 +1,17 @@ #include "WriteMemory.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp" -namespace Bloom::DebugServers::Gdb::CommandPackets +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" + +namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets { - using namespace Bloom::Exceptions; + using ResponsePackets::ErrorResponsePacket; + using ResponsePackets::OkResponsePacket; - void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using namespace Bloom::Exceptions; void WriteMemory::init() { if (this->data.size() < 4) { @@ -57,4 +60,27 @@ namespace Bloom::DebugServers::Gdb::CommandPackets throw Exception("Buffer size does not match length value given in write memory packet"); } } + + void WriteMemory::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling WriteMemory packet"); + + try { + const auto memoryType = this->getMemoryTypeFromGdbAddress(this->startAddress); + + if (memoryType == Targets::TargetMemoryType::FLASH) { + throw Exception( + "GDB client requested a flash memory write - This is not currently supported by Bloom." + ); + } + + const auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(this->startAddress); + targetControllerConsole.writeMemory(memoryType, startAddress, this->buffer); + + debugSession.connection.writePacket(OkResponsePacket()); + + } catch (const Exception& exception) { + Logger::error("Failed to write memory to target - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.hpp b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.hpp similarity index 68% rename from src/DebugServers/GdbRsp/CommandPackets/WriteMemory.hpp rename to src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.hpp index 73fb761e..e9dfb3b7 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/WriteMemory.hpp +++ b/src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.hpp @@ -3,16 +3,15 @@ #include #include -#include "CommandPacket.hpp" -#include "src/Targets/TargetMemory.hpp" +#include "AbstractMemoryAccessPacket.hpp" -namespace Bloom::DebugServers::Gdb::CommandPackets +namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets { /** * The WriteMemory class implements the structure for "M" packets. Upon receiving this packet, the server is * expected to write data to the target's memory, at the specified start address. */ - class WriteMemory: public CommandPacket + class WriteMemory: public AbstractMemoryAccessPacket { public: /** @@ -23,11 +22,11 @@ namespace Bloom::DebugServers::Gdb::CommandPackets Targets::TargetMemoryBuffer buffer; - explicit WriteMemory(const std::vector& rawPacket): CommandPacket(rawPacket) { + explicit WriteMemory(const std::vector& rawPacket): AbstractMemoryAccessPacket(rawPacket) { init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp b/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp index 64e6595c..95a47a09 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp @@ -1,10 +1,45 @@ #include "CommandPacket.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/DebugServers/GdbRsp/Signal.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); + using ResponsePackets::ResponsePacket; + using ResponsePackets::OkResponsePacket; + using ResponsePackets::TargetStopped; + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; + + void CommandPacket::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + auto packetData = this->getData(); + auto packetString = std::string(packetData.begin(), packetData.end()); + + if (packetString[0] == '?') { + // Status report + debugSession.connection.writePacket(TargetStopped(Signal::TRAP)); + + } else if (packetString[0] == 'D') { + // Detach packet - there's not really anything we need to do here, so just respond with an OK + debugSession.connection.writePacket(OkResponsePacket()); + + } else if (packetString.find("qAttached") == 0) { + Logger::debug("Handling qAttached"); + debugSession.connection.writePacket(ResponsePacket({1})); + + } else { + Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response"); + + // Respond with an empty packet + debugSession.connection.writePacket(ResponsePacket({0})); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.hpp b/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.hpp index 68e680c9..074debe5 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/CommandPacket.hpp @@ -4,10 +4,9 @@ #include #include "src/DebugServers/GdbRsp/Packet.hpp" +#include "src/DebugServers/GdbRsp/DebugSession.hpp" -namespace Bloom::DebugServers::Gdb { - class GdbRspDebugServer; -} +#include "src/TargetController/TargetControllerConsole.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { @@ -40,12 +39,13 @@ namespace Bloom::DebugServers::Gdb::CommandPackets explicit CommandPacket(const std::vector& rawPacket): Packet(rawPacket) {} /** - * Double dispatches the packet to the appropriate overload of handleGdbPacket(), within the passed instance of - * GdbRspDebugServer. If there is no overload defined for a specific CommandPacket-inherited type, the - * generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) is used. + * Should handle the command for the current active debug session. * - * @param gdbRspDebugServer + * @param debugSession + * The current active debug session. + * + * @param targetControllerConsole */ - virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer); + virtual void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole); }; } diff --git a/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp b/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp index cd846eef..af200a2c 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp @@ -119,13 +119,13 @@ namespace Bloom::DebugServers::Gdb return std::make_unique(rawPacket); } - if (rawPacketString[1] == 'm') { - return std::make_unique(rawPacket); - } - - if (rawPacketString[1] == 'M') { - return std::make_unique(rawPacket); - } +// if (rawPacketString[1] == 'm') { +// return std::make_unique(rawPacket); +// } +// +// if (rawPacketString[1] == 'M') { +// return std::make_unique(rawPacket); +// } if (rawPacketString[1] == 'Z') { return std::make_unique(rawPacket); diff --git a/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.hpp b/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.hpp index 5a19b28a..096e50ec 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.hpp @@ -9,8 +9,6 @@ #include "SupportedFeaturesQuery.hpp" #include "ReadRegisters.hpp" #include "WriteRegister.hpp" -#include "ReadMemory.hpp" -#include "WriteMemory.hpp" #include "StepExecution.hpp" #include "ContinueExecution.hpp" #include "SetBreakpoint.hpp" diff --git a/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp b/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp index 6058e4f7..54049243 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp @@ -1,14 +1,14 @@ #include "ContinueExecution.hpp" -#include +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using ResponsePackets::ErrorResponsePacket; + using Exceptions::Exception; void ContinueExecution::init() { if (this->data.size() > 1) { @@ -17,4 +17,17 @@ namespace Bloom::DebugServers::Gdb::CommandPackets ); } } + + void ContinueExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling ContinueExecution packet"); + + try { + targetControllerConsole.continueTargetExecution(this->fromProgramCounter); + debugSession.waitingForBreak = true; + + } catch (const Exception& exception) { + Logger::error("Failed to continue execution on target - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.hpp b/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.hpp index df248abb..8a36ec6f 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.hpp @@ -28,7 +28,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets init(); } - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp b/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp index 5bba19f3..c5ae2412 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp @@ -1,10 +1,29 @@ #include "InterruptExecution.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/Signal.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); + using ResponsePackets::TargetStopped; + using ResponsePackets::ErrorResponsePacket; + using Exceptions::Exception; + + void InterruptExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling InterruptExecution packet"); + + try { + targetControllerConsole.stopTargetExecution(); + debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED)); + debugSession.waitingForBreak = false; + + } catch (const Exception& exception) { + Logger::error("Failed to interrupt execution - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.hpp b/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.hpp index eab29b9d..acdea58b 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.hpp @@ -17,6 +17,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets public: explicit InterruptExecution(const std::vector& rawPacket): CommandPacket(rawPacket) {} - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; }; } diff --git a/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp b/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp deleted file mode 100644 index ce68b108..00000000 --- a/src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "ReadMemory.hpp" - -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" - -namespace Bloom::DebugServers::Gdb::CommandPackets -{ - using namespace Bloom::Exceptions; - - void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } - - void ReadMemory::init() { - if (this->data.size() < 4) { - throw Exception("Invalid packet length"); - } - - auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 1), - static_cast(this->data.size() - 1) - ); - - /* - * The read memory ('m') packet consists of two segments, an address and a number of bytes to read. - * These are separated by a comma character. - */ - auto packetSegments = packetString.split(","); - - if (packetSegments.size() != 2) { - throw Exception( - "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) - ); - } - - bool conversionStatus = false; - this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse start address from read memory packet data"); - } - - this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse read length from read memory packet data"); - } - } -} diff --git a/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp b/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp index b1068a68..8192c6f3 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp @@ -1,12 +1,22 @@ #include "ReadRegisters.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/Targets/TargetRegister.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using Targets::TargetRegister; + using Targets::TargetRegisterDescriptors; + + using ResponsePackets::ResponsePacket; + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; void ReadRegisters::init() { if (this->data.size() >= 2 && this->data.front() == 'p') { @@ -16,4 +26,69 @@ namespace Bloom::DebugServers::Gdb::CommandPackets ); } } + + void ReadRegisters::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling ReadRegisters packet"); + + try { + const auto& targetDescriptor = debugSession.targetDescriptor; + auto descriptors = TargetRegisterDescriptors(); + + if (this->registerNumber.has_value()) { + Logger::debug("Reading register number: " + std::to_string(this->registerNumber.value())); + descriptors.insert(targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber.value())); + + } else { + // Read all target registers mapped to a GDB register + for (const auto& descriptor : targetDescriptor.getRegisterNumberToDescriptorMapping().getMap()) { + descriptors.insert(targetDescriptor.getTargetRegisterDescriptorFromNumber(descriptor.second.number)); + } + } + + auto registerSet = targetControllerConsole.readRegisters(descriptors); + + /* + * Sort each register by their respective GDB register number - this will leave us with a collection of + * registers in the order expected by the GDB client. + */ + std::sort( + registerSet.begin(), + registerSet.end(), + [this](const TargetRegister& registerA, const TargetRegister& registerB) { + return targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) < + targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor); + } + ); + + /* + * Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure + * that each register value size matches the size in the associated GDB register descriptor, implode the + * values, convert to hexadecimal form and send to the GDB client. + */ + auto registers = std::vector(); + for (auto& reg : registerSet) { + std::reverse(reg.value.begin(), reg.value.end()); + + const auto gdbRegisterNumber = targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor( + reg.descriptor + ).value(); + const auto& gdbRegisterDescriptor = targetDescriptor.getRegisterDescriptorFromNumber(gdbRegisterNumber); + + if (reg.value.size() < gdbRegisterDescriptor.size) { + reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00); + } + + registers.insert(registers.end(), reg.value.begin(), reg.value.end()); + } + + auto responseRegisters = Packet::dataToHex(registers); + debugSession.connection.writePacket( + ResponsePacket(std::vector(responseRegisters.begin(), responseRegisters.end())) + ); + + } catch (const Exception& exception) { + Logger::error("Failed to read general registers - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.hpp b/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.hpp index 539d8215..9c6bd285 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.hpp @@ -29,7 +29,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp b/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp index ddba1777..9915f237 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp @@ -2,15 +2,22 @@ #include -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/Targets/TargetBreakpoint.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - using namespace Bloom::Exceptions; + using Targets::TargetBreakpoint; - void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using ResponsePackets::OkResponsePacket; + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; void RemoveBreakpoint::init() { if (data.size() < 6) { @@ -38,4 +45,20 @@ namespace Bloom::DebugServers::Gdb::CommandPackets throw Exception("Failed to convert address hex value from RemoveBreakpoint packet."); } } + + void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Removing breakpoint at address " + std::to_string(this->address)); + + try { + auto breakpoint = TargetBreakpoint(); + breakpoint.address = this->address; + targetControllerConsole.removeBreakpoint(breakpoint); + + debugSession.connection.writePacket(OkResponsePacket()); + + } catch (const Exception& exception) { + Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.hpp b/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.hpp index 94fee821..d441248e 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.hpp @@ -4,13 +4,8 @@ #include #include -#include "../BreakpointType.hpp" #include "CommandPacket.hpp" - -namespace Bloom::DebugServers::Gdb -{ - enum class Feature: int; -} +#include "src/DebugServers/GdbRsp/BreakpointType.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { @@ -35,7 +30,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets this->init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp b/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp index 7c592e3c..d11390f1 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp @@ -2,15 +2,22 @@ #include -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/Targets/TargetBreakpoint.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - using namespace Bloom::Exceptions; + using Targets::TargetBreakpoint; - void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using ResponsePackets::OkResponsePacket; + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; void SetBreakpoint::init() { if (data.size() < 6) { @@ -38,4 +45,20 @@ namespace Bloom::DebugServers::Gdb::CommandPackets throw Exception("Failed to convert address hex value from SetBreakpoint packet."); } } + + void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling SetBreakpoint packet"); + + try { + auto breakpoint = TargetBreakpoint(); + breakpoint.address = this->address; + targetControllerConsole.setBreakpoint(breakpoint); + + debugSession.connection.writePacket(OkResponsePacket()); + + } catch (const Exception& exception) { + Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.hpp b/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.hpp index 3465317e..e046c288 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.hpp @@ -4,13 +4,8 @@ #include #include -#include "../BreakpointType.hpp" #include "CommandPacket.hpp" - -namespace Bloom::DebugServers::Gdb -{ - enum class Feature: int; -} +#include "src/DebugServers/GdbRsp/BreakpointType.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { @@ -35,7 +30,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets this->init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp index 584d8400..cd64a1ef 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp @@ -1,14 +1,15 @@ #include "StepExecution.hpp" -#include +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; void StepExecution::init() { if (this->data.size() > 1) { @@ -17,4 +18,17 @@ namespace Bloom::DebugServers::Gdb::CommandPackets ); } } + + void StepExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling StepExecution packet"); + + try { + targetControllerConsole.stepTargetExecution(this->fromProgramCounter); + debugSession.waitingForBreak = true; + + } catch (const Exception& exception) { + Logger::error("Failed to step execution on target - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp index 21d88c1d..a9f626d0 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/StepExecution.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "CommandPacket.hpp" @@ -22,7 +23,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp b/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp index f88a3b25..a14fa22a 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp @@ -2,13 +2,22 @@ #include -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/Feature.hpp" + +#include "src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Exceptions/Exception.hpp" +#include "src/DebugServers/GdbRsp/Exceptions/ClientNotSupported.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using ResponsePackets::SupportedFeaturesResponse; + using ResponsePackets::ErrorResponsePacket; + + using Bloom::Exceptions::Exception; + using Gdb::Exceptions::ClientNotSupported; void SupportedFeaturesQuery::init() { /* @@ -41,4 +50,23 @@ namespace Bloom::DebugServers::Gdb::CommandPackets } } } + + void SupportedFeaturesQuery::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling QuerySupport packet"); + + if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS) + && !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS) + ) { + // All GDB clients are expected to support breakpoints! + throw ClientNotSupported("GDB client does not support HW or SW breakpoints"); + } + + // Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom + auto response = SupportedFeaturesResponse({ + {Feature::SOFTWARE_BREAKPOINTS, std::nullopt}, + {Feature::PACKET_SIZE, std::to_string(debugSession.connection.getMaxPacketSize())}, + }); + + debugSession.connection.writePacket(response); + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.hpp b/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.hpp index bebae6b5..46d215df 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.hpp @@ -34,7 +34,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets return this->supportedFeatures; } - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: std::set supportedFeatures; diff --git a/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp b/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp index fb1c7a1c..2dc3343e 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp +++ b/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp @@ -1,15 +1,24 @@ #include "WriteRegister.hpp" -#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp" +#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/Targets/TargetRegister.hpp" + +#include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" namespace Bloom::DebugServers::Gdb::CommandPackets { - using namespace Bloom::Exceptions; + using Targets::TargetRegister; + using Targets::TargetRegisterDescriptors; - void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { - gdbRspDebugServer.handleGdbPacket(*this); - } + using ResponsePackets::ResponsePacket; + using ResponsePackets::OkResponsePacket; + using ResponsePackets::ErrorResponsePacket; + + using Exceptions::Exception; void WriteRegister::init() { // The P packet updates a single register @@ -28,4 +37,41 @@ namespace Bloom::DebugServers::Gdb::CommandPackets this->registerValue = Packet::hexToData(packetSegments.back().toStdString()); std::reverse(this->registerValue.begin(), this->registerValue.end()); } + + void WriteRegister::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling WriteRegister packet"); + + try { + auto targetRegisterDescriptor = debugSession.targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber); + + const auto valueSize = this->registerValue.size(); + if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) { + // Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size. + for (auto i = this->registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) { + if (this->registerValue.at(i) != 0x00) { + // If we reach a non-zero byte, we cannot trim anymore without changing the data + break; + } + + this->registerValue.erase(this->registerValue.begin() + i); + } + + if (this->registerValue.size() > targetRegisterDescriptor.size) { + const auto& gdbRegisterDescriptor = debugSession.targetDescriptor.getRegisterDescriptorFromNumber(this->registerNumber); + throw Exception("Cannot set value for " + gdbRegisterDescriptor.name + + " - value size exceeds register size." + ); + } + } + + targetControllerConsole.writeRegisters({ + TargetRegister(targetRegisterDescriptor, this->registerValue) + }); + debugSession.connection.writePacket(OkResponsePacket()); + + } catch (const Exception& exception) { + Logger::error("Failed to write registers - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } } diff --git a/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.hpp b/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.hpp index c2b7ce9c..d00ff5da 100644 --- a/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.hpp +++ b/src/DebugServers/GdbRsp/CommandPackets/WriteRegister.hpp @@ -21,7 +21,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets init(); }; - void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; + void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override; private: void init(); diff --git a/src/DebugServers/GdbRsp/Connection.hpp b/src/DebugServers/GdbRsp/Connection.hpp index 3036947c..5721c391 100644 --- a/src/DebugServers/GdbRsp/Connection.hpp +++ b/src/DebugServers/GdbRsp/Connection.hpp @@ -23,16 +23,9 @@ namespace Bloom::DebugServers::Gdb class Connection { public: - /** - * When the GDB client is waiting for the target to reach a breakpoint, this is set to true so we know when to - * notify the client. - * - * @TODO: This is pretty gross. Consider rethinking it. - */ - bool waitingForBreak = false; - explicit Connection(std::shared_ptr interruptEventNotifier) - : interruptEventNotifier(std::move(interruptEventNotifier)) {}; + : interruptEventNotifier(std::move(interruptEventNotifier)) + {}; /** * Accepts a connection on serverSocketFileDescriptor. diff --git a/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp b/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp index 97e02294..1f997ba2 100644 --- a/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp +++ b/src/DebugServers/GdbRsp/GdbRspDebugServer.cpp @@ -26,264 +26,6 @@ namespace Bloom::DebugServers::Gdb using Bloom::Targets::TargetRegisterDescriptors; using Bloom::Targets::TargetBreakpoint; - void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) { - auto packetData = packet.getData(); - auto packetString = std::string(packetData.begin(), packetData.end()); - - if (packetString[0] == '?') { - // Status report - this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); - - } else if (packetString[0] == 'D') { - // Detach packet - there's not really anything we need to do here, so just respond with an OK - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } else if (packetString.find("qAttached") == 0) { - Logger::debug("Handling qAttached"); - this->clientConnection->writePacket(ResponsePacket({1})); - - } else { - Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response"); - - // Respond with an empty packet - this->clientConnection->writePacket(ResponsePacket({0})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) { - Logger::debug("Handling QuerySupport packet"); - - if (!packet.isFeatureSupported(Feature::HARDWARE_BREAKPOINTS) - && !packet.isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS) - ) { - // All GDB clients are expected to support breakpoints! - throw ClientNotSupported("GDB client does not support HW or SW breakpoints"); - } - - // Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom - auto response = ResponsePackets::SupportedFeaturesResponse({ - {Feature::SOFTWARE_BREAKPOINTS, std::nullopt}, - {Feature::PACKET_SIZE, std::to_string(this->clientConnection->getMaxPacketSize())}, - }); - - this->clientConnection->writePacket(response); - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadRegisters& packet) { - Logger::debug("Handling ReadRegisters packet"); - - try { - auto descriptors = TargetRegisterDescriptors(); - - if (packet.registerNumber.has_value()) { - Logger::debug("Reading register number: " + std::to_string(packet.registerNumber.value())); - descriptors.insert(this->getTargetRegisterDescriptorFromNumber(packet.registerNumber.value())); - - } else { - // Read all target registers mapped to a GDB register - for (const auto& descriptor : this->getRegisterNumberToDescriptorMapping().getMap()) { - descriptors.insert(this->getTargetRegisterDescriptorFromNumber(descriptor.second.number)); - } - } - - auto registerSet = this->targetControllerConsole.readRegisters(descriptors); - - /* - * Sort each register by their respective GDB register number - this will leave us with a collection of - * registers in the order expected by the GDB client. - */ - std::sort( - registerSet.begin(), - registerSet.end(), - [this](const TargetRegister& registerA, const TargetRegister& registerB) { - return this->getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) < - this->getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor); - } - ); - - /* - * Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure - * that each register value size matches the size in the associated GDB register descriptor, implode the - * values, convert to hexadecimal form and send to the GDB client. - */ - auto registers = std::vector(); - for (auto& reg : registerSet) { - std::reverse(reg.value.begin(), reg.value.end()); - - const auto gdbRegisterNumber = this->getRegisterNumberFromTargetRegisterDescriptor( - reg.descriptor - ).value(); - const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(gdbRegisterNumber); - - if (reg.value.size() < gdbRegisterDescriptor.size) { - reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00); - } - - registers.insert(registers.end(), reg.value.begin(), reg.value.end()); - } - - auto responseRegisters = Packet::dataToHex(registers); - this->clientConnection->writePacket( - ResponsePacket(std::vector(responseRegisters.begin(), responseRegisters.end())) - ); - - } catch (const Exception& exception) { - Logger::error("Failed to read general registers - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteRegister& packet) { - Logger::debug("Handling WriteRegister packet"); - - try { - auto targetRegisterDescriptor = this->getTargetRegisterDescriptorFromNumber(packet.registerNumber); - - const auto valueSize = packet.registerValue.size(); - if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) { - // Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size. - for (auto i = packet.registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) { - if (packet.registerValue.at(i) != 0x00) { - // If we reach a non-zero byte, we cannot trim anymore without changing the data - break; - } - - packet.registerValue.erase(packet.registerValue.begin() + i); - } - - if (packet.registerValue.size() > targetRegisterDescriptor.size) { - const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(packet.registerNumber); - throw Exception("Cannot set value for " + gdbRegisterDescriptor.name - + " - value size exceeds register size." - ); - } - } - - this->targetControllerConsole.writeRegisters({ - TargetRegister(targetRegisterDescriptor, packet.registerValue) - }); - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to write registers - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::ContinueExecution& packet) { - Logger::debug("Handling ContinueExecution packet"); - - try { - this->targetControllerConsole.continueTargetExecution(packet.fromProgramCounter); - this->clientConnection->waitingForBreak = true; - - } catch (const Exception& exception) { - Logger::error("Failed to continue execution on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::StepExecution& packet) { - Logger::debug("Handling StepExecution packet"); - - try { - this->targetControllerConsole.stepTargetExecution(packet.fromProgramCounter); - this->clientConnection->waitingForBreak = true; - - } catch (const Exception& exception) { - Logger::error("Failed to step execution on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadMemory& packet) { - Logger::debug("Handling ReadMemory packet"); - - try { - auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress); - auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress); - auto memoryBuffer = this->targetControllerConsole.readMemory(memoryType, startAddress, packet.bytes); - - auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer); - this->clientConnection->writePacket( - ResponsePacket(std::vector(hexMemoryBuffer.begin(), hexMemoryBuffer.end())) - ); - - } catch (const Exception& exception) { - Logger::error("Failed to read memory from target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteMemory& packet) { - Logger::debug("Handling WriteMemory packet"); - - try { - const auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress); - - if (memoryType == Targets::TargetMemoryType::FLASH) { - throw Exception( - "GDB client requested a flash memory write - This is not currently supported by Bloom." - ); - } - - const auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress); - this->targetControllerConsole.writeMemory(memoryType, startAddress, packet.buffer); - - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to write memory to target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::SetBreakpoint& packet) { - Logger::debug("Handling SetBreakpoint packet"); - - try { - auto breakpoint = TargetBreakpoint(); - breakpoint.address = packet.address; - this->targetControllerConsole.setBreakpoint(breakpoint); - - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::RemoveBreakpoint& packet) { - Logger::debug("Removing breakpoint at address " + std::to_string(packet.address)); - - try { - auto breakpoint = TargetBreakpoint(); - breakpoint.address = packet.address; - this->targetControllerConsole.removeBreakpoint(breakpoint); - - this->clientConnection->writePacket(ResponsePacket({'O', 'K'})); - - } catch (const Exception& exception) { - Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - - void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& packet) { - Logger::debug("Handling InterruptExecution packet"); - - try { - this->targetControllerConsole.stopTargetExecution(); - this->clientConnection->writePacket(TargetStopped(Signal::INTERRUPTED)); - this->clientConnection->waitingForBreak = false; - - } catch (const Exception& exception) { - Logger::error("Failed to interrupt execution - " + exception.getMessage()); - this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'})); - } - } - void GdbRspDebugServer::init() { this->debugServerConfig = GdbDebugServerConfig(DebugServer::debugServerConfig); @@ -364,7 +106,7 @@ namespace Bloom::DebugServers::Gdb } void GdbRspDebugServer::close() { - this->closeClientConnection(); + this->terminateActiveDebugSession(); if (this->serverSocketFileDescriptor > 0) { ::close(this->serverSocketFileDescriptor); @@ -373,16 +115,20 @@ namespace Bloom::DebugServers::Gdb void GdbRspDebugServer::serve() { try { - if (!this->clientConnection.has_value()) { + if (!this->activeDebugSession.has_value()) { Logger::info("Waiting for GDB RSP connection"); - do { - this->waitForConnection(); + auto connection = this->waitForConnection(); - } while (!this->clientConnection.has_value()); + if (!connection.has_value()) { + // Likely an interrupt + return; + } - this->clientConnection->accept(this->serverSocketFileDescriptor); - Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress()); + connection->accept(this->serverSocketFileDescriptor); + Logger::info("Accepted GDP RSP connection from " + connection->getIpAddress()); + + this->activeDebugSession = DebugSession(connection.value(), this->getGdbTargetDescriptor()); EventManager::triggerEvent(std::make_shared()); /* @@ -390,39 +136,39 @@ namespace Bloom::DebugServers::Gdb * service it. */ if (!this->targetControllerConsole.isTargetControllerInService()) { - this->closeClientConnection(); + this->terminateActiveDebugSession(); throw DebugSessionAborted("TargetController not in service"); } } - auto packets = this->clientConnection->readPackets(); - - // Only process the last packet - any others will likely be duplicates from an impatient client - if (!packets.empty()) { - // Double-dispatch to appropriate handler - packets.back()->dispatchToHandler(*this); - } +// auto packets = this->activeDebugSession->connection.readPackets(); +// +// // Only process the last packet - any others will likely be duplicates from an impatient client +// if (!packets.empty()) { +// // Double-dispatch to appropriate handler +// packets.back()->dispatchToHandler(*this); +// } } catch (const ClientDisconnected&) { Logger::info("GDB RSP client disconnected"); - this->closeClientConnection(); + this->terminateActiveDebugSession(); return; } catch (const ClientCommunicationError& exception) { Logger::error( "GDB RSP client communication error - " + exception.getMessage() + " - closing connection" ); - this->closeClientConnection(); + this->terminateActiveDebugSession(); return; } catch (const ClientNotSupported& exception) { Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection"); - this->closeClientConnection(); + this->terminateActiveDebugSession(); return; } catch (const DebugSessionAborted& exception) { Logger::warning("GDB debug session aborted - " + exception.getMessage()); - this->closeClientConnection(); + this->terminateActiveDebugSession(); return; } catch (const DebugServerInterrupted&) { @@ -432,7 +178,7 @@ namespace Bloom::DebugServers::Gdb } } - void GdbRspDebugServer::waitForConnection() { + std::optional GdbRspDebugServer::waitForConnection() { if (::listen(this->serverSocketFileDescriptor, 3) != 0) { throw Exception("Failed to listen on server socket"); } @@ -453,25 +199,36 @@ namespace Bloom::DebugServers::Gdb if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) { // Interrupted this->interruptEventNotifier->clear(); - throw DebugServerInterrupted(); + return std::nullopt; } } - this->clientConnection = Connection(this->interruptEventNotifier); + return Connection(this->interruptEventNotifier); + } + + return std::nullopt; + } + + void GdbRspDebugServer::terminateActiveDebugSession() { + if (this->activeDebugSession.has_value()) { + this->activeDebugSession->terminate(); + this->activeDebugSession = std::nullopt; + + EventManager::triggerEvent(std::make_shared()); } } void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) { - if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) { + if (event.state == TargetControllerState::SUSPENDED && this->activeDebugSession.has_value()) { Logger::warning("Terminating debug session - TargetController suspended unexpectedly"); - this->closeClientConnection(); + this->terminateActiveDebugSession(); } } void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) { - if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) { - this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); - this->clientConnection->waitingForBreak = false; + if (this->activeDebugSession.has_value() && this->activeDebugSession->waitingForBreak) { + this->activeDebugSession->connection.writePacket(TargetStopped(Signal::TRAP)); + this->activeDebugSession->waitingForBreak = false; } } } diff --git a/src/DebugServers/GdbRsp/GdbRspDebugServer.hpp b/src/DebugServers/GdbRsp/GdbRspDebugServer.hpp index a8222b01..d70698f0 100644 --- a/src/DebugServers/GdbRsp/GdbRspDebugServer.hpp +++ b/src/DebugServers/GdbRsp/GdbRspDebugServer.hpp @@ -11,6 +11,8 @@ #include "GdbDebugServerConfig.hpp" #include "Connection.hpp" +#include "TargetDescriptor.hpp" +#include "DebugSession.hpp" #include "Signal.hpp" #include "RegisterDescriptor.hpp" #include "Feature.hpp" @@ -51,85 +53,6 @@ namespace Bloom::DebugServers::Gdb return "GDB Remote Serial Protocol DebugServer"; }; - /** - * Handles any other GDB command packet that has not been promoted to a more specific type. - * This would be packets like "?" and "qAttached". - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::CommandPacket& packet); - - /** - * Handles the supported features query ("qSupported") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet); - - /** - * Handles the read registers ("g" and "p") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::ReadRegisters& packet); - - /** - * Handles the write general register ("P") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::WriteRegister& packet); - - /** - * Handles the continue execution ("c") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::ContinueExecution& packet); - - /** - * Handles the step execution ("s") packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::StepExecution& packet); - - /** - * Handles the read memory ("m") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::ReadMemory& packet); - - /** - * Handles the write memory ("M") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::WriteMemory& packet); - - /** - * Handles the set breakpoint ("Z") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::SetBreakpoint& packet); - - /** - * Handles the remove breakpoint ("z") command packet. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::RemoveBreakpoint& packet); - - /** - * Handles the interrupt command packet. - * Will attempt to halt execution on the target. Should respond with a "stop reply" packet, or an error code. - * - * @param packet - */ - virtual void handleGdbPacket(CommandPackets::InterruptExecution& packet); - protected: std::optional debugServerConfig; @@ -159,10 +82,7 @@ namespace Bloom::DebugServers::Gdb */ int enableReuseAddressSocketOption = 1; - /** - * The current active GDB client connection, if any. - */ - std::optional clientConnection; + std::optional activeDebugSession; /** * Prepares the GDB server for listing on the selected address and port. @@ -180,72 +100,16 @@ namespace Bloom::DebugServers::Gdb void serve() override; /** - * Waits for a GDB client to connect on the listening socket. Accepts the connection and - * sets this->clientConnection. - */ - void waitForConnection(); - - void closeClientConnection() { - if (this->clientConnection.has_value()) { - this->clientConnection->close(); - this->clientConnection = std::nullopt; - EventManager::triggerEvent(std::make_shared()); - } - } - - /** - * GDB clients encode memory type information (flash, ram, eeprom, etc) in memory addresses. This is typically - * hardcoded in the GDB client source. This method extracts memory type information from a given memory address. - * The specifics of the encoding may vary with targets, which is why this method is virtual. For an example, - * see the implementation of this method in AvrGdbRsp. + * Waits for a GDB client to connect on the listening socket. * - * @param address - * @return + * This function may return an std::nullopt, if the waiting was interrupted by some other event. */ - virtual Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) = 0; + std::optional waitForConnection(); - /** - * Removes memory type information from memory address. - * See comment for GdbRspDebugServer::getMemoryTypeFromGdbAddress() - * - * @param address - * @return - */ - virtual std::uint32_t removeMemoryTypeIndicatorFromGdbAddress(std::uint32_t address) = 0; + void terminateActiveDebugSession(); - /** - * Should return the mapping of GDB register numbers to GDB register descriptors. - */ - virtual const BiMap& getRegisterNumberToDescriptorMapping() = 0; + virtual const TargetDescriptor& getGdbTargetDescriptor() = 0; - /** - * Should retrieve the GDB register number, given a target register descriptor. Or std::nullopt if the target - * register descriptor isn't mapped to any GDB register. - * - * @param registerDescriptor - * @return - */ - virtual std::optional getRegisterNumberFromTargetRegisterDescriptor( - const Targets::TargetRegisterDescriptor& registerDescriptor - ) = 0; - - /** - * Should retrieve the GDB register descriptor for a given GDB register number. - * - * @param number - * @return - */ - virtual const RegisterDescriptor& getRegisterDescriptorFromNumber(GdbRegisterNumberType number) = 0; - - /** - * Should retrieve the mapped target register descriptor for a given GDB register number. - * - * @param number - * @return - */ - virtual const Targets::TargetRegisterDescriptor& getTargetRegisterDescriptorFromNumber( - GdbRegisterNumberType number - ) = 0; void onTargetControllerStateReported(const Events::TargetControllerStateReported& event);