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;
|
||||
};
|
||||
}
|
||||
99
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp
Normal file
99
src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
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:
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user