Massive refactor to accommodate RISC-V targets

- Refactored entire codebase (excluding the Insight component) to accommodate multiple target architectures (no longer specific to AVR)
- Deleted 'generate SVD' GDB monitor command - I will eventually move this functionality to the Bloom website
- Added unit size property to address spaces
- Many other changes which I couldn't be bothered to describe here
This commit is contained in:
Nav
2024-07-23 21:14:22 +01:00
parent 2986934485
commit 6cdbfbe950
331 changed files with 8815 additions and 8565 deletions

View File

@@ -17,6 +17,9 @@
#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;
@@ -30,7 +33,7 @@ namespace DebugServer::Gdb::AvrGdb
EventListener& eventListener,
EventFdNotifier& eventNotifier
)
: GdbRspDebugServer(debugServerConfig, eventListener, eventNotifier)
: GdbRspDebugServer(debugServerConfig, targetDescriptor, eventListener, eventNotifier)
, gdbTargetDescriptor(targetDescriptor)
{}
@@ -38,7 +41,6 @@ namespace DebugServer::Gdb::AvrGdb
this->activeDebugSession.emplace(
std::move(connection),
this->getSupportedFeatures(),
this->gdbTargetDescriptor,
this->debugServerConfig
);
@@ -57,52 +59,57 @@ namespace DebugServer::Gdb::AvrGdb
return this->activeDebugSession.has_value() ? &*(this->activeDebugSession) : nullptr;
}
std::unique_ptr<Gdb::CommandPackets::CommandPacket> 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;
std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket(const RawPacket& rawPacket) {
using Gdb::CommandPackets::Monitor;
if (rawPacket.size() >= 2) {
if (rawPacket[1] == 'p') {
return std::make_unique<ReadRegister>(rawPacket);
}
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[1] == 'g') {
return std::make_unique<ReadRegisters>(rawPacket);
}
if (rawPacket.size() < 2) {
throw Exception{"Invalid raw packet - no data"};
}
if (rawPacket[1] == 'P') {
return std::make_unique<WriteRegister>(rawPacket);
}
if (rawPacket[1] == 'p') {
return std::make_unique<ReadRegister>(rawPacket);
}
if (rawPacket[1] == 'm') {
return std::make_unique<ReadMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'g') {
return std::make_unique<ReadRegisters>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'M') {
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'P') {
return std::make_unique<WriteRegister>(rawPacket);
}
const auto rawPacketString = std::string(rawPacket.begin() + 1, rawPacket.end());
if (rawPacket[1] == 'm') {
return std::make_unique<ReadMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'M') {
return std::make_unique<WriteMemory>(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<ReadMemoryMap>(rawPacket);
return std::make_unique<ReadMemoryMap>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacketString.find("vFlashErase") == 0) {
return std::make_unique<FlashErase>(rawPacket);
return std::make_unique<FlashErase>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacketString.find("vFlashWrite") == 0) {
@@ -110,7 +117,7 @@ namespace DebugServer::Gdb::AvrGdb
}
if (rawPacketString.find("vFlashDone") == 0) {
return std::make_unique<FlashDone>(rawPacket);
return std::make_unique<FlashDone>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacketString.find("vCont?") == 0) {
@@ -127,7 +134,19 @@ namespace DebugServer::Gdb::AvrGdb
if (this->debugServerConfig.rangeStepping) {
if (rawPacketString.find("vCont;r") == 0) {
return std::make_unique<VContRangeStep>(rawPacket);
return std::make_unique<VContRangeStep>(rawPacket, this->gdbTargetDescriptor);
}
}
if (rawPacketString.find("qRcmd") == 0) {
// This is a monitor packet
auto monitorCommand = std::make_unique<Monitor>(rawPacket);
if (monitorCommand->command.find("eeprom fill") == 0) {
return std::make_unique<EepromFill>(
std::move(*(monitorCommand.release())),
this->gdbTargetDescriptor
);
}
}
}
@@ -184,7 +203,7 @@ namespace DebugServer::Gdb::AvrGdb
Logger::debug("Attempting single step from 0x" + StringService::toHex(programAddress));
activeRangeSteppingSession->singleStepping = true;
this->targetControllerService.stepTargetExecution(std::nullopt);
this->targetControllerService.stepTargetExecution();
return;
}
@@ -197,10 +216,7 @@ namespace DebugServer::Gdb::AvrGdb
Logger::debug("Continuing range stepping");
activeRangeSteppingSession->singleStepping = false;
this->targetControllerService.continueTargetExecution(
std::nullopt,
activeRangeSteppingSession->range.endAddress
);
this->targetControllerService.resumeTargetExecution();
return;
}
@@ -213,9 +229,7 @@ namespace DebugServer::Gdb::AvrGdb
* 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

View File

@@ -0,0 +1,107 @@
#include "EepromFill.hpp"
#include <QByteArray>
#include <algorithm>
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ::Exceptions::Exception;
using Exceptions::InvalidCommandOption;
EepromFill::EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor)
: Monitor(std::move(monitorPacket))
, eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor)
, eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor)
{
const auto fillValueOptionIt = this->commandOptions.find("value");
if (fillValueOptionIt != this->commandOptions.end() && fillValueOptionIt->second.has_value()) {
this->fillValue = Services::StringService::dataFromHex(*(fillValueOptionIt->second));
}
}
void EepromFill::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling EepromFill packet");
try {
const auto eepromSize = this->eepromMemorySegmentDescriptor.size();
const auto fillValueSize = this->fillValue.size();
if (fillValueSize == 0) {
throw InvalidCommandOption{"Fill value required"};
}
if (fillValueSize > eepromSize) {
throw InvalidCommandOption{
"Fill value size (" + std::to_string(fillValueSize) + " bytes) exceeds EEPROM size ("
+ std::to_string(eepromSize) + " bytes)"
};
}
if ((eepromSize % fillValueSize) != 0) {
Logger::warning(
"The fill value size (" + std::to_string(fillValueSize) + " bytes) is not a multiple of the EEPROM "
"size (" + std::to_string(eepromSize) + " bytes) - the fill value will be truncated"
);
}
Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM");
auto data = Targets::TargetMemoryBuffer{};
data.reserve(eepromSize);
// Repeat this->fillValue until we've filled `data`
while (data.size() < eepromSize) {
data.insert(
data.end(),
this->fillValue.begin(),
this->fillValue.begin() + static_cast<std::int32_t>(
std::min(fillValueSize, (eepromSize - data.size()))
)
);
}
const auto hexValues = Services::StringService::toHex(data);
Logger::debug("Filling EEPROM with values: " + hexValues);
targetControllerService.writeMemory(
this->eepromAddressSpaceDescriptor,
this->eepromMemorySegmentDescriptor,
this->eepromMemorySegmentDescriptor.addressRange.startAddress,
std::move(data)
);
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
"Filled " + std::to_string(eepromSize) + " bytes of EEPROM, with values: " + hexValues + "\n"
)});
} catch (const InvalidCommandOption& exception) {
Logger::error(exception.getMessage());
debugSession.connection.writePacket(
ResponsePacket{Services::StringService::toHex(exception.getMessage() + "\n")}
);
} catch (const Exception& exception) {
Logger::error("Failed to fill EEPROM - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <cstdint>
#include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
/**
* The EepromFill class implements a structure for the "monitor eeprom fill" GDB command.
*
* This command fills the target's EEPROM with the given value.
*/
class EepromFill: public Gdb::CommandPackets::Monitor
{
public:
const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor;
explicit EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
private:
Targets::TargetMemoryBuffer fillValue;
};
}

View File

@@ -15,31 +15,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
using namespace Exceptions;
FlashDone::FlashDone(const RawPacket& rawPacket)
FlashDone::FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor)
: CommandPacket(rawPacket)
, programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor)
, programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor)
{}
void FlashDone::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void FlashDone::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling FlashDone packet");
try {
if (debugSession.programmingSession.has_value()) {
const auto& programmingSession = debugSession.programmingSession.value();
Logger::info(
"Flushing " + std::to_string(programmingSession.buffer.size()) + " bytes to target's program memory"
);
targetControllerService.enableProgrammingMode();
targetControllerService.writeMemory(
Targets::TargetMemoryType::FLASH,
programmingSession.startAddress,
std::move(programmingSession.buffer)
);
debugSession.programmingSession.reset();
if (!debugSession.programmingSession.has_value()) {
throw Exception{"No active programming session"};
}
Logger::info(
"Flushing " + std::to_string(debugSession.programmingSession->buffer.size())
+ " bytes to target's program memory"
);
targetControllerService.enableProgrammingMode();
targetControllerService.writeMemory(
this->programMemoryAddressSpaceDescriptor,
this->programMemorySegmentDescriptor,
debugSession.programmingSession->startAddress,
std::move(debugSession.programmingSession->buffer)
);
debugSession.programmingSession.reset();
Logger::warning("Program memory updated");
targetControllerService.disableProgrammingMode();
@@ -47,7 +57,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
targetControllerService.resetTarget();
Logger::info("Target reset complete");
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to handle FlashDone packet - " + exception.getMessage());
@@ -60,7 +70,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
Logger::error("Failed to disable programming mode - " + exception.getMessage());
}
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -4,9 +4,10 @@
#include <optional>
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
@@ -16,10 +17,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
class FlashDone: public Gdb::CommandPackets::CommandPacket
{
public:
explicit FlashDone(const RawPacket& rawPacket);
const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
explicit FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -3,6 +3,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -15,40 +16,40 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
using namespace Exceptions;
FlashErase::FlashErase(const RawPacket& rawPacket)
FlashErase::FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor)
: CommandPacket(rawPacket)
, programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor)
, programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor)
{
const auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 12),
static_cast<int>(this->data.size() - 12)
);
using Services::StringService;
if (rawPacket.size() < 8) {
throw Exception{"Invalid packet length"};
}
/*
* The flash erase ('vFlashErase') packet consists of two segments, an address and a length, separated by a
* comma.
*
* Example: $vFlashErase:00000000,00004f00#f4
*/
const auto packetSegments = packetString.split(",");
if (packetSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
);
const auto command = std::string{this->data.begin() + 12, this->data.end()};
const auto delimiterPos = command.find_first_of(',');
if (delimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from flash erase packet data");
}
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse length from flash erase packet data");
}
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
this->bytes = StringService::toUint32(command.substr(delimiterPos + 1), 16);
}
void FlashErase::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void FlashErase::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling FlashErase packet");
try {
@@ -57,9 +58,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
Logger::warning("Erasing program memory, in preparation for programming");
// We don't erase a specific address range - we just erase the entire program memory.
targetControllerService.eraseMemory(debugSession.gdbTargetDescriptor.targetDescriptor.programMemoryType);
targetControllerService.eraseMemory(
this->programMemoryAddressSpaceDescriptor,
this->programMemorySegmentDescriptor
);
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to erase flash memory - " + exception.getMessage());
@@ -72,7 +76,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
Logger::error("Failed to disable programming mode - " + exception.getMessage());
}
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -4,9 +4,10 @@
#include <optional>
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
@@ -19,11 +20,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
public:
std::uint32_t startAddress = 0;
std::uint32_t bytes = 0;
const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
explicit FlashErase(const RawPacket& rawPacket);
explicit FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -1,10 +1,9 @@
#include "FlashWrite.hpp"
#include <QByteArray>
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -20,44 +19,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
FlashWrite::FlashWrite(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
using Services::StringService;
if (this->data.size() < 15) {
throw Exception("Invalid packet length");
throw Exception{"Invalid packet length"};
}
/*
* The flash write ('vFlashWrite') packet consists of two segments, an address and a buffer.
*
* Seperated by a colon.
* The flash write ('vFlashWrite') packet consists of two segments: an address and a buffer, seperated by a
* colon.
*/
auto colonIt = std::find(this->data.begin() + 12, this->data.end(), ':');
const auto delimiterIt = std::find(this->data.begin() + 12, this->data.end(), ':');
if (colonIt == this->data.end()) {
throw Exception("Failed to find colon delimiter in write flash packet.");
if (delimiterIt == this->data.end()) {
throw Exception{"Failed to find colon delimiter in write flash packet."};
}
bool conversionStatus = false;
this->startAddress = QByteArray(
reinterpret_cast<const char*>(this->data.data() + 12),
std::distance(this->data.begin(), colonIt) - 12
).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from flash write packet data");
}
this->buffer = Targets::TargetMemoryBuffer(colonIt + 1, this->data.end());
this->startAddress = StringService::toUint32(std::string{this->data.begin() + 12, delimiterIt}, 16);
this->buffer = Targets::TargetMemoryBuffer{delimiterIt + 1, this->data.end()};
}
void FlashWrite::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void FlashWrite::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling FlashWrite packet");
try {
if (this->buffer.empty()) {
throw Exception("Received empty buffer from GDB");
throw Exception{"Received empty buffer from GDB"};
}
if (!debugSession.programmingSession.has_value()) {
debugSession.programmingSession = ProgrammingSession(this->startAddress, this->buffer);
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
} else {
auto& programmingSession = debugSession.programmingSession.value();
@@ -65,7 +61,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
const auto expectedStartAddress = (currentEndAddress + 1);
if (this->startAddress < expectedStartAddress) {
throw Exception("Invalid start address from GDB - the buffer would overlap a previous buffer");
throw Exception{"Invalid start address from GDB - the buffer would overlap a previous buffer"};
}
if (this->startAddress > expectedStartAddress) {
@@ -84,7 +80,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
);
}
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to handle FlashWrite packet - " + exception.getMessage());
@@ -97,7 +93,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
Logger::error("Failed to disable programming mode - " + exception.getMessage());
}
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -24,6 +24,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -1,5 +1,7 @@
#include "ReadMemory.hpp"
#include <cassert>
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
@@ -18,141 +20,139 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
using Exceptions::Exception;
ReadMemory::ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
: CommandPacket(rawPacket)
{
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
: ReadMemory(rawPacket, gdbTargetDescriptor, ReadMemory::extractPacketData(rawPacket))
{}
const auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(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.
*/
const 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;
const auto gdbStartAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from read memory packet data");
}
/*
* Extract the memory type from the memory address (see Gdb::TargetDescriptor::memoryOffsetsByType for more on
* this).
*/
this->memoryType = gdbTargetDescriptor.getMemoryTypeFromGdbAddress(gdbStartAddress);
this->startAddress = gdbStartAddress & ~(gdbTargetDescriptor.getMemoryOffset(this->memoryType));
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(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void ReadMemory::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadMemory packet");
try {
const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType;
const auto memoryDescriptorIt = memoryDescriptorsByType.find(this->memoryType);
if (memoryDescriptorIt == memoryDescriptorsByType.end()) {
throw Exception("Target does not support the requested memory type.");
}
if (this->bytes == 0) {
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>()));
debugSession.connection.writePacket(ResponsePacket{Targets::TargetMemoryBuffer{}});
return;
}
const auto& memoryDescriptor = memoryDescriptorIt->second;
const auto addressRange = Targets::TargetMemoryAddressRange{
this->startAddress,
this->startAddress + this->bytes - 1
};
if (this->memoryType == Targets::TargetMemoryType::EEPROM) {
// GDB sends EEPROM addresses in relative form - we convert them to absolute form, here.
this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress;
const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
addressRange
);
/*
* First pass to ensure that we can read all of the memory before attempting to do so. And to ensure that
* the requested address range completely resides within known memory segments.
*/
auto accessibleBytes = Targets::TargetMemorySize{0};
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
if (!memorySegmentDescriptor->debugModeAccess.readable) {
throw Exception{
"Attempted to access restricted memory segment (" + memorySegmentDescriptor->key
+ ") - segment not readable in debug mode"
};
}
accessibleBytes += memorySegmentDescriptor->addressRange.intersectingSize(addressRange);
}
/*
* In AVR targets, RAM is mapped to many registers and peripherals - we don't want to block GDB from
* accessing them.
* GDB will sometimes request an excess of up to two bytes outside the memory segment address range, even
* though we provide it with a memory map. I don't know why it does this, but I do know that we must
* tolerate it, otherwise GDB will moan.
*/
const auto permittedStartAddress = (this->memoryType == Targets::TargetMemoryType::RAM)
? 0x00
: memoryDescriptor.addressRange.startAddress;
const auto permittedEndAddress = memoryDescriptor.addressRange.endAddress + 2;
if (
this->startAddress < permittedStartAddress
|| (this->startAddress + (this->bytes - 1)) > permittedEndAddress
) {
if (accessibleBytes < this->bytes && (this->bytes - accessibleBytes) > 2) {
/*
* GDB can be configured to generate backtraces past the main function and the internal entry point
* of the application. Although this isn't very useful to most devs, CLion now seems to enable it by
* default. Somewhere between CLion 2021.1 and 2022.1, it began issuing the "-gdb-set backtrace past-entry on"
* command to GDB, at the beginning of each debug session.
* GDB has requested memory that, at least partially, does not reside in any known memory segment.
*
* This means that GDB will attempt to walk down the stack to identify every frame. The problem is that
* GDB doesn't really know where the stack begins, so it ends up in a loop, continually issuing read
* memory commands. This has exposed an issue on our end - we need to validate the requested memory
* address range and reject any request for a range that's not within the target's memory. We do this
* here.
* This could be a result of GDB being configured to generate backtraces past the main function and
* the internal entry point of the application. This means that GDB will attempt to walk down the stack
* to identify every frame. The problem is that GDB doesn't really know where the stack begins, so it
* probes the target by continuously issuing read memory commands until the server responds with an
* error.
*
* CLion seems to enable this by default. Somewhere between CLion 2021.1 and 2022.1, it began issuing
* the "-gdb-set backtrace past-entry on" command to GDB, at the beginning of each debug session.
*
* We don't throw an exception here, because this isn't really an error and so it's best not to report
* it as such. I don't think it's an error because it's expected behaviour, even though we respond to
* GDB with an error response.
*/
Logger::debug(
"GDB requested access to memory which is outside the target's memory range - returning error "
"response"
"GDB requested access to memory which does not reside within any memory segment - returning error "
"response"
);
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
return;
}
/*
* GDB may request more bytes than what's available (even though we give it a memory map?!) - ensure that
* we don't try to read any more than what's available.
*
* We fill the out-of-bounds bytes with 0x00, below.
*/
const auto bytesToRead = (this->startAddress <= memoryDescriptor.addressRange.endAddress)
? std::min(this->bytes, (memoryDescriptor.addressRange.endAddress - this->startAddress) + 1)
: 0;
auto buffer = Targets::TargetMemoryBuffer(this->bytes, 0x00);
auto memoryBuffer = Targets::TargetMemoryBuffer();
{
const auto atomicSession = targetControllerService.makeAtomicSession();
if (bytesToRead > 0) {
memoryBuffer = targetControllerService.readMemory(
this->memoryType,
this->startAddress,
bytesToRead
);
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
const auto segmentStartAddress = std::max(
this->startAddress,
memorySegmentDescriptor->addressRange.startAddress
);
const auto segmentBuffer = targetControllerService.readMemory(
this->addressSpaceDescriptor,
*memorySegmentDescriptor,
segmentStartAddress,
memorySegmentDescriptor->addressRange.intersectingSize(addressRange)
);
const auto bufferOffsetIt = buffer.begin() + (segmentStartAddress - this->startAddress);
assert(segmentBuffer.size() <= std::distance(bufferOffsetIt, buffer.end()));
std::copy(segmentBuffer.begin(), segmentBuffer.end(), bufferOffsetIt);
}
}
if (bytesToRead < this->bytes) {
// GDB requested some out-of-bounds memory - fill the inaccessible bytes with 0x00
memoryBuffer.insert(memoryBuffer.end(), (this->bytes - bytesToRead), 0x00);
}
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(memoryBuffer)));
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
} catch (const Exception& exception) {
Logger::error("Failed to read memory from target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
ReadMemory::PacketData ReadMemory::extractPacketData(const RawPacket& rawPacket) {
using Services::StringService;
if (rawPacket.size() < 8) {
throw Exception{"Invalid packet length"};
}
const auto command = std::string{rawPacket.begin() + 2, rawPacket.end() - 3};
const auto delimiterPos = command.find_first_of(',');
if (delimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
return {
StringService::toUint32(command.substr(0, delimiterPos), 16),
StringService::toUint32(command.substr(delimiterPos + 1), 16)
};
}
ReadMemory::ReadMemory(
const RawPacket& rawPacket,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
ReadMemory::PacketData&& packetData
)
: CommandPacket(rawPacket)
, addressSpaceDescriptor(gdbTargetDescriptor.addressSpaceDescriptorFromGdbAddress(packetData.gdbStartAddress))
, startAddress(gdbTargetDescriptor.translateGdbAddress(packetData.gdbStartAddress))
, bytes(packetData.bytes)
{}
}

View File

@@ -4,9 +4,10 @@
#include <optional>
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
@@ -17,26 +18,32 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
class ReadMemory: public Gdb::CommandPackets::CommandPacket
{
public:
/**
* Start address of the memory operation.
*/
Targets::TargetMemoryAddress startAddress = 0;
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
/**
* The type of memory to read from.
*/
Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH;
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemorySize bytes;
/**
* Number of bytes to read.
*/
Targets::TargetMemorySize bytes = 0;
explicit ReadMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
private:
struct PacketData
{
GdbMemoryAddress gdbStartAddress;
std::uint32_t bytes;
};
static PacketData extractPacketData(const RawPacket& rawPacket);
ReadMemory(
const RawPacket& rawPacket,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
PacketData&& packetData
);
};
}

View File

@@ -2,6 +2,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
@@ -12,111 +13,74 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
using Exceptions::Exception;
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket)
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
: CommandPacket(rawPacket)
, eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor)
, programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor)
, eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor)
{
if (this->data.size() < 26) {
throw Exception("Invalid packet length");
}
using Services::StringService;
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 23), // +23 to exclude the "qXfer:memory-map:read::"
static_cast<int>(this->data.size() - 23)
);
if (this->data.size() < 26) {
throw Exception{"Invalid packet length"};
}
/*
* The read memory map ('qXfer:memory-map:read::...') packet consists of two segments, an offset and a length.
* These are separated by a comma character.
*/
auto packetSegments = packetString.split(",");
const auto command = std::string{this->data.begin() + 23, this->data.end()};
if (packetSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
);
const auto delimiterPos = command.find_first_of(',');
if (delimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
bool conversionStatus = false;
this->offset = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse offset from read memory map packet data");
}
this->length = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse read length from read memory map packet data");
}
this->offset = StringService::toUint32(command.substr(0, delimiterPos), 16);
this->length = StringService::toUint32(command.substr(delimiterPos + 1), 16);
}
void ReadMemoryMap::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void ReadMemoryMap::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadMemoryMap packet");
using Targets::TargetMemoryType;
const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType;
const auto& ramDescriptor = memoryDescriptorsByType.at(TargetMemoryType::RAM);
const auto& flashDescriptor = memoryDescriptorsByType.at(TargetMemoryType::FLASH);
const auto eepromDescriptorIt = memoryDescriptorsByType.find(TargetMemoryType::EEPROM);
const auto eepromDescriptor = eepromDescriptorIt != memoryDescriptorsByType.end()
? std::optional(eepromDescriptorIt->second)
: std::nullopt;
const auto ramGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset(
Targets::TargetMemoryType::RAM
);
const auto eepromGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset(
Targets::TargetMemoryType::EEPROM
);
const auto flashGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset(
Targets::TargetMemoryType::FLASH
);
/*
* We include register and EEPROM memory in our RAM section. This allows GDB to access registers and EEPROM
* data via memory read/write packets.
*
* Like SRAM, GDB applies an offset to EEPROM addresses. We account for that offset in our ramSectionSize.
*
* The SRAM and EEPROM offsets allow for a maximum of 65KB of SRAM. But that must also accommodate the
* register addresses, which can vary in size.
*
* As of writing this (Dec 2022), there are no 8-bit AVR targets on sale today, with 65KB+ of SRAM.
*/
const auto eepromEndAddress = eepromGdbOffset + (eepromDescriptor.has_value() ? eepromDescriptor->size() : 0);
const auto ramSectionSize = eepromEndAddress - ramGdbOffset;
const auto flashSize = flashDescriptor.size();
const auto flashPageSize = flashDescriptor.pageSize.value();
const auto ramSectionEndAddress = gdbTargetDescriptor.translateTargetMemoryAddress(
this->eepromMemorySegmentDescriptor.addressRange.endAddress,
this->eepromAddressSpaceDescriptor,
this->eepromMemorySegmentDescriptor
);
const auto ramSectionStartAddress = TargetDescriptor::SRAM_ADDRESS_MASK;
const auto ramSectionSize = ramSectionEndAddress - ramSectionStartAddress + 1;
const auto memoryMap =
std::string("<memory-map>")
+ "<memory type=\"ram\" start=\"" + std::to_string(ramGdbOffset) + "\" length=\"" + std::to_string(ramSectionSize) + "\"/>"
+ "<memory type=\"flash\" start=\"" + std::to_string(flashGdbOffset) + "\" length=\"" + std::to_string(flashSize) + "\">"
+ "<property name=\"blocksize\">" + std::to_string(flashPageSize) + "</property>"
std::string{"<memory-map>"}
+ "<memory type=\"ram\" start=\"" + std::to_string(ramSectionStartAddress) + "\" length=\"" + std::to_string(ramSectionSize) + "\"/>"
+ "<memory type=\"flash\" start=\"0\" length=\"" + std::to_string(this->programMemorySegmentDescriptor.size()) + "\">"
+ "<property name=\"blocksize\">" + std::to_string(this->programMemorySegmentDescriptor.pageSize.value()) + "</property>"
+ "</memory>"
+ "</memory-map>";
auto responseData = std::vector<unsigned char>{'l'};
if (this->offset < memoryMap.size() && this->length > 0) {
auto memoryMapData = std::vector<unsigned char>(
responseData.insert(
responseData.end(),
memoryMap.begin() + this->offset,
memoryMap.begin() + std::min(
static_cast<long>(this->offset + this->length),
static_cast<long>(memoryMap.size())
memoryMap.begin() + this->offset + std::min(
static_cast<long>(this->length),
static_cast<long>(memoryMap.size() - this->offset)
)
);
auto responseData = std::vector<unsigned char>({'l'});
std::move(memoryMapData.begin(), memoryMapData.end(), std::back_inserter(responseData));
debugSession.connection.writePacket(ResponsePacket(responseData));
return;
}
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>({'l'})));
debugSession.connection.writePacket(ResponsePacket{responseData});
}
}

View File

@@ -3,6 +3,10 @@
#include <cstdint>
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
@@ -13,6 +17,10 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
class ReadMemoryMap: public Gdb::CommandPackets::CommandPacket
{
public:
const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor;
/**
* The offset of the memory map, from which to read.
*/
@@ -23,10 +31,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
*/
std::uint32_t length = 0;
explicit ReadMemoryMap(const RawPacket& rawPacket);
explicit ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -1,10 +1,10 @@
#include "ReadRegister.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
@@ -24,16 +24,23 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
ReadRegister::ReadRegister(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
using Services::StringService;
if (this->data.size() < 2) {
throw Exception("Invalid packet length");
throw Exception{"Invalid packet length"};
}
this->registerId = static_cast<GdbRegisterId>(
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
StringService::toUint32(std::string{this->data.begin() + 1, this->data.end()}, 16)
);
}
void ReadRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void ReadRegister::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadRegister packet");
try {
@@ -47,49 +54,60 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
const auto programCounter = targetControllerService.getProgramCounter();
debugSession.connection.writePacket(
ResponsePacket(Services::StringService::toHex(Targets::TargetMemoryBuffer({
ResponsePacket{Services::StringService::toHex(Targets::TargetMemoryBuffer{
static_cast<unsigned char>(programCounter),
static_cast<unsigned char>(programCounter >> 8),
static_cast<unsigned char>(programCounter >> 16),
static_cast<unsigned char>(programCounter >> 24),
})))
})}
);
return;
}
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(this->registerId);
if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) {
/*
* GDB has requested the program counter. We can't access this in the same way as we do with other
* registers.
*/
const auto stackPointer = targetControllerService.getStackPointer();
const auto targetRegisterDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
debugSession.connection.writePacket(
ResponsePacket{Services::StringService::toHex(Targets::TargetMemoryBuffer{
static_cast<unsigned char>(stackPointer),
static_cast<unsigned char>(stackPointer >> 8),
})}
);
return;
}
const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId);
const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find(
this->registerId
);
if (!targetRegisterDescriptorId.has_value()) {
throw Exception("GDB requested an invalid/unknown register");
if (
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
) {
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
}
auto registerValue = targetControllerService.readRegisters({*targetRegisterDescriptorId}).front().value;
// GDB expects register values to be in LSB.
std::reverse(registerValue.begin(), registerValue.end());
auto registerValue = targetControllerService.readRegister(*(targetRegisterDescriptorIt->second));
std::reverse(registerValue.begin(), registerValue.end()); // MSB to LSB
const auto& gdbRegisterDescriptor = gdbRegisterDescriptorIt->second;
if (registerValue.size() < gdbRegisterDescriptor.size) {
/*
* The register on the target is smaller than the size expected by GDB.
*
* Insert the rest of the bytes.
*/
// The register on the target is smaller than the size expected by GDB.
registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00);
}
debugSession.connection.writePacket(
ResponsePacket(Services::StringService::toHex(registerValue))
);
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(registerValue)});
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -1,13 +1,14 @@
#include "ReadRegisters.hpp"
#include <algorithm>
#include <iterator>
#include <cassert>
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -16,93 +17,78 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptorIds;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
ReadRegisters::ReadRegisters(const RawPacket& rawPacket)
ReadRegisters::ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
: CommandPacket(rawPacket)
, gpRegistersMemorySegmentDescriptor(gdbTargetDescriptor.gpRegistersMemorySegmentDescriptor)
{}
void ReadRegisters::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void ReadRegisters::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadRegisters packet");
try {
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
auto descriptorIds = TargetRegisterDescriptorIds();
auto buffer = Targets::TargetMemoryBuffer(39, 0x00);
// Read all target registers mapped to a GDB register
for (const auto& [gdbRegisterId, gdbRegisterDescriptor] : targetDescriptor.gdbRegisterDescriptorsById) {
const auto registerDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
gdbRegisterId
);
if (registerDescriptorId.has_value()) {
descriptorIds.insert(*registerDescriptorId);
auto gpRegDescriptors = Targets::TargetRegisterDescriptors{};
std::transform(
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.begin(),
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end(),
std::back_inserter(gpRegDescriptors),
[] (const auto& pair) {
return pair.second;
}
}
Targets::TargetRegisters registerSet;
Targets::TargetMemoryAddress programCounter;
);
{
const auto atomicSession = targetControllerService.makeAtomicSession();
registerSet = targetControllerService.readRegisters(descriptorIds);
programCounter = targetControllerService.getProgramCounter();
}
for (auto& [regDesc, regVal] : targetControllerService.readRegisters(gpRegDescriptors)) {
if (regDesc.type != Targets::TargetRegisterType::GENERAL_PURPOSE_REGISTER) {
// Status register (SREG)
assert(regVal.size() == 1);
buffer[32] = regVal[0];
continue;
}
/*
* Sort each register by their respective GDB register ID - this will leave us with a collection of
* registers in the order expected by the GDB client.
*/
std::sort(
registerSet.begin(),
registerSet.end(),
[this, &targetDescriptor] (const TargetRegister& regA, const TargetRegister& regB) {
return targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regA.descriptorId).value() <
targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regB.descriptorId).value();
}
);
const auto bufferOffset = regDesc.startAddress
- this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress;
/*
* 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 and implode the
* values.
*/
auto registers = std::vector<unsigned char>();
for (auto& reg : registerSet) {
std::reverse(reg.value.begin(), reg.value.end());
assert((buffer.size() - bufferOffset) >= regVal.size());
const auto gdbRegisterId = targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(
reg.descriptorId
).value();
const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(gdbRegisterId);
if (reg.value.size() < gdbRegisterDescriptor.size) {
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
/*
* GDB expects register values in LSB form, which is why we use reverse iterators below.
*
* This isn't really necessary though, as all of the registers that are handled here are
* single-byte registers.
*/
std::copy(regVal.rbegin(), regVal.rend(), buffer.begin() + bufferOffset);
}
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
const auto spValue = targetControllerService.getStackPointer();
buffer[33] = static_cast<unsigned char>(spValue);
buffer[34] = static_cast<unsigned char>(spValue >> 8);
const auto pcValue = targetControllerService.getProgramCounter();
buffer[35] = static_cast<unsigned char>(pcValue);
buffer[36] = static_cast<unsigned char>(pcValue >> 8);
buffer[37] = static_cast<unsigned char>(pcValue >> 16);
buffer[38] = static_cast<unsigned char>(pcValue >> 24);
}
// Finally, include the program counter (which GDB expects to reside at the end)
registers.insert(registers.end(), static_cast<unsigned char>(programCounter));
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 8));
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 16));
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 24));
debugSession.connection.writePacket(
ResponsePacket(Services::StringService::toHex(registers))
);
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
} catch (const Exception& exception) {
Logger::error("Failed to read registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -5,6 +5,8 @@
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
@@ -15,10 +17,14 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
class ReadRegisters: public Gdb::CommandPackets::CommandPacket
{
public:
explicit ReadRegisters(const RawPacket& rawPacket);
const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor;
explicit ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
: CommandPacket(rawPacket)
{}
void VContContinueExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void VContContinueExecution::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling VContContinueExecution packet");
try {
targetControllerService.continueTargetExecution(std::nullopt, std::nullopt);
targetControllerService.resumeTargetExecution();
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -17,6 +17,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -2,6 +2,7 @@
#include <string>
#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp"
#include "src/Services/Avr8InstructionService.hpp"
#include "src/Services/StringService.hpp"
#include "src/Services/PathService.hpp"
@@ -13,43 +14,42 @@
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ErrorResponsePacket;
using ::Exceptions::Exception;
VContRangeStep::VContRangeStep(const RawPacket& rawPacket)
VContRangeStep::VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
: CommandPacket(rawPacket)
, programAddressSpaceDescriptor(gdbTargetDescriptor.programAddressSpaceDescriptor)
, programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor)
{
using Services::StringService;
if (this->data.size() < 10) {
throw Exception("Unexpected VContRangeStep packet size");
throw Exception{"Unexpected VContRangeStep packet size"};
}
const auto commandData = std::string(this->data.begin() + 7, this->data.end());
const auto command = std::string{this->data.begin() + 7, this->data.end()};
const auto delimiterPosition = commandData.find(',');
const auto threadIdDelimiterPosition = commandData.find(':');
if (delimiterPosition == std::string::npos || delimiterPosition >= (commandData.size() - 1)) {
throw Exception("Invalid VContRangeStep packet");
const auto delimiterPos = command.find(',');
const auto threadIdDelimiterPos = command.find(':');
if (delimiterPos == std::string::npos || delimiterPos >= (command.size() - 1)) {
throw Exception{"Invalid VContRangeStep packet"};
}
const auto& delimiterIt = commandData.begin() + static_cast<decltype(commandData)::difference_type>(
delimiterPosition
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
this->endAddress = StringService::toUint32(
command.substr(delimiterPos + 1, threadIdDelimiterPos - (delimiterPos + 1)),
16
);
const auto startAddressHex = std::string(commandData.begin(), delimiterIt);
const auto endAddressHex = std::string(
delimiterIt + 1,
threadIdDelimiterPosition != std::string::npos
? commandData.begin() + static_cast<decltype(commandData)::difference_type>(threadIdDelimiterPosition)
: commandData.end()
);
this->startAddress = static_cast<Targets::TargetMemoryAddress>(std::stoi(startAddressHex, nullptr, 16));
this->endAddress = static_cast<Targets::TargetMemoryAddress>(std::stoi(endAddressHex, nullptr, 16));
}
void VContRangeStep::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void VContRangeStep::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
using Targets::Microchip::Avr8::OpcodeDecoder::Decoder;
using Services::Avr8InstructionService;
using Services::StringService;
@@ -59,19 +59,17 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
Logger::debug("Requested stepping range end address (exclusive): 0x" + StringService::toHex(this->endAddress));
try {
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
const auto& programMemoryAddressRange = targetDescriptor.memoryDescriptorsByType.at(
targetDescriptor.programMemoryType
).addressRange;
const auto stepAddressRange = Targets::TargetMemoryAddressRange{this->startAddress, this->endAddress};
const auto stepByteSize = stepAddressRange.size() - 1; // -1 because the end address is exclusive
const auto& programMemoryAddressRange = this->programMemorySegmentDescriptor.addressRange;
if (
this->startAddress > this->endAddress
|| (this->startAddress % 2) != 0
|| (this->endAddress % 2) != 0
|| this->startAddress < programMemoryAddressRange.startAddress
|| this->endAddress > programMemoryAddressRange.endAddress
stepAddressRange.startAddress > stepAddressRange.endAddress
|| (stepAddressRange.startAddress % 2) != 0
|| (stepAddressRange.endAddress % 2) != 0
|| !programMemoryAddressRange.contains(stepAddressRange)
) {
throw Exception("Invalid address range in VContRangeStep");
throw Exception{"Invalid address range in VContRangeStep"};
}
if (debugSession.activeRangeSteppingSession.has_value()) {
@@ -81,26 +79,30 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
debugSession.terminateRangeSteppingSession(targetControllerService);
}
if (this->startAddress == this->endAddress || (this->endAddress - this->startAddress) <= 2) {
if (stepByteSize <= 2) {
// Single step requested. No need for a range step here.
targetControllerService.stepTargetExecution(std::nullopt);
targetControllerService.stepTargetExecution();
debugSession.waitingForBreak = true;
return;
}
const auto addressRange = Targets::TargetMemoryAddressRange(this->startAddress, this->endAddress);
auto rangeSteppingSession = RangeSteppingSession(addressRange, {});
auto rangeSteppingSession = RangeSteppingSession{stepAddressRange, {}};
const auto instructionsByAddress = Avr8InstructionService::fetchInstructions(
addressRange,
targetDescriptor,
targetControllerService
const auto instructionsByAddress = Decoder::decode(
stepAddressRange.startAddress,
targetControllerService.readMemory(
this->programAddressSpaceDescriptor,
this->programMemorySegmentDescriptor,
stepAddressRange.startAddress,
stepByteSize
)
);
Logger::debug(
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instructions within stepping range "
"(byte addresses) 0x" + StringService::toHex(addressRange.startAddress) + " -> 0x"
+ StringService::toHex(addressRange.endAddress) + ", in preparation for new range stepping session"
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instruction(s) within stepping range "
"(byte addresses) 0x" + StringService::toHex(stepAddressRange.startAddress) + " -> 0x"
+ StringService::toHex(stepAddressRange.endAddress) + ", in preparation for new range stepping "
"session"
);
for (const auto& [instructionAddress, instruction] : instructionsByAddress) {
@@ -144,10 +146,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
continue;
}
if (
*destinationAddress < programMemoryAddressRange.startAddress
|| *destinationAddress > programMemoryAddressRange.endAddress
) {
if (!programMemoryAddressRange.contains(*destinationAddress)) {
/*
* This instruction may jump to an invalid address. Someone screwed up here - could be
* something wrong in Bloom (opcode decoding bug, incorrect program memory address range in
@@ -158,7 +157,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
*/
Logger::debug(
"Intercepting CCPF instruction (\"" + instruction->name + "\") with invalid destination "
"byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x"
"byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x"
+ StringService::toHex(instructionAddress)
);
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
@@ -166,8 +165,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
}
if (
*destinationAddress < addressRange.startAddress
|| *destinationAddress >= addressRange.endAddress
*destinationAddress < stepAddressRange.startAddress
|| *destinationAddress >= stepAddressRange.endAddress
) {
/*
* This instruction may jump to an address outside the requested stepping range.
@@ -185,6 +184,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
}
}
/*
* Finally, ensure that we intercept the first instruction outside the range (which is the end address
* of the range, because it's exclusive).
*/
rangeSteppingSession.interceptedAddresses.insert(stepAddressRange.endAddress);
debugSession.startRangeSteppingSession(std::move(rangeSteppingSession), targetControllerService);
/*
@@ -195,12 +200,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
* we should continue. See that member function for more.
*/
debugSession.activeRangeSteppingSession->singleStepping = true;
targetControllerService.stepTargetExecution(std::nullopt);
targetControllerService.stepTargetExecution();
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to start new range stepping session - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -4,6 +4,9 @@
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
@@ -16,13 +19,18 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
class VContRangeStep: public Gdb::CommandPackets::CommandPacket
{
public:
const Targets::TargetAddressSpaceDescriptor& programAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemoryAddress endAddress;
explicit VContRangeStep(const RawPacket& rawPacket);
explicit VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
: CommandPacket(rawPacket)
{}
void VContStepExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void VContStepExecution::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling VContStepExecution packet");
try {
targetControllerService.stepTargetExecution(std::nullopt);
targetControllerService.stepTargetExecution();
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -16,6 +16,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -12,15 +12,20 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
: CommandPacket(rawPacket)
{}
void VContSupportedActionsQuery::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void VContSupportedActionsQuery::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling VContSupportedActionsQuery packet");
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
debugSession.connection.writePacket(ResponsePackets::ResponsePacket(
debugSession.connection.writePacket(ResponsePackets::ResponsePacket{
debugSession.serverConfig.rangeStepping
? "vCont;c;C;s;S;r"
: "vCont;c;C;s;S"
)
}
);
}
}

View File

@@ -20,6 +20,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -3,6 +3,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -16,124 +17,116 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
using namespace Exceptions;
WriteMemory::WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
: CommandPacket(rawPacket)
{
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
: WriteMemory(rawPacket, gdbTargetDescriptor, WriteMemory::extractPacketData(rawPacket))
{}
const auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
/*
* The write memory ('M') packet consists of three segments, an address, a length and a buffer.
* The address and length are separated by a comma character, and the buffer proceeds a colon character.
*/
const 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;
const auto gdbStartAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from write memory packet data");
}
this->memoryType = gdbTargetDescriptor.getMemoryTypeFromGdbAddress(gdbStartAddress);
this->startAddress = gdbStartAddress & ~(gdbTargetDescriptor.getMemoryOffset(this->memoryType));
const auto lengthAndBufferSegments = packetSegments.at(1).split(":");
if (lengthAndBufferSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: "
+ std::to_string(lengthAndBufferSegments.size())
);
}
const auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse write length from write memory packet data");
}
this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString());
if (this->buffer.size() != bufferSize) {
throw Exception("Buffer size does not match length value given in write memory packet");
}
}
void WriteMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void WriteMemory::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling WriteMemory packet");
try {
const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType;
const auto memoryDescriptorIt = memoryDescriptorsByType.find(this->memoryType);
if (memoryDescriptorIt == memoryDescriptorsByType.end()) {
throw Exception("Target does not support the requested memory type.");
}
if (this->memoryType == Targets::TargetMemoryType::FLASH) {
/*
* This shouldn't happen - GDB should send the FlashWrite (vFlashWrite) packet to write to the target's
* program memory.
*
* A number of actions have to be taken before we can write to the target's program memory - this is
* all covered in the FlashWrite and FlashDone command classes. I don't want to cover it again in here,
* so just respond with an error and request that this issue be reported.
*/
throw Exception(
"GDB attempted to write to program memory via an \"M\" packet - this is not supported. Please "
"report this issue to Bloom developers with the full debug log."
);
}
if (this->buffer.size() == 0) {
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
return;
}
const auto& memoryDescriptor = memoryDescriptorIt->second;
if (this->memoryType == Targets::TargetMemoryType::EEPROM) {
// GDB sends EEPROM addresses in relative form - we convert them to absolute form, here.
this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress;
}
/*
* In AVR targets, RAM is mapped to many registers and peripherals - we don't want to block GDB from
* accessing them.
*/
const auto memoryStartAddress = (this->memoryType == Targets::TargetMemoryType::RAM)
? 0x00
: memoryDescriptor.addressRange.startAddress;
if (
this->startAddress < memoryStartAddress
|| (this->startAddress + (this->buffer.size() - 1)) > memoryDescriptor.addressRange.endAddress
) {
throw Exception(
"GDB requested access to memory which is outside the target's memory range"
);
}
targetControllerService.writeMemory(
this->memoryType,
const auto addressRange = Targets::TargetMemoryAddressRange{
this->startAddress,
this->buffer
this->startAddress + static_cast<Targets::TargetMemorySize>(this->buffer.size()) - 1
};
const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
addressRange
);
debugSession.connection.writePacket(OkResponsePacket());
auto accessibleBytes = Targets::TargetMemorySize{0};
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
if (!memorySegmentDescriptor->debugModeAccess.writeable) {
throw Exception{
"Attempted to access restricted memory segment (" + memorySegmentDescriptor->key
+ ") - segment not writeable in debug mode"
};
}
accessibleBytes += memorySegmentDescriptor->addressRange.intersectingSize(addressRange);
}
if (accessibleBytes < this->bytes) {
throw Exception{"GDB requested access to memory which does not reside within any memory segment"};
}
{
const auto atomicSession = targetControllerService.makeAtomicSession();
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
const auto segmentStartAddress = std::max(
this->startAddress,
memorySegmentDescriptor->addressRange.startAddress
);
const auto bufferOffsetIt = buffer.begin() + (segmentStartAddress - this->startAddress);
targetControllerService.writeMemory(
this->addressSpaceDescriptor,
*memorySegmentDescriptor,
segmentStartAddress,
Targets::TargetMemoryBuffer{
bufferOffsetIt,
bufferOffsetIt + memorySegmentDescriptor->addressRange.intersectingSize(addressRange)
}
);
}
}
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to write memory to target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
WriteMemory::PacketData WriteMemory::extractPacketData(const RawPacket& rawPacket) {
using Services::StringService;
if (rawPacket.size() < 8) {
throw Exception{"Invalid packet length"};
}
const auto command = std::string{rawPacket.begin() + 2, rawPacket.end() - 3};
const auto commaDelimiterPos = command.find_first_of(',');
const auto colonDelimiterPos = command.find_first_of(':');
if (commaDelimiterPos == std::string::npos || colonDelimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
return {
StringService::toUint32(command.substr(0, commaDelimiterPos), 16),
StringService::toUint32(
command.substr(commaDelimiterPos + 1, colonDelimiterPos - (commaDelimiterPos + 1)),
16
),
StringService::dataFromHex(command.substr(colonDelimiterPos + 1))
};
}
WriteMemory::WriteMemory(
const RawPacket& rawPacket,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
PacketData&& packetData
)
: CommandPacket(rawPacket)
, addressSpaceDescriptor(gdbTargetDescriptor.addressSpaceDescriptorFromGdbAddress(packetData.gdbStartAddress))
, startAddress(gdbTargetDescriptor.translateGdbAddress(packetData.gdbStartAddress))
, bytes(packetData.bytes)
, buffer(std::move(packetData.buffer))
{
if (this->buffer.size() != this->bytes) {
throw Exception{"Buffer size does not match length value given in write memory packet"};
}
}
}

View File

@@ -4,9 +4,10 @@
#include <optional>
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
@@ -17,26 +18,29 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
class WriteMemory: public Gdb::CommandPackets::CommandPacket
{
public:
/**
* Start address of the memory operation.
*/
Targets::TargetMemoryAddress startAddress = 0;
/**
* The type of memory to read from.
*/
Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH;
/**
* Data to write.
*/
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemorySize bytes;
Targets::TargetMemoryBuffer buffer;
explicit WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
explicit WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
private:
struct PacketData
{
std::uint32_t gdbStartAddress;
std::uint32_t bytes;
Targets::TargetMemoryBuffer buffer;
};
static PacketData extractPacketData(const RawPacket& rawPacket);
WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor, PacketData&& packetData);
};
}

View File

@@ -3,9 +3,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -13,9 +11,6 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
@@ -25,87 +20,88 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
WriteRegister::WriteRegister(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
using Services::StringService;
if (this->data.size() < 4) {
throw Exception{"Invalid WriteRegister command packet - insufficient data in packet."};
}
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
auto command = std::string{this->data.begin() + 1, this->data.end()};
if (packet.size() < 4) {
throw Exception("Invalid WriteRegister command packet - insufficient data in packet.");
const auto delimiterPos = command.find_first_of('=');
if (delimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
if (packet.find('=') == std::string::npos) {
throw Exception("Invalid WriteRegister command packet - unexpected format");
}
const auto packetSegments = QString::fromStdString(packet).split("=");
this->registerId = static_cast<GdbRegisterId>(packetSegments.front().mid(1).toUInt(nullptr, 16));
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
this->registerId = static_cast<GdbRegisterId>(StringService::toUint32(command.substr(0, delimiterPos), 16));
this->registerValue = Services::StringService::dataFromHex(command.substr(delimiterPos + 1));
if (this->registerValue.empty()) {
throw Exception("Invalid WriteRegister command packet - missing register value");
throw Exception{"Invalid WriteRegister command packet - missing register value"};
}
// LSB to MSB
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
void WriteRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
void WriteRegister::handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling WriteRegister packet");
try {
if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID) {
if (this->registerValue.size() != 4) {
throw Exception{"Invalid PC value register size"};
}
targetControllerService.setProgramCounter(
static_cast<Targets::TargetMemoryAddress>(
(this->registerValue.size() >= 1 ? this->registerValue[0] : 0x00) << 24
| (this->registerValue.size() >= 2 ? this->registerValue[1] : 0x00) << 16
| (this->registerValue.size() >= 3 ? this->registerValue[2] : 0x00) << 8
| (this->registerValue.size() >= 4 ? this->registerValue[3] : 0x00)
this->registerValue[0] << 24
| this->registerValue[1] << 16
| this->registerValue[2] << 8
| this->registerValue[3]
)
);
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
return;
}
const auto& gdbTargetDescriptor = debugSession.gdbTargetDescriptor;
const auto descriptorId = gdbTargetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) {
if (this->registerValue.size() != 2) {
throw Exception{"Invalid SP register value size"};
}
targetControllerService.setStackPointer(
static_cast<Targets::TargetStackPointer>(this->registerValue[0] << 8 | this->registerValue[1])
);
debugSession.connection.writePacket(OkResponsePacket{});
return;
}
const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId);
const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find(
this->registerId
);
if (!descriptorId.has_value()) {
throw Exception("Invalid/unknown register");
if (
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
) {
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
}
const auto& descriptor = gdbTargetDescriptor.targetDescriptor.registerDescriptorsById.at(*descriptorId);
if (this->registerValue.size() > descriptor.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 >= descriptor.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() > descriptor.size) {
const auto& gdbRegisterDescriptor = gdbTargetDescriptor.gdbRegisterDescriptorsById.at(
this->registerId
);
throw Exception(
"Cannot set value for " + gdbRegisterDescriptor.name + " - value size exceeds register size."
);
}
}
targetControllerService.writeRegisters({
TargetRegister(descriptor.id, this->registerValue)
});
debugSession.connection.writePacket(OkResponsePacket());
targetControllerService.writeRegister(*(targetRegisterDescriptorIt->second), this->registerValue);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -3,6 +3,7 @@
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
@@ -21,6 +22,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
void handle(
Gdb::DebugSession& debugSession,
const Gdb::TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

@@ -5,14 +5,8 @@ namespace DebugServer::Gdb::AvrGdb
DebugSession::DebugSession(
Connection&& connection,
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
const TargetDescriptor& targetDescriptor,
const GdbDebugServerConfig& serverConfig
)
: Gdb::DebugSession(
std::move(connection),
supportedFeatures,
targetDescriptor,
serverConfig
)
: Gdb::DebugSession(std::move(connection), supportedFeatures, serverConfig)
{}
}

View File

@@ -12,7 +12,6 @@ namespace DebugServer::Gdb::AvrGdb
DebugSession(
Connection&& connection,
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
const TargetDescriptor& targetDescriptor,
const GdbDebugServerConfig& serverConfig
);
};

View File

@@ -1,9 +1,6 @@
#include "TargetDescriptor.hpp"
#include <numeric>
#include "src/Exceptions/Exception.hpp"
#include "src/Logger/Logger.hpp"
namespace DebugServer::Gdb::AvrGdb
{
@@ -13,149 +10,110 @@ namespace DebugServer::Gdb::AvrGdb
using Exceptions::Exception;
TargetDescriptor::TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor)
: DebugServer::Gdb::TargetDescriptor(
targetDescriptor,
{
{Targets::TargetMemoryType::FLASH, 0},
{Targets::TargetMemoryType::RAM, 0x00800000U},
{Targets::TargetMemoryType::EEPROM, 0x00810000U},
},
{},
{},
{}
)
: programAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("prog"))
, eepromAddressSpaceDescriptor(targetDescriptor.getFirstAddressSpaceDescriptorContainingMemorySegment("internal_eeprom"))
, sramAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("data"))
, gpRegistersAddressSpaceDescriptor(targetDescriptor.getFirstAddressSpaceDescriptorContainingMemorySegment("gp_registers"))
, programMemorySegmentDescriptor(this->programAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_program_memory"))
, eepromMemorySegmentDescriptor(this->eepromAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_eeprom"))
, sramMemorySegmentDescriptor(this->sramAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_ram"))
, gpRegistersMemorySegmentDescriptor(this->gpRegistersAddressSpaceDescriptor.getMemorySegmentDescriptor("gp_registers"))
, cpuGpPeripheralDescriptor(targetDescriptor.getPeripheralDescriptor("cpu_gpr"))
, cpuGpRegisterGroupDescriptor(this->cpuGpPeripheralDescriptor.getRegisterGroupDescriptor("gpr"))
{
this->loadRegisterMappings();
}
void TargetDescriptor::loadRegisterMappings() {
const auto generalPurposeTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType(
TargetRegisterType::GENERAL_PURPOSE_REGISTER
);
const auto statusTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType(
TargetRegisterType::STATUS_REGISTER
);
const auto stackPointerTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType(
TargetRegisterType::STACK_POINTER
);
if (generalPurposeTargetRegisterDescriptorIds.size() != 32) {
throw Exception("Unexpected general purpose register count");
}
if (statusTargetRegisterDescriptorIds.empty()) {
throw Exception("Missing status register descriptor");
}
if (stackPointerTargetRegisterDescriptorIds.empty()) {
throw Exception("Missing stack pointer register descriptor");
}
/*
* For AVR targets, GDB defines 35 registers in total:
*
* - Register ID 0 through 31 are general purpose registers
* - Register ID 32 is the status register (SREG)
* - Register ID 33 is the stack pointer register
* - Register ID 34 is the program counter
*
* For AVR targets, we don't have a target register descriptor for the program counter, so we don't map that
* GDB register ID (34) to anything here. Instead, the register command packet handlers (ReadRegisters,
* WriteRegister, etc) will handle any operations involving that GDB register.
* - Register ID 33 is the stack pointer register (SP)
* - Register ID 34 is the program counter (PC)
*/
// General purpose registers
GdbRegisterId gdbRegisterId = 0;
for (const auto descriptorId : generalPurposeTargetRegisterDescriptorIds) {
auto gdbRegisterDescriptor = RegisterDescriptor(
gdbRegisterId,
1,
"General Purpose Register " + std::to_string(gdbRegisterId)
// Create the GDB register descriptors and populate the mappings for the general purpose registers (ID 0->31)
for (const auto& [key, descriptor] : this->cpuGpRegisterGroupDescriptor.registerDescriptorsByKey) {
if (descriptor.type != TargetRegisterType::GENERAL_PURPOSE_REGISTER) {
continue;
}
const auto gdbRegisterId = static_cast<GdbRegisterId>(
descriptor.startAddress - this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress
);
this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(descriptorId, gdbRegisterDescriptor.id);
this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(gdbRegisterDescriptor.id, descriptorId);
this->gdbRegisterDescriptorsById.emplace(gdbRegisterDescriptor.id, std::move(gdbRegisterDescriptor));
gdbRegisterId++;
this->gdbRegisterDescriptorsById.emplace(gdbRegisterId, RegisterDescriptor{gdbRegisterId, 1});
this->targetRegisterDescriptorsByGdbId.emplace(gdbRegisterId, &descriptor);
}
const auto& statusTargetRegisterDescriptor = this->targetDescriptor.registerDescriptorsById.at(
*(statusTargetRegisterDescriptorIds.begin())
);
auto statusGdbRegisterDescriptor = RegisterDescriptor(
this->gdbRegisterDescriptorsById.emplace(
TargetDescriptor::STATUS_GDB_REGISTER_ID,
1,
"Status Register"
RegisterDescriptor{TargetDescriptor::STATUS_GDB_REGISTER_ID, 1}
);
if (statusTargetRegisterDescriptor.size > statusGdbRegisterDescriptor.size) {
throw Exception("AVR8 status target register size exceeds the GDB register size.");
}
this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(
statusTargetRegisterDescriptor.id,
statusGdbRegisterDescriptor.id
);
this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(
statusGdbRegisterDescriptor.id,
statusTargetRegisterDescriptor.id
);
this->gdbRegisterDescriptorsById.emplace(
statusGdbRegisterDescriptor.id,
std::move(statusGdbRegisterDescriptor)
);
const auto& stackPointerTargetRegisterDescriptor = this->targetDescriptor.registerDescriptorsById.at(
*(stackPointerTargetRegisterDescriptorIds.begin())
);
auto stackPointerGdbRegisterDescriptor = RegisterDescriptor(
TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID,
2,
"Stack Pointer Register"
);
if (stackPointerTargetRegisterDescriptor.size > stackPointerGdbRegisterDescriptor.size) {
throw Exception("AVR8 stack pointer target register size exceeds the GDB register size.");
}
this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(
stackPointerTargetRegisterDescriptor.id,
stackPointerGdbRegisterDescriptor.id
);
this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(
stackPointerGdbRegisterDescriptor.id,
stackPointerTargetRegisterDescriptor.id
);
this->gdbRegisterDescriptorsById.emplace(
stackPointerGdbRegisterDescriptor.id,
std::move(stackPointerGdbRegisterDescriptor)
this->targetRegisterDescriptorsByGdbId.emplace(
TargetDescriptor::STATUS_GDB_REGISTER_ID,
&(targetDescriptor.getPeripheralDescriptor("cpu").getRegisterGroupDescriptor("cpu")
.getRegisterDescriptor("sreg"))
);
/*
* We acknowledge the GDB program counter register here, but we don't map it to any target register descriptors.
*
* This is because we can't access the program counter on AVR targets in the same way we do with other
* registers. We don't have a register descriptor for the program counter. We have to treat it as a special
* case in the register access command packet handlers. See CommandPackets::ReadRegister,
* We don't map the SP and PC GDB register IDs to target register descriptors because of inconsistencies.
* The register command handlers will deal with these registers separately. See CommandPackets::ReadRegister,
* CommandPackets::WriteRegister, etc for more.
*/
auto programCounterGdbRegisterDescriptor = RegisterDescriptor(
TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID,
4,
"Program Counter"
this->gdbRegisterDescriptorsById.emplace(
TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID,
RegisterDescriptor{TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID, 2}
);
this->gdbRegisterDescriptorsById.emplace(
programCounterGdbRegisterDescriptor.id,
std::move(programCounterGdbRegisterDescriptor)
TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID,
RegisterDescriptor{TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID, 4}
);
}
const Targets::TargetAddressSpaceDescriptor& TargetDescriptor::addressSpaceDescriptorFromGdbAddress(
GdbMemoryAddress address
) const {
if ((address & TargetDescriptor::EEPROM_ADDRESS_MASK) == TargetDescriptor::EEPROM_ADDRESS_MASK) {
return this->eepromAddressSpaceDescriptor;
}
if ((address & TargetDescriptor::SRAM_ADDRESS_MASK) == TargetDescriptor::SRAM_ADDRESS_MASK) {
return this->sramAddressSpaceDescriptor;
}
return this->programAddressSpaceDescriptor;
}
Targets::TargetMemoryAddress TargetDescriptor::translateGdbAddress(GdbMemoryAddress address) const {
if ((address & TargetDescriptor::EEPROM_ADDRESS_MASK) == TargetDescriptor::EEPROM_ADDRESS_MASK) {
// GDB sends EEPROM addresses in relative form - convert them to absolute form.
return this->eepromMemorySegmentDescriptor.addressRange.startAddress
+ (address & ~(TargetDescriptor::EEPROM_ADDRESS_MASK));
}
if ((address & TargetDescriptor::SRAM_ADDRESS_MASK) == TargetDescriptor::SRAM_ADDRESS_MASK) {
return address & ~(TargetDescriptor::SRAM_ADDRESS_MASK);
}
return address;
}
GdbMemoryAddress TargetDescriptor::translateTargetMemoryAddress(
Targets::TargetMemoryAddress address,
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
) const {
if (memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::FLASH) {
return address;
}
if (memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::EEPROM) {
// GDB expects EEPROM addresses in relative form
return (address - memorySegmentDescriptor.addressRange.startAddress)
| TargetDescriptor::EEPROM_ADDRESS_MASK;
}
// We assume everything else is SRAM
return address | TargetDescriptor::SRAM_ADDRESS_MASK;
}
}

View File

@@ -2,28 +2,48 @@
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
#include "src/Targets/TargetPeripheralDescriptor.hpp"
#include "src/Targets/TargetRegisterGroupDescriptor.hpp"
namespace DebugServer::Gdb::AvrGdb
{
class TargetDescriptor: public DebugServer::Gdb::TargetDescriptor
{
public:
static constexpr auto SRAM_ADDRESS_MASK = 0x00800000U;
static constexpr auto EEPROM_ADDRESS_MASK = 0x00810000U;
static constexpr auto STATUS_GDB_REGISTER_ID = 32;
static constexpr auto STACK_POINTER_GDB_REGISTER_ID = 33;
static constexpr auto PROGRAM_COUNTER_GDB_REGISTER_ID = 34;
const Targets::TargetAddressSpaceDescriptor& programAddressSpaceDescriptor;
const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor;
const Targets::TargetAddressSpaceDescriptor& sramAddressSpaceDescriptor;
const Targets::TargetAddressSpaceDescriptor& gpRegistersAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor;
const Targets::TargetMemorySegmentDescriptor& sramMemorySegmentDescriptor;
const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor;
const Targets::TargetPeripheralDescriptor& cpuGpPeripheralDescriptor;
const Targets::TargetRegisterGroupDescriptor& cpuGpRegisterGroupDescriptor;
explicit TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor);
private:
/**
* For AVR targets, avr-gdb defines 35 registers in total:
*
* Register number 0 through 31 are general purpose registers
* Register number 32 is the status register (SREG)
* Register number 33 is the stack pointer register
* Register number 34 is the program counter register
*
* This function will prepare the appropriate GDB register numbers and mappings.
*/
void loadRegisterMappings();
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptorFromGdbAddress(
GdbMemoryAddress address
) const override;
Targets::TargetMemoryAddress translateGdbAddress(GdbMemoryAddress address) const override;
GdbMemoryAddress translateTargetMemoryAddress(
Targets::TargetMemoryAddress address,
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
) const override;
};
}