#include "AvrGdbRsp.hpp" #include "src/Services/StringService.hpp" // Command packets #include "CommandPackets/ReadRegister.hpp" #include "CommandPackets/ReadRegisters.hpp" #include "CommandPackets/WriteRegister.hpp" #include "CommandPackets/ReadMemory.hpp" #include "CommandPackets/WriteMemory.hpp" #include "CommandPackets/ReadMemoryMap.hpp" #include "CommandPackets/FlashErase.hpp" #include "CommandPackets/FlashWrite.hpp" #include "CommandPackets/FlashDone.hpp" #include "CommandPackets/VContSupportedActionsQuery.hpp" #include "CommandPackets/VContContinueExecution.hpp" #include "CommandPackets/VContStepExecution.hpp" #include "CommandPackets/VContRangeStep.hpp" namespace DebugServer::Gdb::AvrGdb { using namespace Exceptions; using Targets::TargetRegisterDescriptor; using Targets::TargetRegisterType; AvrGdbRsp::AvrGdbRsp( const DebugServerConfig& debugServerConfig, EventListener& eventListener, EventFdNotifier& eventNotifier ) : GdbRspDebugServer(debugServerConfig, eventListener, eventNotifier) , gdbTargetDescriptor(TargetDescriptor(this->targetControllerService.getTargetDescriptor())) {} DebugSession* AvrGdbRsp::startDebugSession(Connection&& connection) { this->activeDebugSession.emplace( std::move(connection), this->getSupportedFeatures(), this->gdbTargetDescriptor, this->debugServerConfig ); return &*(this->activeDebugSession); } void AvrGdbRsp::endDebugSession() { this->activeDebugSession.reset(); } const Gdb::TargetDescriptor& AvrGdbRsp::getGdbTargetDescriptor() { return this->gdbTargetDescriptor; } DebugSession* AvrGdbRsp::getActiveDebugSession() { return this->activeDebugSession.has_value() ? &*(this->activeDebugSession) : nullptr; } std::unique_ptr AvrGdbRsp::resolveCommandPacket( const RawPacket& rawPacket ) { using AvrGdb::CommandPackets::ReadRegister; using AvrGdb::CommandPackets::ReadRegisters; using AvrGdb::CommandPackets::WriteRegister; using AvrGdb::CommandPackets::ReadMemory; using AvrGdb::CommandPackets::WriteMemory; using AvrGdb::CommandPackets::ReadMemoryMap; using AvrGdb::CommandPackets::FlashErase; using AvrGdb::CommandPackets::FlashWrite; using AvrGdb::CommandPackets::FlashDone; using AvrGdb::CommandPackets::VContSupportedActionsQuery; using AvrGdb::CommandPackets::VContContinueExecution; using AvrGdb::CommandPackets::VContStepExecution; using AvrGdb::CommandPackets::VContRangeStep; if (rawPacket.size() >= 2) { if (rawPacket[1] == 'p') { return std::make_unique(rawPacket); } if (rawPacket[1] == 'g') { return std::make_unique(rawPacket); } if (rawPacket[1] == 'P') { return std::make_unique(rawPacket); } if (rawPacket[1] == 'm') { return std::make_unique(rawPacket, this->gdbTargetDescriptor); } if (rawPacket[1] == 'M') { return std::make_unique(rawPacket, this->gdbTargetDescriptor); } const auto rawPacketString = std::string(rawPacket.begin() + 1, rawPacket.end()); if (rawPacketString.find("qXfer:memory-map:read::") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vFlashErase") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vFlashWrite") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vFlashDone") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vCont?") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vCont;c") == 0 || rawPacketString.find("vCont;C") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vCont;s") == 0 || rawPacketString.find("vCont;S") == 0) { return std::make_unique(rawPacket); } if (this->debugServerConfig.rangeStepping) { if (rawPacketString.find("vCont;r") == 0) { return std::make_unique(rawPacket); } } } return GdbRspDebugServer::resolveCommandPacket(rawPacket); } std::set>> AvrGdbRsp::getSupportedFeatures() { return { {Feature::SOFTWARE_BREAKPOINTS, std::nullopt}, {Feature::MEMORY_MAP_READ, std::nullopt}, {Feature::VCONT_ACTIONS_QUERY, std::nullopt}, }; } void AvrGdbRsp::handleTargetStoppedGdbResponse(Targets::TargetProgramCounter programAddress) { using Services::StringService; Logger::debug("Target stopped at byte address: 0x" + StringService::toHex(programAddress)); auto& activeRangeSteppingSession = this->activeDebugSession->activeRangeSteppingSession; if ( activeRangeSteppingSession.has_value() && programAddress >= activeRangeSteppingSession->range.startAddress && programAddress < activeRangeSteppingSession->range.endAddress ) { /* * The target stopped within the stepping range of an active range stepping session. * * We need to figure out why, and determine whether the stop should be reported to GDB. */ if (this->activeDebugSession->externalBreakpointsByAddress.contains(programAddress)) { /* * The target stopped due to an external breakpoint, set by GDB. * * We have to end the range stepping session and report this to GDB. */ Logger::debug("Reached external breakpoint within stepping range"); this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService); return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress); } if (activeRangeSteppingSession->interceptedAddresses.contains(programAddress)) { /* * The target stopped due to an intercepting breakpoint, but we're still within the stepping range, * which can only mean that we weren't sure where this instruction would lead to. * * We must perform a single step and see what happens. If the instruction takes us out of the stepping * range, we'll end the session and report back to GDB. */ Logger::debug("Reached intercepting breakpoint within stepping range"); Logger::debug("Attempting single step from 0x" + StringService::toHex(programAddress)); activeRangeSteppingSession->singleStepping = true; this->targetControllerService.stepTargetExecution(std::nullopt); return; } if (activeRangeSteppingSession->singleStepping) { /* * We performed a single step once and we're still within the stepping range, so we're good to * continue the range stepping session. */ Logger::debug("Completed single step - PC still within stepping range"); Logger::debug("Continuing range stepping"); activeRangeSteppingSession->singleStepping = false; this->targetControllerService.continueTargetExecution(std::nullopt, std::nullopt); return; } /* * If we get here, the target stopped for an unknown reason that doesn't seem to have anything to do with * the active range stepping session. * * This could be due to a permanent breakpoint in the target's program code. * * We have to end the range stepping session and report the stop to GDB. */ Logger::debug("Target stopped within stepping range, but for an unknown reason"); this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService); return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress); } // Report the stop to GDB return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress); } }