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

@@ -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

@@ -0,0 +1,99 @@
#include "ReadRegisters.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::TargetRegisterDescriptorIds;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
ReadRegisters::ReadRegisters(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{}
void ReadRegisters::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
Logger::info("Handling ReadRegisters packet");
try {
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
auto descriptorIds = TargetRegisterDescriptorIds();
// 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 registerSet = targetControllerService.readRegisters(descriptorIds);
/*
* 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();
}
);
/*
* 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 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);
}
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 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;
};
}