Lots of tidying

- Removed generic `avr8` target
- Simplified AVR8 target construction
- Introduced register descriptor IDs
- Simplified GDB register mappings
- Simplified target interface contract
- Other bits of tidying
This commit is contained in:
Nav
2023-05-21 21:08:25 +01:00
parent 5f8242a87a
commit ba03833325
62 changed files with 1304 additions and 1577 deletions

View File

@@ -1,6 +1,9 @@
#include "AvrGdbRsp.hpp"
// Command packets
#include "CommandPackets/ReadRegister.hpp"
#include "CommandPackets/ReadRegisters.hpp"
#include "CommandPackets/WriteRegister.hpp"
#include "CommandPackets/ReadMemory.hpp"
#include "CommandPackets/WriteMemory.hpp"
#include "CommandPackets/ReadMemoryMap.hpp"
@@ -34,6 +37,9 @@ namespace Bloom::DebugServer::Gdb::AvrGdb
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;
@@ -42,6 +48,18 @@ namespace Bloom::DebugServer::Gdb::AvrGdb
using AvrGdb::CommandPackets::FlashDone;
if (rawPacket.size() >= 2) {
if (rawPacket[1] == 'p') {
return std::make_unique<ReadRegister>(rawPacket);
}
if (rawPacket[1] == 'g') {
return std::make_unique<ReadRegisters>(rawPacket);
}
if (rawPacket[1] == 'P') {
return std::make_unique<WriteRegister>(rawPacket);
}
if (rawPacket[1] == 'm') {
return std::make_unique<ReadMemory>(rawPacket, this->gdbTargetDescriptor.value());
}

View File

@@ -0,0 +1,96 @@
#include "ReadRegister.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"
namespace Bloom::DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
ReadRegister::ReadRegister(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
if (this->data.size() < 2) {
throw Exception("Invalid packet length");
}
this->registerId = static_cast<GdbRegisterId>(
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
);
}
void ReadRegister::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
Logger::info("Handling ReadRegister packet");
try {
Logger::debug("Reading GDB register ID: " + std::to_string(this->registerId));
if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_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 programCounter = targetControllerService.getProgramCounter();
debugSession.connection.writePacket(
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);
const auto targetRegisterDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
this->registerId
);
if (!targetRegisterDescriptorId.has_value()) {
throw Exception("GDB requested an invalid/unknown register");
}
auto registerValue = targetControllerService.readRegisters({*targetRegisterDescriptorId}).front().value;
// GDB expects register values to be in LSB.
std::reverse(registerValue.begin(), registerValue.end());
if (registerValue.size() < gdbRegisterDescriptor.size) {
/*
* The register on the target is smaller than the size expected by GDB.
*
* Insert the rest of the bytes.
*/
registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00);
}
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());
}
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
namespace Bloom::DebugServer::Gdb::AvrGdb::CommandPackets
{
/**
* The ReadRegister class implements a structure for the "p" command packet. In response to this packet, the server
* is expected to send register values for the requested register.
*/
class ReadRegister: public Gdb::CommandPackets::CommandPacket
{
public:
GdbRegisterId registerId;
explicit ReadRegister(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -1,6 +1,7 @@
#include "ReadRegisters.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Targets/TargetRegister.hpp"
@@ -9,12 +10,12 @@
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
namespace Bloom::DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using Targets::TargetRegisterDescriptorIds;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
@@ -23,63 +24,54 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
ReadRegisters::ReadRegisters(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
if (this->data.size() >= 2 && this->data.front() == 'p') {
// This command packet is requesting a specific register
this->registerNumber = static_cast<size_t>(
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
);
}
}
{}
void ReadRegisters::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
Logger::info("Handling ReadRegisters packet");
try {
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
auto descriptors = TargetRegisterDescriptors();
auto descriptorIds = TargetRegisterDescriptorIds();
if (this->registerNumber.has_value()) {
Logger::debug("Reading register number: " + std::to_string(this->registerNumber.value()));
descriptors.insert(
targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber.value())
// Read all target registers mapped to a GDB register
for (const auto& [gdbRegisterId, gdbRegisterDescriptor] : targetDescriptor.gdbRegisterDescriptorsById) {
const auto registerDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
gdbRegisterId
);
} else {
// Read all target registers mapped to a GDB register
for (const auto& registerNumber : targetDescriptor.getRegisterNumbers()) {
descriptors.insert(targetDescriptor.getTargetRegisterDescriptorFromNumber(registerNumber));
if (registerDescriptorId.has_value()) {
descriptorIds.insert(*registerDescriptorId);
}
}
auto registerSet = targetControllerService.readRegisters(descriptors);
auto registerSet = targetControllerService.readRegisters(descriptorIds);
/*
* Sort each register by their respective GDB register number - this will leave us with a collection of
* 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& registerA, const TargetRegister& registerB) {
return targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) <
targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor);
[this, &targetDescriptor] (const TargetRegister& regA, const TargetRegister& regB) {
return targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regA.descriptorId).value() <
targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regB.descriptorId).value();
}
);
/*
* Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure
* that each register value size matches the size in the associated GDB register descriptor, implode the
* values, convert to hexadecimal form and send to the GDB client.
* 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());
const auto gdbRegisterNumber = targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(
reg.descriptor
const auto gdbRegisterId = targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(
reg.descriptorId
).value();
const auto& gdbRegisterDescriptor = targetDescriptor.getRegisterDescriptorFromNumber(gdbRegisterNumber);
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);
@@ -88,12 +80,19 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
}
// Finally, include the program counter (which GDB expects to reside at the end)
const auto programCounter = targetControllerService.getProgramCounter();
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))
);
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
Logger::error("Failed to read registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <optional>
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
namespace Bloom::DebugServer::Gdb::AvrGdb::CommandPackets
{
/**
* The ReadRegisters class implements a structure for the "g" command packet. In response to this packet, the
* server is expected to send register values for all registers.
*/
class ReadRegisters: public Gdb::CommandPackets::CommandPacket
{
public:
explicit ReadRegisters(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,110 @@
#include "WriteRegister.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
WriteRegister::WriteRegister(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
if (packet.size() < 4) {
throw Exception("Invalid WriteRegister command packet - insufficient data in 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());
if (this->registerValue.empty()) {
throw Exception("Invalid WriteRegister command packet - missing register value");
}
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
void WriteRegister::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
Logger::info("Handling WriteRegister packet");
try {
if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID) {
targetControllerService.setProgramCounter(
static_cast<Targets::TargetProgramCounter>(
(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)
)
);
debugSession.connection.writePacket(OkResponsePacket());
return;
}
const auto& gdbTargetDescriptor = debugSession.gdbTargetDescriptor;
const auto descriptorId = gdbTargetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
this->registerId
);
if (!descriptorId.has_value()) {
throw Exception("Invalid/unknown register");
}
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());
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::DebugServer::Gdb::AvrGdb::CommandPackets
{
/**
* The WriteRegister class implements the structure for "P" packets.
*/
class WriteRegister: public Gdb::CommandPackets::CommandPacket
{
public:
GdbRegisterId registerId;
Targets::TargetMemoryBuffer registerValue;
explicit WriteRegister(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -19,164 +19,143 @@ namespace Bloom::DebugServer::Gdb::AvrGdb
{Targets::TargetMemoryType::FLASH, 0},
{Targets::TargetMemoryType::RAM, 0x00800000U},
{Targets::TargetMemoryType::EEPROM, 0x00810000U},
}
},
{},
{},
{}
)
{
this->loadRegisterMappings();
}
std::optional<GdbRegisterNumber> TargetDescriptor::getRegisterNumberFromTargetRegisterDescriptor(
const Targets::TargetRegisterDescriptor& registerDescriptor
) const {
return this->targetRegisterDescriptorsByGdbNumber.valueAt(registerDescriptor);
}
const RegisterDescriptor& TargetDescriptor::getRegisterDescriptorFromNumber(GdbRegisterNumber number) const {
const auto registerDescriptorIt = this->registerDescriptorsByGdbNumber.find(number);
if (registerDescriptorIt.has_value()) {
return (*registerDescriptorIt)->second;
}
throw Exception(
"Unknown register from GDB - register number (" + std::to_string(number)
+ ") not mapped to any GDB register descriptor."
);
}
const TargetRegisterDescriptor& TargetDescriptor::getTargetRegisterDescriptorFromNumber(
GdbRegisterNumber number
) const {
const auto targetRegisterDescriptorIt = this->targetRegisterDescriptorsByGdbNumber.find(number);
if (targetRegisterDescriptorIt.has_value()) {
return (*targetRegisterDescriptorIt)->second;
}
throw Exception(
"Unknown register from GDB - register number (" + std::to_string(number)
+ ") not mapped to any target register descriptor."
);
}
const std::vector<GdbRegisterNumber>& TargetDescriptor::getRegisterNumbers() const {
return this->registerNumbers;
}
void TargetDescriptor::loadRegisterMappings() {
const auto& registerDescriptorsByType = this->targetDescriptor.registerDescriptorsByType;
if (!registerDescriptorsByType.contains(TargetRegisterType::STATUS_REGISTER)) {
throw Exception("Missing status register descriptor");
}
if (!registerDescriptorsByType.contains(TargetRegisterType::STACK_POINTER)) {
throw Exception("Missing stack pointer register descriptor");
}
if (!registerDescriptorsByType.contains(TargetRegisterType::PROGRAM_COUNTER)) {
throw Exception("Missing program counter register descriptor");
}
if (
!registerDescriptorsByType.contains(TargetRegisterType::GENERAL_PURPOSE_REGISTER)
|| registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).size() != 32
) {
throw Exception("Unexpected general purpose register count");
}
/*
* 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
*/
// Generate 35 register numbers (0 -> 34)
std::iota(this->registerNumbers.begin(), this->registerNumbers.end(), 0);
/*
* Worth noting that gpRegisterDescriptors will always be sorted in the correct order, from register 0 to 31.
*
* Hmm, but the sorting is based on the start address (see TargetRegisterDescriptor::<() for more). So
* effectively, we're assuming that the registers will be laid out in the correct order, in memory. I think
* this assumption is fair.
*/
const auto& gpRegisterDescriptors = registerDescriptorsByType.at(
const auto generalPurposeTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType(
TargetRegisterType::GENERAL_PURPOSE_REGISTER
);
// General purpose registers
GdbRegisterNumber regNumber = 0;
for (const auto& descriptor : gpRegisterDescriptors) {
this->registerDescriptorsByGdbNumber.insert(std::pair(
regNumber,
RegisterDescriptor(
regNumber,
1,
"General Purpose Register " + std::to_string(regNumber)
)
));
const auto statusTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType(
TargetRegisterType::STATUS_REGISTER
);
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
regNumber,
descriptor
));
const auto stackPointerTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType(
TargetRegisterType::STACK_POINTER
);
regNumber++;
if (generalPurposeTargetRegisterDescriptorIds.size() != 32) {
throw Exception("Unexpected general purpose register count");
}
// Status, stack pointer and program counter registers
const auto statusDescriptor = RegisterDescriptor(
32,
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.
*/
// General purpose registers
GdbRegisterId gdbRegisterId = 0;
for (const auto descriptorId : generalPurposeTargetRegisterDescriptorIds) {
auto gdbRegisterDescriptor = RegisterDescriptor(
gdbRegisterId,
1,
"General Purpose Register " + std::to_string(gdbRegisterId)
);
this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(descriptorId, gdbRegisterDescriptor.id);
this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(gdbRegisterDescriptor.id, descriptorId);
this->gdbRegisterDescriptorsById.emplace(gdbRegisterDescriptor.id, std::move(gdbRegisterDescriptor));
gdbRegisterId++;
}
const auto& statusTargetRegisterDescriptor = this->targetDescriptor.registerDescriptorsById.at(
*(statusTargetRegisterDescriptorIds.begin())
);
auto statusGdbRegisterDescriptor = RegisterDescriptor(
TargetDescriptor::STATUS_GDB_REGISTER_ID,
1,
"Status Register"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(statusDescriptor.number, statusDescriptor));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
statusDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).begin())
));
if (statusTargetRegisterDescriptor.size > statusGdbRegisterDescriptor.size) {
throw Exception("AVR8 status target register size exceeds the GDB register size.");
}
const auto stackPointerDescriptor = RegisterDescriptor(
33,
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"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(stackPointerDescriptor.number, stackPointerDescriptor));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
stackPointerDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).begin())
));
if (stackPointerTargetRegisterDescriptor.size > stackPointerGdbRegisterDescriptor.size) {
throw Exception("AVR8 stack pointer target register size exceeds the GDB register size.");
}
const auto programCounterDescriptor = RegisterDescriptor(
34,
this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(
stackPointerTargetRegisterDescriptor.id,
stackPointerGdbRegisterDescriptor.id
);
this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(
stackPointerGdbRegisterDescriptor.id,
stackPointerTargetRegisterDescriptor.id
);
this->gdbRegisterDescriptorsById.emplace(
stackPointerGdbRegisterDescriptor.id,
std::move(stackPointerGdbRegisterDescriptor)
);
/*
* 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,
* CommandPackets::WriteRegister, etc for more.
*/
auto programCounterGdbRegisterDescriptor = RegisterDescriptor(
TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID,
4,
"Program Counter"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(
programCounterDescriptor.number,
programCounterDescriptor
));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
programCounterDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).begin())
));
if (registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).size() > statusDescriptor.size) {
throw Exception("AVR8 status target register size exceeds the GDB register size.");
}
if (registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).size() > stackPointerDescriptor.size) {
throw Exception("AVR8 stack pointer target register size exceeds the GDB register size.");
}
if (
registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).size() > programCounterDescriptor.size
) {
throw Exception("AVR8 program counter size exceeds the GDB register size.");
}
this->gdbRegisterDescriptorsById.emplace(
programCounterGdbRegisterDescriptor.id,
std::move(programCounterGdbRegisterDescriptor)
);
}
}

View File

@@ -2,52 +2,18 @@
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/Helpers/BiMap.hpp"
namespace Bloom::DebugServer::Gdb::AvrGdb
{
class TargetDescriptor: public DebugServer::Gdb::TargetDescriptor
{
public:
BiMap<GdbRegisterNumber, RegisterDescriptor> registerDescriptorsByGdbNumber = {};
BiMap<GdbRegisterNumber, Targets::TargetRegisterDescriptor> targetRegisterDescriptorsByGdbNumber = {};
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;
explicit TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor);
/**
* Should retrieve the GDB register number, given a target register descriptor. Or std::nullopt if the target
* register descriptor isn't mapped to any GDB register.
*
* @param registerDescriptor
* @return
*/
std::optional<GdbRegisterNumber> getRegisterNumberFromTargetRegisterDescriptor(
const Targets::TargetRegisterDescriptor& registerDescriptor
) const override;
/**
* Should retrieve the GDB register descriptor for a given GDB register number.
*
* @param number
* @return
*/
const RegisterDescriptor& getRegisterDescriptorFromNumber(GdbRegisterNumber number) const override;
/**
* Should retrieve the mapped target register descriptor for a given GDB register number.
*
* @param number
* @return
*/
const Targets::TargetRegisterDescriptor& getTargetRegisterDescriptorFromNumber(
GdbRegisterNumber number
) const override;
const std::vector<GdbRegisterNumber>& getRegisterNumbers() const override;
private:
std::vector<GdbRegisterNumber> registerNumbers = std::vector<GdbRegisterNumber>(35);
/**
* For AVR targets, avr-gdb defines 35 registers in total:
*

View File

@@ -142,40 +142,34 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
auto peripheralsByName = std::map<std::string, Peripheral>();
for (const auto& [registerType, registerDescriptors] : targetDescriptor.registerDescriptorsByType) {
if (registerDescriptors.empty()) {
for (const auto& [descriptorId, registerDescriptor] : targetDescriptor.registerDescriptorsById) {
if (
!registerDescriptor.startAddress.has_value()
|| !registerDescriptor.name.has_value()
|| registerDescriptor.name->empty()
|| !registerDescriptor.groupName.has_value()
|| (
registerDescriptor.type != Targets::TargetRegisterType::OTHER
&& registerDescriptor.type != Targets::TargetRegisterType::PORT_REGISTER
)
) {
continue;
}
for (const auto& registerDescriptor : registerDescriptors) {
if (
!registerDescriptor.startAddress.has_value()
|| !registerDescriptor.name.has_value()
|| registerDescriptor.name->empty()
|| !registerDescriptor.groupName.has_value()
|| (
registerDescriptor.type != Targets::TargetRegisterType::OTHER
&& registerDescriptor.type != Targets::TargetRegisterType::PORT_REGISTER
)
) {
continue;
}
auto peripheralIt = peripheralsByName.find(*registerDescriptor.groupName);
auto peripheralIt = peripheralsByName.find(*registerDescriptor.groupName);
if (peripheralIt == peripheralsByName.end()) {
auto peripheral = Peripheral{
.name = QString::fromStdString(
*registerDescriptor.groupName
).replace(QChar(' '), QChar('_')).toUpper(),
.baseAddress = baseAddressOffset
};
if (peripheralIt == peripheralsByName.end()) {
auto peripheral = Peripheral{
.name = QString::fromStdString(
*registerDescriptor.groupName
).replace(QChar(' '), QChar('_')).toUpper(),
.baseAddress = baseAddressOffset
};
peripheralIt = peripheralsByName.insert(std::pair(*registerDescriptor.groupName, peripheral)).first;
}
peripheralIt->second.registerDescriptors.insert(registerDescriptor);
peripheralIt = peripheralsByName.insert(std::pair(*registerDescriptor.groupName, peripheral)).first;
}
peripheralIt->second.registerDescriptors.insert(registerDescriptor);
}
auto peripheralsElement = document.createElement("peripherals");
@@ -213,7 +207,7 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
);
registerElement.appendChild(
createElement("access", registerDescriptor.writable ? "read-write" : "read-only")
createElement("access", registerDescriptor.access.writable ? "read-write" : "read-only")
);
registersElement.appendChild(registerElement);

View File

@@ -1,35 +0,0 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The ReadRegisters class implements a structure for "g" and "p" command packets. In response to these
* packets, the server is expected to send register values for all registers (for "g" packets) or for a single
* register (for "p" packets).
*/
class ReadRegisters: public CommandPacket
{
public:
/**
* "p" packets include a register number to indicate which register is requested for reading. When this is set,
* the server is expected to respond with only the value of the requested register.
*
* If the register number is not supplied (as is the case with "g" packets), the server is expected to respond
* with values for all registers.
*/
std::optional<GdbRegisterNumber> registerNumber;
explicit ReadRegisters(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -1,86 +0,0 @@
#include "WriteRegister.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
WriteRegister::WriteRegister(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
if (packet.size() < 4) {
throw Exception("Invalid P command packet - insufficient data in packet.");
}
if (packet.find('=') == std::string::npos) {
throw Exception("Invalid P command packet - unexpected format");
}
auto packetSegments = QString::fromStdString(packet).split("=");
this->registerNumber = static_cast<int>(packetSegments.front().mid(1).toUInt(nullptr, 16));
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
void WriteRegister::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
Logger::info("Handling WriteRegister packet");
try {
auto targetRegisterDescriptor = debugSession.gdbTargetDescriptor.getTargetRegisterDescriptorFromNumber(
this->registerNumber
);
const auto valueSize = this->registerValue.size();
if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) {
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
for (auto i = this->registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) {
if (this->registerValue.at(i) != 0x00) {
// If we reach a non-zero byte, we cannot trim anymore without changing the data
break;
}
this->registerValue.erase(this->registerValue.begin() + i);
}
if (this->registerValue.size() > targetRegisterDescriptor.size) {
const auto& gdbRegisterDescriptor = debugSession.gdbTargetDescriptor.getRegisterDescriptorFromNumber(
this->registerNumber
);
throw Exception("Cannot set value for " + gdbRegisterDescriptor.name
+ " - value size exceeds register size."
);
}
}
targetControllerService.writeRegisters({
TargetRegister(targetRegisterDescriptor, this->registerValue)
});
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -1,27 +0,0 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
#include "src/Targets/TargetRegister.hpp"
namespace Bloom::DebugServer::Gdb::CommandPackets
{
/**
* The WriteRegisters class implements the structure for "P" packets. Upon receiving this packet,
* server is expected to update a register value to the target.
*/
class WriteRegister: public CommandPacket
{
public:
int registerNumber = 0;
std::vector<unsigned char> registerValue;
explicit WriteRegister(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -21,8 +21,6 @@
#include "CommandPackets/InterruptExecution.hpp"
#include "CommandPackets/ContinueExecution.hpp"
#include "CommandPackets/StepExecution.hpp"
#include "CommandPackets/ReadRegisters.hpp"
#include "CommandPackets/WriteRegister.hpp"
#include "CommandPackets/SetBreakpoint.hpp"
#include "CommandPackets/RemoveBreakpoint.hpp"
#include "CommandPackets/Monitor.hpp"
@@ -280,14 +278,6 @@ namespace Bloom::DebugServer::Gdb
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
}
if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
}
if (rawPacketString[1] == 'P') {
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
}
if (rawPacketString[1] == 'c') {
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
}

View File

@@ -5,27 +5,28 @@
namespace Bloom::DebugServer::Gdb
{
using GdbRegisterNumber = int;
using GdbRegisterId = std::uint16_t;
/*
* GDB defines a set of registers for each target architecture.
*
* Each register in the set is assigned a register number, which is used to identify the register.
* Each register in the set is assigned an ID, which is used to identify the registers. Although the mapping of
* registers to IDs is hardcoded in GDB, GDB server implementations are expected to be aware of this mapping.
*/
struct RegisterDescriptor
{
GdbRegisterNumber number;
GdbRegisterId id;
std::uint16_t size;
std::string name;
RegisterDescriptor(GdbRegisterNumber number, std::uint16_t size, const std::string& name)
: number(number)
RegisterDescriptor(GdbRegisterId id, std::uint16_t size, const std::string& name)
: id(id)
, size(size)
, name(name)
{};
bool operator == (const RegisterDescriptor& other) const {
return this->number == other.number;
return this->id == other.id;
}
bool operator != (const RegisterDescriptor& other) const {
@@ -33,7 +34,7 @@ namespace Bloom::DebugServer::Gdb
}
bool operator < (const RegisterDescriptor& rhs) const {
return this->number < rhs.number;
return this->id < rhs.id;
}
bool operator > (const RegisterDescriptor& rhs) const {
@@ -64,7 +65,7 @@ namespace std
public:
std::size_t operator () (const Bloom::DebugServer::Gdb::RegisterDescriptor& descriptor) const {
// We use the GDB register number as the hash, as it is unique to the register.
return static_cast<size_t>(descriptor.number);
return static_cast<size_t>(descriptor.id);
}
};
}

View File

@@ -0,0 +1,64 @@
#include "TargetDescriptor.hpp"
namespace Bloom::DebugServer::Gdb
{
TargetDescriptor::TargetDescriptor(
const Targets::TargetDescriptor& targetDescriptor,
const BiMap<Targets::TargetMemoryType, std::uint32_t>& memoryOffsetsByType,
std::map<GdbRegisterId, RegisterDescriptor> gdbRegisterDescriptorsById,
std::map<Targets::TargetRegisterDescriptorId, GdbRegisterId> gdbRegisterIdsByTargetRegisterDescriptorId,
std::map<GdbRegisterId, Targets::TargetRegisterDescriptorId> targetRegisterDescriptorIdsByGdbRegisterId
)
: targetDescriptor(targetDescriptor)
, memoryOffsetsByType(memoryOffsetsByType)
, memoryOffsets(memoryOffsetsByType.getValues())
, gdbRegisterDescriptorsById(gdbRegisterDescriptorsById)
, gdbRegisterIdsByTargetRegisterDescriptorId(gdbRegisterIdsByTargetRegisterDescriptorId)
, targetRegisterDescriptorIdsByGdbRegisterId(targetRegisterDescriptorIdsByGdbRegisterId)
{}
std::uint32_t TargetDescriptor::getMemoryOffset(Targets::TargetMemoryType memoryType) const {
return this->memoryOffsetsByType.valueAt(memoryType).value_or(0);
}
Targets::TargetMemoryType TargetDescriptor::getMemoryTypeFromGdbAddress(std::uint32_t address) const {
// Start with the largest offset until we find a match
for (
auto memoryOffsetIt = this->memoryOffsets.rbegin();
memoryOffsetIt != this->memoryOffsets.rend();
++memoryOffsetIt
) {
if ((address & *memoryOffsetIt) == *memoryOffsetIt) {
return this->memoryOffsetsByType.at(*memoryOffsetIt);
}
}
return Targets::TargetMemoryType::FLASH;
}
std::optional<GdbRegisterId> TargetDescriptor::getGdbRegisterIdFromTargetRegisterDescriptorId(
Targets::TargetRegisterDescriptorId targetRegisterDescriptorId
) const {
const auto gdbRegisterIdIt = this->gdbRegisterIdsByTargetRegisterDescriptorId.find(
targetRegisterDescriptorId
);
if (gdbRegisterIdIt != this->gdbRegisterIdsByTargetRegisterDescriptorId.end()) {
return gdbRegisterIdIt->second;
}
return std::nullopt;
}
std::optional<Targets::TargetRegisterDescriptorId> TargetDescriptor::getTargetRegisterDescriptorIdFromGdbRegisterId(
GdbRegisterId gdbRegisterId
) const {
const auto registerDescriptorIdIt = this->targetRegisterDescriptorIdsByGdbRegisterId.find(gdbRegisterId);
if (registerDescriptorIdIt != this->targetRegisterDescriptorIdsByGdbRegisterId.end()) {
return registerDescriptorIdIt->second;
}
return std::nullopt;
}
}

View File

@@ -4,6 +4,7 @@
#include <optional>
#include <vector>
#include <set>
#include <map>
#include "src/Helpers/BiMap.hpp"
#include "src/Targets/TargetDescriptor.hpp"
@@ -21,21 +22,19 @@ namespace Bloom::DebugServer::Gdb
{
public:
Targets::TargetDescriptor targetDescriptor;
std::map<GdbRegisterId, RegisterDescriptor> gdbRegisterDescriptorsById;
explicit TargetDescriptor(
const Targets::TargetDescriptor& targetDescriptor,
const BiMap<Targets::TargetMemoryType, std::uint32_t>& memoryOffsetsByType
)
: targetDescriptor(targetDescriptor)
, memoryOffsetsByType(memoryOffsetsByType)
, memoryOffsets(memoryOffsetsByType.getValues())
{}
const BiMap<Targets::TargetMemoryType, std::uint32_t>& memoryOffsetsByType,
std::map<GdbRegisterId, RegisterDescriptor> gdbRegisterDescriptorsById,
std::map<Targets::TargetRegisterDescriptorId, GdbRegisterId> gdbRegisterIdsByTargetRegisterDescriptorId,
std::map<GdbRegisterId, Targets::TargetRegisterDescriptorId> targetRegisterDescriptorIdsByGdbRegisterId
);
virtual ~TargetDescriptor() = default;
virtual std::uint32_t getMemoryOffset(Targets::TargetMemoryType memoryType) const {
return this->memoryOffsetsByType.valueAt(memoryType).value_or(0);
}
std::uint32_t getMemoryOffset(Targets::TargetMemoryType memoryType) const;
/**
* Helper method to extract the target memory type (Flash, RAM, etc) from a GDB memory address.
@@ -43,58 +42,35 @@ namespace Bloom::DebugServer::Gdb
* @param address
* @return
*/
Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) const {
// Start with the largest offset until we find a match
for (
auto memoryOffsetIt = this->memoryOffsets.rbegin();
memoryOffsetIt != this->memoryOffsets.rend();
++memoryOffsetIt
) {
if ((address & *memoryOffsetIt) == *memoryOffsetIt) {
return this->memoryOffsetsByType.at(*memoryOffsetIt);
}
}
return Targets::TargetMemoryType::FLASH;
}
Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) const;
/**
* Should retrieve the GDB register number, given a target register descriptor. Or std::nullopt if the target
* register descriptor isn't mapped to any GDB register.
* Should retrieve the GDB register ID, given a target register descriptor ID. Or std::nullopt if the
* target register descriptor ID isn't mapped to any GDB register.
*
* @param registerDescriptor
* @param registerDescriptorId
* @return
*/
virtual std::optional<GdbRegisterNumber> getRegisterNumberFromTargetRegisterDescriptor(
const Targets::TargetRegisterDescriptor& registerDescriptor
) const = 0;
std::optional<GdbRegisterId> getGdbRegisterIdFromTargetRegisterDescriptorId(
Targets::TargetRegisterDescriptorId targetRegisterDescriptorId
) const;
/**
* Should retrieve the GDB register descriptor for a given GDB register number.
* Should retrieve the mapped target register descriptor ID for a given GDB register ID.
*
* @param number
* This function may return std::nullopt if the GDB register ID maps to something that isn't considered a
* register on our end. For example, for AVR targets, the GDB register ID 34 maps to the program counter. But
* the program counter is not treated like any other register in Bloom (there's no TargetRegisterDescriptor for
* it). So in that case, the GDB register ID is not mapped to any target register descriptor ID.
*
* @param gdbRegisterId
* @return
*/
virtual const RegisterDescriptor& getRegisterDescriptorFromNumber(GdbRegisterNumber number) const = 0;
std::optional<Targets::TargetRegisterDescriptorId> getTargetRegisterDescriptorIdFromGdbRegisterId(
GdbRegisterId gdbRegisterId
) const;
/**
* Should retrieve the mapped target register descriptor for a given GDB register number.
*
* @param number
* @return
*/
virtual const Targets::TargetRegisterDescriptor& getTargetRegisterDescriptorFromNumber(
GdbRegisterNumber number
) const = 0;
/**
* Should return all allocated GDB register numbers for the target.
*
* @return
*/
virtual const std::vector<GdbRegisterNumber>& getRegisterNumbers() const = 0;
private:
protected:
/**
* When GDB sends us a memory address, the memory type (Flash, RAM, EEPROM, etc) is embedded within. This is
* done by ORing the address with some constant. For example, for AVR targets, RAM addresses are ORed with
@@ -109,5 +85,8 @@ namespace Bloom::DebugServer::Gdb
* Sorted set of the known memory offsets (see memoryOffsetsByType).
*/
std::set<std::uint32_t> memoryOffsets;
std::map<Targets::TargetRegisterDescriptorId, GdbRegisterId> gdbRegisterIdsByTargetRegisterDescriptorId;
std::map<GdbRegisterId, Targets::TargetRegisterDescriptorId> targetRegisterDescriptorIdsByGdbRegisterId;
};
}