#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" #include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp" #include "CommandPackets/EepromFill.hpp" namespace DebugServer::Gdb::AvrGdb { using namespace Exceptions; using Targets::TargetRegisterDescriptor; using Targets::TargetRegisterType; AvrGdbRsp::AvrGdbRsp( const DebugServerConfig& debugServerConfig, const Targets::TargetDescriptor& targetDescriptor, EventListener& eventListener, EventFdNotifier& eventNotifier ) : GdbRspDebugServer(debugServerConfig, targetDescriptor, eventListener, eventNotifier) , gdbTargetDescriptor(targetDescriptor) {} DebugSession* AvrGdbRsp::startDebugSession(Connection&& connection) { this->activeDebugSession.emplace( std::move(connection), this->getSupportedFeatures(), 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 Gdb::CommandPackets::Monitor; using CommandPackets::ReadRegister; using CommandPackets::ReadRegisters; using CommandPackets::WriteRegister; using CommandPackets::ReadMemory; using CommandPackets::WriteMemory; using CommandPackets::ReadMemoryMap; using CommandPackets::FlashErase; using CommandPackets::FlashWrite; using CommandPackets::FlashDone; using CommandPackets::VContSupportedActionsQuery; using CommandPackets::VContContinueExecution; using CommandPackets::VContStepExecution; using CommandPackets::VContRangeStep; using CommandPackets::EepromFill; if (rawPacket.size() < 2) { throw Exception{"Invalid raw packet - no data"}; } if (rawPacket[1] == 'p') { return std::make_unique(rawPacket); } if (rawPacket[1] == 'g') { return std::make_unique(rawPacket, this->gdbTargetDescriptor); } 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); } if (rawPacket.size() > 1) { const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()}; if (rawPacketString.find("qXfer:memory-map:read::") == 0) { return std::make_unique(rawPacket, this->gdbTargetDescriptor); } if (rawPacketString.find("vFlashErase") == 0) { return std::make_unique(rawPacket, this->gdbTargetDescriptor); } if (rawPacketString.find("vFlashWrite") == 0) { return std::make_unique(rawPacket); } if (rawPacketString.find("vFlashDone") == 0) { return std::make_unique(rawPacket, this->gdbTargetDescriptor); } 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, this->gdbTargetDescriptor); } } if (rawPacketString.find("qRcmd") == 0) { // This is a monitor packet auto monitorCommand = std::make_unique(rawPacket); if (monitorCommand->command.find("eeprom fill") == 0) { return std::make_unique( std::move(*(monitorCommand.release())), this->gdbTargetDescriptor ); } } } return GdbRspDebugServer::resolveCommandPacket(rawPacket); } std::set>> AvrGdbRsp::getSupportedFeatures() { auto output = std::set>>{ {Feature::HARDWARE_BREAKPOINTS, std::nullopt}, {Feature::SOFTWARE_BREAKPOINTS, std::nullopt}, {Feature::MEMORY_MAP_READ, std::nullopt}, {Feature::VCONT_ACTIONS_QUERY, std::nullopt}, }; if (!this->debugServerConfig.packetAcknowledgement) { output.emplace(Feature::NO_ACK_MODE, std::nullopt); } return output; } void AvrGdbRsp::handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress 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(); 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.resumeTargetExecution(); 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); } // Report the stop to GDB return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress); } }