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:
@@ -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());
|
||||
}
|
||||
|
||||
96
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp
Normal file
96
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.hpp
Normal file
25
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
25
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.hpp
Normal file
25
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
110
src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp
Normal file
110
src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.hpp
Normal file
28
src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
64
src/DebugServer/Gdb/TargetDescriptor.cpp
Normal file
64
src/DebugServer/Gdb/TargetDescriptor.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user