Massive refactor to accommodate RISC-V targets

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

View File

@@ -8,16 +8,18 @@ class AddressSpace
public ?string $key = null;
public ?int $startAddress = null;
public ?int $size = null;
public ?int $unitSize = null;
public ?string $endianness = null;
/** @var MemorySegment[] */
public array $memorySegments = [];
public function __construct(?string $key, ?int $startAddress, ?int $size, ?string $endianness)
public function __construct(?string $key, ?int $startAddress, ?int $size, ?int $unitSize, ?string $endianness)
{
$this->key = $key;
$this->startAddress = $startAddress;
$this->size = $size;
$this->unitSize = $unitSize;
$this->endianness = $endianness;
}

View File

@@ -93,10 +93,7 @@ class MemorySegment
&& $endAddress !== null
&& $other->startAddress !== null
&& $otherEndAddress !== null
&& (
($other->startAddress <= $this->startAddress && $otherEndAddress >= $this->startAddress)
|| ($other->startAddress >= $this->startAddress && $other->startAddress <= $endAddress)
)
&& $this->startAddress <= $otherEndAddress && $other->startAddress <= $endAddress
;
}

View File

@@ -323,6 +323,7 @@ class AtdfService
isset($attributes['id']) ? strtolower($attributes['id']) : null,
$this->stringService->tryStringToInt($attributes['start'] ?? null),
$this->stringService->tryStringToInt($attributes['size'] ?? null),
null,
$attributes['endianness'] ?? null,
);

View File

@@ -606,7 +606,7 @@ class ValidationService
if ($register->size === null) {
$failures[] = 'Missing size';
} elseif ($register->size < 1) {
} elseif ($register->size < 1 || $register->size > 8) {
$failures[] = 'Invalid size (' . $register->size . ')';
}

View File

@@ -116,6 +116,7 @@ class FromXmlService
$attributes['key'] ?? null,
$this->stringService->tryStringToInt($attributes['start'] ?? null),
$this->stringService->tryStringToInt($attributes['size'] ?? null),
$this->stringService->tryStringToInt($attributes['unit-size'] ?? null),
$attributes['endianness'] ?? null,
);

View File

@@ -70,6 +70,10 @@ class ToXmlService
$element->setAttribute('start', $this->stringService->tryIntToHex($addressSpace->startAddress, 8));
$element->setAttribute('size', $addressSpace->size);
if (!empty($addressSpace->unitSize)) {
$element->setAttribute('unit-size', $addressSpace->unitSize);
}
if (!empty($addressSpace->endianness)) {
$element->setAttribute('endianness', strtolower($addressSpace->endianness));
}

View File

@@ -19,8 +19,6 @@
#include "src/Services/ProcessService.hpp"
#include "src/Helpers/BiMap.hpp"
#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp"
#include "src/Exceptions/InvalidConfig.hpp"
using namespace Exceptions;
@@ -75,12 +73,11 @@ int Application::run() {
this->checkBloomVersion();
/*
* We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to
* process our events. To address this, we use a QTimer to dispatch our events on an interval.
* We use a QTimer to dispatch our events on an interval.
*
* This allows us to use Qt's event loop whilst still being able to process our own events.
*/
auto* eventDispatchTimer = new QTimer(this->qtApplication.get());
auto* eventDispatchTimer = new QTimer{this->qtApplication.get()};
QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Application::dispatchEvents);
eventDispatchTimer->start(100);
@@ -98,7 +95,7 @@ int Application::run() {
}
std::map<std::string, std::function<int()>> Application::getCommandHandlersByCommandName() {
return std::map<std::string, std::function<int()>> {
return std::map<std::string, std::function<int()>>{
{
"--help",
std::bind(&Application::presentHelpText, this)
@@ -225,17 +222,15 @@ void Application::triggerShutdown() {
void Application::loadProjectSettings() {
const auto projectSettingsPath = Services::PathService::projectSettingsPath();
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
auto jsonSettingsFile = QFile{QString::fromStdString(projectSettingsPath)};
if (jsonSettingsFile.exists()) {
try {
if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw Exception("Failed to open settings file.");
throw Exception{"Failed to open settings file."};
}
this->projectSettings = ProjectSettings(
QJsonDocument::fromJson(jsonSettingsFile.readAll()).object()
);
this->projectSettings = ProjectSettings{QJsonDocument::fromJson(jsonSettingsFile.readAll()).object()};
jsonSettingsFile.close();
return;
@@ -256,45 +251,43 @@ void Application::saveProjectSettings() {
}
const auto projectSettingsPath = Services::PathService::projectSettingsPath();
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
auto jsonSettingsFile = QFile{QString::fromStdString(projectSettingsPath)};
Logger::debug("Saving project settings to " + projectSettingsPath);
QDir().mkpath(QString::fromStdString(Services::PathService::projectSettingsDirPath()));
QDir{}.mkpath(QString::fromStdString(Services::PathService::projectSettingsDirPath()));
try {
const auto jsonDocument = QJsonDocument(this->projectSettings->toJson());
const auto jsonDocument = QJsonDocument{this->projectSettings->toJson()};
if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
throw Exception(
throw Exception{
"Failed to open/create settings file (" + projectSettingsPath + "). Check file permissions."
);
};
}
jsonSettingsFile.write(jsonDocument.toJson());
jsonSettingsFile.close();
} catch (const Exception& exception) {
Logger::error(
"Failed to save project settings - " + exception.getMessage()
);
Logger::error("Failed to save project settings - " + exception.getMessage());
}
}
void Application::loadProjectConfiguration() {
auto configFile = QFile(QString::fromStdString(Services::PathService::projectConfigPath()));
auto configFile = QFile{QString::fromStdString(Services::PathService::projectConfigPath())};
if (!configFile.exists()) {
throw Exception(
throw Exception{
"Bloom configuration file (bloom.yaml) not found. Working directory: "
+ Services::PathService::projectDirPath()
);
};
}
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw InvalidConfig(
throw InvalidConfig{
"Failed to open Bloom configuration file. Working directory: " + Services::PathService::projectDirPath()
);
};
}
try {
@@ -310,9 +303,9 @@ void Application::loadProjectConfiguration() {
// Validate the selected environment
const auto selectedEnvironmentIt = this->projectConfig->environments.find(this->selectedEnvironmentName);
if (selectedEnvironmentIt == this->projectConfig->environments.end()) {
throw InvalidConfig(
throw InvalidConfig{
"Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration."
);
};
}
this->environmentConfig = selectedEnvironmentIt->second;
@@ -328,7 +321,7 @@ void Application::loadProjectConfiguration() {
this->debugServerConfig = this->projectConfig->debugServerConfig.value();
} else {
throw InvalidConfig("Debug server configuration missing.");
throw InvalidConfig{"Debug server configuration missing."};
}
}
@@ -340,18 +333,20 @@ int Application::presentHelpText() {
Logger::silence();
// The file help.txt is included in Bloom's binary, as a resource. See the root-level CMakeLists.txt for more.
auto helpFile = QFile(QString::fromStdString(Services::PathService::compiledResourcesPath() + "/resources/help.txt"));
auto helpFile = QFile{
QString::fromStdString(Services::PathService::compiledResourcesPath() + "/resources/help.txt")
};
if (!helpFile.open(QIODevice::ReadOnly)) {
// This should never happen - if it does, something has gone very wrong
throw Exception(
throw Exception{
"Failed to open help file - please report this issue at " + Services::PathService::homeDomainName()
+ "/report-issue"
);
};
}
std::cout << "Bloom v" << Application::VERSION.toString() << "\n";
std::cout << QTextStream(&helpFile).readAll().toUtf8().constData() << "\n";
std::cout << QTextStream{&helpFile}.readAll().toUtf8().constData() << "\n";
return EXIT_SUCCESS;
}
@@ -376,14 +371,14 @@ int Application::presentVersionText() {
int Application::presentVersionMachineText() {
Logger::silence();
std::cout << QJsonDocument(QJsonObject({
std::cout << QJsonDocument{QJsonObject{
{"version", QString::fromStdString(Application::VERSION.toString())},
{"components", QJsonObject({
{"major", Application::VERSION.major},
{"minor", Application::VERSION.minor},
{"patch", Application::VERSION.patch},
})},
})).toJson().toStdString();
}}.toJson().toStdString();
return EXIT_SUCCESS;
}
@@ -393,38 +388,38 @@ int Application::presentCapabilitiesMachine() {
Logger::silence();
static const auto targetFamilyNames = BiMap<TargetFamily, QString>({
static const auto targetFamilyNames = BiMap<TargetFamily, QString>{
{TargetFamily::AVR_8, "AVR8"},
{TargetFamily::RISC_V, "RISC-V"},
});
};
auto supportedTargets = QJsonArray();
auto supportedTargets = QJsonArray{};
for (const auto& [configValue, descriptor] : Services::TargetService::briefDescriptorsByConfigValue()) {
supportedTargets.push_back(QJsonObject({
supportedTargets.push_back(QJsonObject{
{"name" , QString::fromStdString(descriptor.name)},
{"family" , targetFamilyNames.at(descriptor.family)},
{"configurationValue" , QString::fromStdString(configValue)},
}));
});
}
std::cout << QJsonDocument(QJsonObject({
std::cout << QJsonDocument{QJsonObject{
{"targets", supportedTargets},
#ifndef EXCLUDE_INSIGHT
{"insight", true},
#else
{"insight", false},
#endif
})).toJson().toStdString();
}}.toJson().toStdString();
return EXIT_SUCCESS;
}
int Application::initProject() {
auto configFile = QFile(QString::fromStdString(Services::PathService::projectConfigPath()));
auto configFile = QFile{QString::fromStdString(Services::PathService::projectConfigPath())};
if (configFile.exists()) {
throw Exception("Bloom configuration file (bloom.yaml) already exists in working directory.");
throw Exception{"Bloom configuration file (bloom.yaml) already exists in working directory."};
}
/*
@@ -433,19 +428,19 @@ int Application::initProject() {
*
* We simply copy the template file into the user's working directory.
*/
auto templateConfigFile = QFile(
auto templateConfigFile = QFile{
QString::fromStdString(Services::PathService::compiledResourcesPath()+ "/resources/bloom.template.yaml")
);
};
if (!templateConfigFile.open(QIODevice::ReadOnly)) {
throw Exception(
throw Exception{
"Failed to open template configuration file - please report this issue at "
+ Services::PathService::homeDomainName() + "/report-issue"
);
};
}
if (!configFile.open(QIODevice::ReadWrite)) {
throw Exception("Failed to create Bloom configuration file (bloom.yaml)");
throw Exception{"Failed to create Bloom configuration file (bloom.yaml)"};
}
configFile.write(templateConfigFile.readAll());
@@ -471,7 +466,7 @@ void Application::stopSignalHandler() {
* Send meaningless signal to the SignalHandler thread to have it shutdown. The signal will pull it out of
* a blocking state and allow it to action the shutdown. See SignalHandler::run() for more.
*/
pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1);
::pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1);
}
if (this->signalHandlerThread.joinable()) {
@@ -497,7 +492,7 @@ void Application::startTargetController() {
>();
if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) {
throw Exception("TargetController failed to start up");
throw Exception{"TargetController failed to start up"};
}
}
@@ -510,7 +505,7 @@ void Application::stopTargetController() {
if (tcThreadState == ThreadState::STARTING || tcThreadState == ThreadState::READY) {
EventManager::triggerEvent(std::make_shared<Events::ShutdownTargetController>());
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>(
std::chrono::milliseconds(10000)
std::chrono::milliseconds{10000}
);
}
@@ -525,18 +520,14 @@ void Application::startDebugServer() {
this->debugServer = std::make_unique<DebugServer::DebugServerComponent>(
this->debugServerConfig.value()
);
this->debugServerThread = std::thread(
&DebugServer::DebugServerComponent::run,
this->debugServer.get()
);
this->debugServerThread = std::thread{&DebugServer::DebugServerComponent::run, this->debugServer.get()};
const auto dsStateChangeEvent = this->applicationEventListener->waitForEvent<
Events::DebugServerThreadStateChanged
>();
if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) {
throw Exception("DebugServer failed to start up");
throw Exception{"DebugServer failed to start up"};
}
}
@@ -549,7 +540,7 @@ void Application::stopDebugServer() {
if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) {
EventManager::triggerEvent(std::make_shared<Events::ShutdownDebugServer>());
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
std::chrono::milliseconds(5000)
std::chrono::milliseconds{5000}
);
}
@@ -567,12 +558,14 @@ void Application::dispatchEvents() {
void Application::checkBloomVersion() {
const auto currentVersionNumber = Application::VERSION;
auto* networkAccessManager = new QNetworkAccessManager(this);
auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Services::PathService::homeDomainName() + "/latest-version"));
auto* networkAccessManager = new QNetworkAccessManager{this};
auto queryVersionEndpointUrl = QUrl{
QString::fromStdString(Services::PathService::homeDomainName() + "/latest-version")
};
queryVersionEndpointUrl.setScheme("http");
queryVersionEndpointUrl.setQuery(QUrlQuery({
queryVersionEndpointUrl.setQuery(QUrlQuery{
{"currentVersionNumber", QString::fromStdString(currentVersionNumber.toString())}
}));
});
QObject::connect(
networkAccessManager,
@@ -591,7 +584,7 @@ void Application::checkBloomVersion() {
}
);
networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl));
networkAccessManager->get(QNetworkRequest{queryVersionEndpointUrl});
}
#ifndef EXCLUDE_INSIGHT

View File

@@ -43,15 +43,9 @@ class Application: public QObject, public Thread
Q_OBJECT
public:
static const inline VersionNumber VERSION = VersionNumber(std::string(BLOOM_VERSION));
static const inline VersionNumber VERSION = VersionNumber{std::string{BLOOM_VERSION}};
explicit Application(std::vector<std::string>&& arguments);
/**
* Main entry-point for the Bloom program.
*
* @return
*/
int run();
private:

View File

@@ -8,7 +8,7 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/GdbDebugServerConfig.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/Connection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/DebugSession.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/TargetDescriptor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/ResponsePackets/ResponsePacket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/CommandPacket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/SupportedFeaturesQuery.cpp
@@ -22,9 +22,7 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/HelpMonitorInfo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/GenerateSvd.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/Detach.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/EepromFill.cpp
# AVR GDB RSP Server
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp
@@ -43,6 +41,8 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/EepromFill.cpp
)
if (NOT EXCLUDE_INSIGHT)

View File

@@ -29,7 +29,7 @@ namespace DebugServer
}
} catch (const std::exception& exception) {
Logger::error("DebugServer fatal error: " + std::string(exception.what()));
Logger::error("DebugServer fatal error: " + std::string{exception.what()});
}
this->shutdown();
@@ -44,7 +44,7 @@ namespace DebugServer
"avr-gdb-rsp",
[this] () -> std::unique_ptr<ServerInterface> {
if (this->targetDescriptor.family != Targets::TargetFamily::AVR_8) {
throw Exceptions::Exception("The AVR GDB RSP server is only compatible with AVR8 targets.");
throw Exceptions::Exception{"The AVR GDB RSP server is only compatible with AVR8 targets."};
}
return std::make_unique<DebugServer::Gdb::AvrGdb::AvrGdbRsp>(
@@ -75,7 +75,7 @@ namespace DebugServer
const auto selectedServerIt = availableServersByName.find(this->debugServerConfig.name);
if (selectedServerIt == availableServersByName.end()) {
throw Exceptions::InvalidConfig("DebugServer \"" + this->debugServerConfig.name + "\" not found.");
throw Exceptions::InvalidConfig{"DebugServer \"" + this->debugServerConfig.name + "\" not found."};
}
this->server = selectedServerIt->second();
@@ -111,7 +111,7 @@ namespace DebugServer
);
}
void DebugServerComponent::onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event) {
void DebugServerComponent::onShutdownDebugServerEvent(const Events::ShutdownDebugServer&) {
this->shutdown();
}
}

View File

@@ -54,7 +54,7 @@ namespace DebugServer
/**
* The current target descriptor.
*/
Targets::TargetDescriptor targetDescriptor;
const Targets::TargetDescriptor& targetDescriptor;
/**
* This EventFdNotifier is injected into this->eventListener. It can be used by server implementations to

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,19 +22,24 @@ namespace DebugServer::Gdb::CommandPackets
: Monitor(std::move(monitorPacket))
{}
void ActivateInsight::handle(DebugSession& debugSession, TargetControllerService&) {
void ActivateInsight::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService&
) {
Logger::info("Handling ActivateInsight packet");
try {
EventManager::triggerEvent(std::make_shared<Events::InsightActivationRequested>());
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
"The Insight GUI will be with you shortly.\n"
)));
debugSession.connection.writePacket(
ResponsePacket{Services::StringService::toHex("The Insight GUI will be with you shortly.\n")}
);
} catch (const Exception& exception) {
Logger::error("Failed to activate Insight - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -22,15 +22,20 @@ namespace DebugServer::Gdb::CommandPackets
: Monitor(std::move(monitorPacket))
{}
void BloomVersion::handle(DebugSession& debugSession, TargetControllerService&) {
void BloomVersion::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService&
) {
Logger::info("Handling BloomVersion packet");
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
std::string(
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
std::string{
"Bloom v" + Application::VERSION.toString() + "\n"
+ Services::PathService::homeDomainName() + "\n"
+ "Nav Mohammed\n"
)
)));
}
)});
}
}

View File

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

View File

@@ -21,18 +21,28 @@ namespace DebugServer::Gdb::CommandPackets
: Monitor(std::move(monitorPacket))
{}
void BloomVersionMachine::handle(DebugSession& debugSession, TargetControllerService&) {
void BloomVersionMachine::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService&
) {
Logger::info("Handling BloomVersionMachine packet");
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
QJsonDocument(QJsonObject({
{"version", QString::fromStdString(Application::VERSION.toString())},
{"components", QJsonObject({
{"major", Application::VERSION.major},
{"minor", Application::VERSION.minor},
{"patch", Application::VERSION.patch},
})},
})).toJson().toStdString()
)));
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
QJsonDocument{
QJsonObject{
{"version", QString::fromStdString(Application::VERSION.toString())},
{
"components",
QJsonObject{
{"major", Application::VERSION.major},
{"minor", Application::VERSION.minor},
{"patch", Application::VERSION.patch},
}
},
}
}.toJson().toStdString()
)});
}
}

View File

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

View File

@@ -8,6 +8,7 @@
#include "src/DebugServer/Gdb/Signal.hpp"
#include "src/DebugServer/Gdb/Exceptions/ClientCommunicationError.hpp"
#include "src/Logger/Logger.hpp"
namespace DebugServer::Gdb::CommandPackets
@@ -20,36 +21,49 @@ namespace DebugServer::Gdb::CommandPackets
using ResponsePackets::EmptyResponsePacket;
using ResponsePackets::ErrorResponsePacket;
void CommandPacket::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
const auto packetString = std::string(this->data.begin(), this->data.end());
CommandPacket::CommandPacket(const RawPacket& rawPacket) {
if (rawPacket.size() < 5) {
throw Exceptions::ClientCommunicationError{"Invalid raw packet size"};
}
this->data.insert(this->data.begin(), rawPacket.begin() + 1, rawPacket.end() - 3);
}
void CommandPacket::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService&
) {
const auto packetString = std::string{this->data.begin(), this->data.end()};
if (packetString.empty()) {
Logger::error("Empty GDB RSP packet received.");
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
return;
}
if (packetString[0] == '?') {
// Status report
debugSession.connection.writePacket(TargetStopped(Signal::TRAP));
debugSession.connection.writePacket(TargetStopped{Signal::TRAP});
return;
}
if (packetString.find("vMustReplyEmpty") == 0) {
Logger::info("Handling vMustReplyEmpty");
debugSession.connection.writePacket(EmptyResponsePacket());
debugSession.connection.writePacket(EmptyResponsePacket{});
return;
}
if (packetString.find("qAttached") == 0) {
Logger::info("Handling qAttached");
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>({1})));
debugSession.connection.writePacket(ResponsePacket{std::vector<unsigned char>({1})});
return;
}
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
// Respond with an empty packet
debugSession.connection.writePacket(EmptyResponsePacket());
// GDB expects an empty response for all unsupported commands
debugSession.connection.writePacket(EmptyResponsePacket{});
}
}

View File

@@ -1,44 +1,18 @@
#pragma once
#include <vector>
#include <memory>
#include "src/DebugServer/Gdb/Packet.hpp"
#include "src/DebugServer/Gdb/DebugSession.hpp"
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "src/Services/TargetControllerService.hpp"
namespace DebugServer::Gdb::CommandPackets
{
/**
* GDB RSP command packets are sent to the server, from the GDB client. These packets carry instructions that the
* server is expected to carry out. Upon completion, the server is expected to respond to the client with
* a ResponsePacket.
*
* For some command packets, we define a specific data structure by extending this CommandPacket class. These
* classes extend the data structure to include fields for data which may be specific to the command.
* They also implement additional methods that allow us to easily access the additional data. An example
* of this would be the SupportedFeaturesQuery class. It extends the CommandPacket class and provides access
* to additional data fields that are specific to the command (in this case, a set of GDB features reported to be
* supported by the GDB client).
*
* Typically, command packets that require specific data structures are handled in a dedicated handler method
* in the GdbRspDebugServer. This is done by double dispatching the packet object to the appropriate handler.
* See CommandPacket::dispatchToHandler(), GdbRspDebugServer::serve() and the overloads
* for GdbRspDebugServer::handleGdbPacket() for more on this.
*
* Some command packets are so simple they do not require a dedicated data structure. An example of this is
* the halt reason packet, which contains nothing more than an ? character in the packet body. These packets are
* typically handled in the generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) method.
*
* See the Packet class for information on how the raw packets are formatted.
*/
class CommandPacket: public Packet
class CommandPacket
{
public:
explicit CommandPacket(const RawPacket& rawPacket)
: Packet(rawPacket)
{}
explicit CommandPacket(const RawPacket& rawPacket);
/**
* Should handle the command for the current active debug session.
@@ -50,7 +24,12 @@ namespace DebugServer::Gdb::CommandPackets
*/
virtual void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
);
protected:
std::vector<unsigned char> data;
};
}

View File

@@ -2,6 +2,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -17,21 +18,34 @@ namespace DebugServer::Gdb::CommandPackets
{
if (this->data.size() > 2) {
this->fromAddress = static_cast<Targets::TargetMemoryAddress>(
std::stoi(std::string(this->data.begin() + 2, this->data.end()), nullptr, 16)
Services::StringService::toUint32(std::string{this->data.begin() + 2, this->data.end()}, 16)
);
}
}
void ContinueExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void ContinueExecution::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ContinueExecution packet");
try {
targetControllerService.continueTargetExecution(this->fromAddress, std::nullopt);
{
const auto atomicSession = targetControllerService.makeAtomicSession();
if (this->fromAddress.has_value()) {
targetControllerService.setProgramCounter(*(this->fromAddress));
}
targetControllerService.resumeTargetExecution();
}
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -20,7 +20,12 @@ namespace DebugServer::Gdb::CommandPackets
: CommandPacket(rawPacket)
{}
void Detach::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void Detach::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling Detach packet");
try {
@@ -28,11 +33,11 @@ namespace DebugServer::Gdb::CommandPackets
targetControllerService.shutdown();
}
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to shut down TargetController - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

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

View File

@@ -1,225 +0,0 @@
#include "GenerateSvd.hpp"
#include <QString>
#include <QFile>
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Application.hpp"
#include "src/Services/PathService.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ::Exceptions::Exception;
GenerateSvd::GenerateSvd(Monitor&& monitorPacket)
: Monitor(std::move(monitorPacket))
, sendOutput(this->commandOptions.contains("out"))
{}
void GenerateSvd::handle(DebugSession& debugSession, TargetControllerService&) {
Logger::info("Handling GenerateSvd packet");
try {
Logger::info("Generating SVD XML for current target");
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
const auto svdXml = this->generateSvd(
targetDescriptor,
debugSession.gdbTargetDescriptor.getMemoryOffset(Targets::TargetMemoryType::RAM)
);
if (this->sendOutput) {
debugSession.connection.writePacket(
ResponsePacket(Services::StringService::toHex(svdXml.toString().toStdString()))
);
return;
}
const auto svdOutputFilePath = Services::PathService::projectDirPath() + "/" + targetDescriptor.name + ".svd";
auto outputFile = QFile(QString::fromStdString(svdOutputFilePath));
if (!outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
throw Exception(
"Failed to open/create SVD output file (" + svdOutputFilePath + "). Check file permissions."
);
}
outputFile.write(svdXml.toByteArray());
outputFile.close();
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
"SVD output saved to " + svdOutputFilePath + "\n"
)));
Logger::info("SVD output saved to " + svdOutputFilePath);
} catch (const Exception& exception) {
Logger::error(exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
QDomDocument GenerateSvd::generateSvd(
const Targets::TargetDescriptor& targetDescriptor,
std::uint32_t baseAddressOffset
) {
auto document = QDomDocument();
const auto createElement = [&document] (const QString& tagName, const QString& value) {
auto element = document.createElement(tagName);
auto textNode = document.createTextNode(value);
element.appendChild(textNode);
return element;
};
document.appendChild(document.createComment(
" This SVD was generated by Bloom (https://bloom.oscillate.io/). "
"Please report any issues via https://bloom.oscillate.io/report-issue "
));
if (baseAddressOffset != 0) {
document.appendChild(document.createComment(
" Base addresses in this SVD have been offset by 0x" + QString::number(baseAddressOffset, 16)
+ ". This offset is required for access via avr-gdb. "
));
}
auto deviceElement = document.createElement("device");
deviceElement.setAttribute("schemaVersion", "1.3");
deviceElement.setAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance");
deviceElement.setAttribute(
"xs:noNamespaceSchemaLocation",
QString::fromStdString(Services::PathService::homeDomainName() + "/assets/svd-schema.xsd")
);
deviceElement.appendChild(createElement("vendor", QString::fromStdString(targetDescriptor.vendorName)));
deviceElement.appendChild(createElement("name", QString::fromStdString(targetDescriptor.name)));
deviceElement.appendChild(document.createComment(
" The version number below is that of the Bloom binary which generated this SVD. "
));
deviceElement.appendChild(createElement("version", QString::fromStdString(Application::VERSION.toString())));
deviceElement.appendChild(
createElement(
"description",
QString::fromStdString(targetDescriptor.name) + " from "
+ QString::fromStdString(targetDescriptor.vendorName)
)
);
/*
* TODO: These values should be part of the TargetDescriptor, but given that Bloom only supports 8-bit AVRs,
* it really doesn't matter ATM. Will fix it later (lol no I won't).
*/
deviceElement.appendChild(createElement("addressUnitBits", "8"));
deviceElement.appendChild(createElement("width", "8"));
deviceElement.appendChild(createElement("size", "8"));
deviceElement.appendChild(createElement("access", "read-only"));
struct Peripheral {
QString name;
std::uint32_t baseAddress;
Targets::TargetRegisterDescriptors registerDescriptors;
};
auto peripheralsByName = std::map<std::string, Peripheral>();
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;
}
auto peripheralIt = peripheralsByName.find(*registerDescriptor.groupName);
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);
}
auto peripheralsElement = document.createElement("peripherals");
for (const auto& [peripheralName, peripheral] : peripheralsByName) {
auto peripheralElement = document.createElement("peripheral");
peripheralElement.appendChild(createElement("name", peripheral.name));
peripheralElement.appendChild(createElement("baseAddress", "0x" + QString::number(peripheral.baseAddress, 16)));
auto registersElement = document.createElement("registers");
for (const auto& registerDescriptor : peripheral.registerDescriptors) {
auto registerElement = document.createElement("register");
registerElement.appendChild(
createElement(
"name",
QString::fromStdString(*registerDescriptor.name).replace(QChar(' '), QChar('_')).toUpper()
)
);
if (registerDescriptor.description.has_value()) {
registerElement.appendChild(
createElement("description", QString::fromStdString(*registerDescriptor.description))
);
}
registerElement.appendChild(
createElement("addressOffset", "0x" + QString::number(*registerDescriptor.startAddress, 16))
);
registerElement.appendChild(
createElement("size", QString::number(registerDescriptor.size * 8))
);
registerElement.appendChild(
createElement("access", registerDescriptor.access.writable ? "read-write" : "read-only")
);
registersElement.appendChild(registerElement);
}
peripheralElement.appendChild(registersElement);
peripheralsElement.appendChild(peripheralElement);
}
deviceElement.appendChild(peripheralsElement);
document.appendChild(deviceElement);
return document;
}
}

View File

@@ -1,35 +0,0 @@
#pragma once
#include <cstdint>
#include "Monitor.hpp"
#include "src/Targets/TargetDescriptor.hpp"
namespace DebugServer::Gdb::CommandPackets
{
/**
* The GenerateSvd class implements a structure for the "monitor svd" GDB command.
*
* This command generates XML conforming to the CMSIS-SVD schema, for the connected target. Will output the XML to
* a file or send it to GDB.
*/
class GenerateSvd: public Monitor
{
public:
explicit GenerateSvd(Monitor&& monitorPacket);
void handle(
DebugSession& debugSession,
Services::TargetControllerService& targetControllerService
) override;
private:
bool sendOutput = false;
QDomDocument generateSvd(
const Targets::TargetDescriptor& targetDescriptor,
std::uint32_t baseAddressOffset = 0
);
};
}

View File

@@ -25,7 +25,12 @@ namespace DebugServer::Gdb::CommandPackets
: Monitor(std::move(monitorPacket))
{}
void HelpMonitorInfo::handle(DebugSession& debugSession, TargetControllerService&) {
void HelpMonitorInfo::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService&
) {
Logger::info("Handling HelpMonitorInfo packet");
try {
@@ -33,26 +38,24 @@ namespace DebugServer::Gdb::CommandPackets
* The file GdbHelpMonitorInfo.txt is included in the binary image as a resource.
* See src/DebugServer/CMakeLists.txt for more.
*/
auto helpFile = QFile(
auto helpFile = QFile{
QString::fromStdString(":/compiled/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt")
);
};
if (!helpFile.open(QIODevice::ReadOnly)) {
throw Exception(
throw Exception{
"Failed to open GDB monitor info help file - please report this issue at "
+ Services::PathService::homeDomainName() + "/report-issue"
);
};
}
debugSession.connection.writePacket(
ResponsePacket(Services::StringService::toHex(
"\n" + QTextStream(&helpFile).readAll().toUtf8().toStdString() + "\n"
))
);
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
"\n" + QTextStream{&helpFile}.readAll().toUtf8().toStdString() + "\n"
)});
} catch (const Exception& exception) {
Logger::error(exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -16,10 +16,15 @@ namespace DebugServer::Gdb::CommandPackets
using ::Exceptions::Exception;
void InterruptExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void InterruptExecution::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling InterruptExecution packet");
if (targetControllerService.getTargetState() == Targets::TargetState::STOPPED) {
if (targetControllerService.getTargetState().executionState == Targets::TargetExecutionState::STOPPED) {
debugSession.pendingInterrupt = true;
return;
}
@@ -32,7 +37,7 @@ namespace DebugServer::Gdb::CommandPackets
}
debugSession.waitingForBreak = false;
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
debugSession.connection.writePacket(TargetStopped{Signal::INTERRUPTED});
} catch (const Exception& exception) {
Logger::error("Failed to interrupt execution - " + exception.getMessage());

View File

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

View File

@@ -2,6 +2,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
namespace DebugServer::Gdb::CommandPackets
@@ -17,23 +18,28 @@ namespace DebugServer::Gdb::CommandPackets
return;
}
const auto decodedCommand = Packet::hexToData(
std::string(this->data.begin() + 6, this->data.end())
const auto decodedCommand = Services::StringService::dataFromHex(
std::string{this->data.begin() + 6, this->data.end()}
);
this->command = std::string(decodedCommand.begin(), decodedCommand.end());
this->command = std::string{decodedCommand.begin(), decodedCommand.end()};
this->command.erase(this->command.find_last_not_of(" ") + 1);
this->commandOptions = this->extractCommandOptions(this->command);
}
void Monitor::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void Monitor::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::error("Unknown custom GDB command (\"" + this->command + "\") received.");
debugSession.connection.writePacket(EmptyResponsePacket());
debugSession.connection.writePacket(EmptyResponsePacket{});
}
std::map<std::string, std::optional<std::string>> Monitor::extractCommandOptions(const std::string& command) {
auto output = std::map<std::string, std::optional<std::string>>();
auto output = std::map<std::string, std::optional<std::string>>{};
for (std::string::size_type cmdIndex = 1; cmdIndex < command.size(); ++cmdIndex) {
const auto cmdChar = command.at(cmdIndex);
@@ -43,17 +49,17 @@ namespace DebugServer::Gdb::CommandPackets
continue;
}
auto option = std::string();
auto optionValue = std::optional<std::string>();
auto option = std::string{};
auto optionValue = std::optional<std::string>{};
bool quoted = false;
auto optIndex = std::string::size_type(0);
auto optIndex = std::string::size_type{0};
for (optIndex = cmdIndex + 1; optIndex < command.size(); ++optIndex) {
const auto optChar = command.at(optIndex);
if (!option.empty() && ((!quoted && optChar == ' ') || (quoted && optChar == '"'))) {
output.insert(std::pair(option, optionValue));
output.emplace(option, optionValue);
option.clear();
optionValue.reset();
@@ -74,7 +80,7 @@ namespace DebugServer::Gdb::CommandPackets
}
if (optChar == '=') {
optionValue = std::string();
optionValue = std::string{};
continue;
}
@@ -83,7 +89,7 @@ namespace DebugServer::Gdb::CommandPackets
}
if (!option.empty()) {
output.insert(std::pair(option, optionValue));
output.emplace(option, optionValue);
cmdIndex = optIndex;
}
}

View File

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

View File

@@ -25,12 +25,13 @@ namespace DebugServer::Gdb::CommandPackets
: CommandPacket(rawPacket)
{
if (this->data.size() < 6) {
throw Exception("Unexpected RemoveBreakpoint packet size");
throw Exception{"Unexpected RemoveBreakpoint packet size"};
}
// z0 = SW breakpoint, z1 = HW breakpoint
this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
this->type = (this->data[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
const auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
@@ -39,29 +40,34 @@ namespace DebugServer::Gdb::CommandPackets
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet");
throw Exception{"Unexpected number of packet segments in RemoveBreakpoint packet"};
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
throw Exception{"Failed to convert address hex value from RemoveBreakpoint packet."};
}
}
void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void RemoveBreakpoint::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling RemoveBreakpoint packet");
try {
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -21,7 +21,12 @@ namespace DebugServer::Gdb::CommandPackets
: Monitor(std::move(monitorPacket))
{}
void ResetTarget::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void ResetTarget::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ResetTarget packet");
try {
@@ -29,15 +34,15 @@ namespace DebugServer::Gdb::CommandPackets
targetControllerService.resetTarget();
Logger::info("Target reset complete");
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
"Target reset complete\n"
"Current PC: 0x" + Services::StringService::toHex(targetControllerService.getProgramCounter()) + "\n"
"Use the 'continue' command to begin execution\n"
)));
)});
} catch (const Exception& exception) {
Logger::error("Failed to reset target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -27,12 +27,13 @@ namespace DebugServer::Gdb::CommandPackets
: CommandPacket(rawPacket)
{
if (this->data.size() < 6) {
throw Exception("Unexpected SetBreakpoint packet size");
throw Exception{"Unexpected SetBreakpoint packet size"};
}
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
this->type = (this->data[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
@@ -41,36 +42,41 @@ namespace DebugServer::Gdb::CommandPackets
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in SetBreakpoint packet");
throw Exception{"Unexpected number of packet segments in SetBreakpoint packet"};
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
throw Exception{"Failed to convert address hex value from SetBreakpoint packet."};
}
}
void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void SetBreakpoint::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling SetBreakpoint packet");
try {
if (this->type == BreakpointType::UNKNOWN) {
Logger::debug(
"Rejecting breakpoint at address " + std::to_string(this->address)
+ " - unsupported breakpoint type"
+ " - unsupported breakpoint type"
);
debugSession.connection.writePacket(EmptyResponsePacket());
debugSession.connection.writePacket(EmptyResponsePacket{});
return;
}
debugSession.setExternalBreakpoint(this->address, targetControllerService);
debugSession.connection.writePacket(OkResponsePacket());
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -2,6 +2,7 @@
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
@@ -18,21 +19,31 @@ namespace DebugServer::Gdb::CommandPackets
{
if (this->data.size() > 2) {
this->fromAddress = static_cast<Targets::TargetMemoryAddress>(
std::stoi(std::string(this->data.begin() + 2, this->data.end()), nullptr, 16)
Services::StringService::toUint32(std::string{this->data.begin() + 2, this->data.end()}, 16)
);
}
}
void StepExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void StepExecution::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling StepExecution packet");
try {
targetControllerService.stepTargetExecution(this->fromAddress);
const auto atomicSession = targetControllerService.makeAtomicSession();
if (this->fromAddress.has_value()) {
targetControllerService.setProgramCounter(*(this->fromAddress));
}
targetControllerService.stepTargetExecution();
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

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

View File

@@ -51,17 +51,23 @@ namespace DebugServer::Gdb::CommandPackets
}
}
void SupportedFeaturesQuery::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
void SupportedFeaturesQuery::handle(
DebugSession& debugSession,
const TargetDescriptor&,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling QuerySupport packet");
if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
if (
!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
&& !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
) {
// All GDB clients are expected to support breakpoints!
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
throw ClientNotSupported{"GDB client does not support HW or SW breakpoints"};
}
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
debugSession.connection.writePacket(SupportedFeaturesResponse(debugSession.supportedFeatures));
debugSession.connection.writePacket(SupportedFeaturesResponse{debugSession.supportedFeatures});
}
}

View File

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

View File

@@ -45,23 +45,23 @@ namespace DebugServer::Gdb
}
std::string Connection::getIpAddress() const {
std::array<char, INET_ADDRSTRLEN> ipAddress = {};
auto ipAddress = std::array<char, INET_ADDRSTRLEN>{};
if (::inet_ntop(AF_INET, &(socketAddress.sin_addr), ipAddress.data(), INET_ADDRSTRLEN) == nullptr) {
throw Exception("Failed to convert client IP address to text form.");
throw Exception{"Failed to convert client IP address to text form."};
}
return std::string(ipAddress.data());
return {ipAddress.data()};
}
std::vector<RawPacket> Connection::readRawPackets() {
std::vector<RawPacket> output;
auto output = std::vector<RawPacket>{};
do {
const auto bytes = this->read();
std::size_t bufferSize = bytes.size();
for (std::size_t byteIndex = 0; byteIndex < bufferSize; byteIndex++) {
auto bufferSize = bytes.size();
for (auto byteIndex = std::size_t{0}; byteIndex < bufferSize; byteIndex++) {
auto byte = bytes[byteIndex];
if (byte == 0x03) {
@@ -131,7 +131,7 @@ namespace DebugServer::Gdb
Logger::debug(
"Read GDB packet: "
+ Services::StringService::replaceUnprintable(
std::string(rawPacket.begin(), rawPacket.end())
std::string{rawPacket.begin(), rawPacket.end()}
)
);
@@ -153,13 +153,13 @@ namespace DebugServer::Gdb
int attempts = 0;
const auto rawPacket = packet.toRawPacket();
Logger::debug("Writing GDB packet: " + std::string(rawPacket.begin(), rawPacket.end()));
Logger::debug("Writing GDB packet: " + std::string{rawPacket.begin(), rawPacket.end()});
do {
if (attempts > 10) {
throw ClientCommunicationError(
throw ClientCommunicationError{
"Failed to write GDB response packet - client failed to acknowledge receipt - retry limit reached"
);
};
}
this->write(rawPacket);
@@ -177,7 +177,7 @@ namespace DebugServer::Gdb
);
if (socketFileDescriptor < 0) {
throw Exception("Failed to accept GDB Remote Serial Protocol connection");
throw Exception{"Failed to accept GDB Remote Serial Protocol connection"};
}
this->socketFileDescriptor = socketFileDescriptor;
@@ -219,7 +219,7 @@ namespace DebugServer::Gdb
if (eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()) {
// Interrupted
this->interruptEventNotifier.clear();
throw DebugServerInterrupted();
throw DebugServerInterrupted{};
}
const auto bytesToRead = bytes.value_or(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE);
@@ -232,14 +232,14 @@ namespace DebugServer::Gdb
);
if (bytesRead < 0) {
throw ClientCommunicationError(
throw ClientCommunicationError{
"Failed to read data from GDB client - error code: " + std::to_string(errno)
);
};
}
if (bytesRead == 0) {
// Client has disconnected
throw ClientDisconnected();
throw ClientDisconnected{};
}
if (bytesRead != output.size()) {
@@ -250,7 +250,7 @@ namespace DebugServer::Gdb
}
std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
auto bytes = this->read(1, interruptible, std::chrono::milliseconds(300));
auto bytes = this->read(1, interruptible, std::chrono::milliseconds{300});
if (!bytes.empty()) {
return bytes.front();
@@ -263,19 +263,18 @@ namespace DebugServer::Gdb
if (::write(this->socketFileDescriptor.value(), buffer.data(), buffer.size()) == -1) {
if (errno == EPIPE || errno == ECONNRESET) {
// Connection was closed
throw ClientDisconnected();
throw ClientDisconnected{};
}
throw ClientCommunicationError(
throw ClientCommunicationError{
"Failed to write " + std::to_string(buffer.size()) + " bytes to GDP client socket - error no: "
+ std::to_string(errno)
);
};
}
}
void Connection::disableReadInterrupts() {
this->epollInstance.removeEntry(this->interruptEventNotifier.getFileDescriptor());
this->readInterruptEnabled = false;
}

View File

@@ -31,7 +31,7 @@ namespace DebugServer::Gdb
*/
static constexpr auto ABSOLUTE_MAXIMUM_PACKET_READ_SIZE = 2097000; // 2MiB
explicit Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier);
Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier);
Connection() = delete;
Connection(const Connection&) = delete;

View File

@@ -7,17 +7,16 @@ namespace DebugServer::Gdb
DebugSession::DebugSession(
Connection&& connection,
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
const TargetDescriptor& targetDescriptor,
const GdbDebugServerConfig& serverConfig
)
: connection(std::move(connection))
, supportedFeatures(supportedFeatures)
, gdbTargetDescriptor(targetDescriptor)
, serverConfig(serverConfig)
{
this->supportedFeatures.insert({
Feature::PACKET_SIZE, std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE)
});
this->supportedFeatures.emplace(
Feature::PACKET_SIZE,
std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE)
);
EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>());
}
@@ -35,18 +34,15 @@ namespace DebugServer::Gdb
}
const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address);
if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) {
// We already have an external breakpoint at this address
this->internalBreakpointsByAddress.insert(std::pair(address, externalBreakpointIt->second));
this->internalBreakpointsByAddress.emplace(address, externalBreakpointIt->second);
return;
}
this->internalBreakpointsByAddress.insert(
std::pair(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
)
this->internalBreakpointsByAddress.emplace(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
);
}
@@ -78,15 +74,13 @@ namespace DebugServer::Gdb
if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) {
// We already have an internal breakpoint at this address
this->externalBreakpointsByAddress.insert(std::pair(address, internalBreakpointIt->second));
this->externalBreakpointsByAddress.emplace(address, internalBreakpointIt->second);
return;
}
this->externalBreakpointsByAddress.insert(
std::pair(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
)
this->externalBreakpointsByAddress.emplace(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
);
}

View File

@@ -32,11 +32,6 @@ namespace DebugServer::Gdb
*/
std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures;
/**
* The GDB target descriptor of the connected target.
*/
const TargetDescriptor& gdbTargetDescriptor;
/**
* The current server configuration.
*/
@@ -92,7 +87,6 @@ namespace DebugServer::Gdb
DebugSession(
Connection&& connection,
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
const TargetDescriptor& targetDescriptor,
const GdbDebugServerConfig& serverConfig
);

View File

@@ -16,6 +16,6 @@ namespace DebugServer::Gdb::Exceptions
class DebugServerInterrupted: public ::Exceptions::Exception
{
public:
explicit DebugServerInterrupted() = default;
DebugServerInterrupted() = default;
};
}

View File

@@ -12,7 +12,7 @@ namespace DebugServer::Gdb
if (!YamlUtilities::isCastable<std::string>(debugServerConfig.debugServerNode["ipAddress"])) {
Logger::error(
"Invalid GDB debug server config parameter ('ipAddress') provided - must be a string. The "
"parameter will be ignored."
"parameter will be ignored."
);
}
@@ -26,7 +26,7 @@ namespace DebugServer::Gdb
} else {
Logger::error(
"Invalid GDB debug server config parameter ('port') provided - value must be castable to a 16-bit "
"unsigned integer. The parameter will be ignored."
"unsigned integer. The parameter will be ignored."
);
}
}
@@ -38,7 +38,7 @@ namespace DebugServer::Gdb
} else {
Logger::error(
"Invalid GDB debug server config parameter ('rangeStepping') provided - value must be castable to "
"a boolean. The parameter will be ignored."
"a boolean. The parameter will be ignored."
);
}
}

View File

@@ -28,9 +28,7 @@
#include "CommandPackets/HelpMonitorInfo.hpp"
#include "CommandPackets/BloomVersion.hpp"
#include "CommandPackets/BloomVersionMachine.hpp"
#include "CommandPackets/GenerateSvd.hpp"
#include "CommandPackets/Detach.hpp"
#include "CommandPackets/EepromFill.hpp"
#ifndef EXCLUDE_INSIGHT
#include "CommandPackets/ActivateInsight.hpp"
@@ -51,10 +49,12 @@ namespace DebugServer::Gdb
GdbRspDebugServer::GdbRspDebugServer(
const DebugServerConfig& debugServerConfig,
const Targets::TargetDescriptor& targetDescriptor,
EventListener& eventListener,
EventFdNotifier& eventNotifier
)
: debugServerConfig(GdbDebugServerConfig(debugServerConfig))
, targetDescriptor(targetDescriptor)
, eventListener(eventListener)
, interruptEventNotifier(eventNotifier)
{}
@@ -63,28 +63,27 @@ namespace DebugServer::Gdb
this->socketAddress.sin_family = AF_INET;
this->socketAddress.sin_port = htons(this->debugServerConfig.listeningPortNumber);
if (::inet_pton(
if (
::inet_pton(
AF_INET,
this->debugServerConfig.listeningAddress.c_str(),
&(this->socketAddress.sin_addr)
) == 0
) {
// Invalid IP address
throw InvalidConfig(
"Invalid IP address provided in config file: (\"" + this->debugServerConfig.listeningAddress
+ "\")"
);
throw InvalidConfig{
"Invalid IP address provided in config file: (\"" + this->debugServerConfig.listeningAddress + "\")"
};
}
int socketFileDescriptor = 0;
auto socketFileDescriptor = int{0};
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
throw Exception("Failed to create socket file descriptor.");
throw Exception{"Failed to create socket file descriptor."};
}
const auto enableReuseAddressSocketOption = 1;
if (::setsockopt(
const auto enableReuseAddressSocketOption = int{1};
if (
::setsockopt(
socketFileDescriptor,
SOL_SOCKET,
SO_REUSEADDR,
@@ -95,14 +94,17 @@ namespace DebugServer::Gdb
Logger::error("Failed to set socket SO_REUSEADDR option.");
}
if (::bind(
if (
::bind(
socketFileDescriptor,
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
sizeof(this->socketAddress)
) < 0
) {
throw Exception("Failed to bind address. The selected port number ("
+ std::to_string(this->debugServerConfig.listeningPortNumber) + ") may be in use.");
throw Exception{
"Failed to bind address. The selected port number ("
+ std::to_string(this->debugServerConfig.listeningPortNumber) + ") may be in use."
};
}
this->serverSocketFileDescriptor = socketFileDescriptor;
@@ -120,12 +122,8 @@ namespace DebugServer::Gdb
Logger::info("GDB RSP address: " + this->debugServerConfig.listeningAddress);
Logger::info("GDB RSP port: " + std::to_string(this->debugServerConfig.listeningPortNumber));
this->eventListener.registerCallbackForEventType<Events::TargetExecutionStopped>(
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
);
this->eventListener.registerCallbackForEventType<Events::TargetExecutionResumed>(
std::bind(&GdbRspDebugServer::onTargetExecutionResumed, this, std::placeholders::_1)
this->eventListener.registerCallbackForEventType<Events::TargetStateChanged>(
std::bind(&GdbRspDebugServer::onTargetStateChanged, this, std::placeholders::_1)
);
if (Services::ProcessService::isManagedByClion()) {
@@ -149,7 +147,6 @@ namespace DebugServer::Gdb
Logger::info("Waiting for GDB RSP connection");
auto connection = this->waitForConnection();
Logger::info("Accepted GDP RSP connection from " + connection.getIpAddress());
this->startDebugSession(std::move(connection));
@@ -159,9 +156,13 @@ namespace DebugServer::Gdb
}
const auto commandPacket = this->waitForCommandPacket();
if (commandPacket) {
commandPacket->handle(*(this->getActiveDebugSession()), this->targetControllerService);
commandPacket->handle(
*(this->getActiveDebugSession()),
this->getGdbTargetDescriptor(),
this->targetDescriptor,
this->targetControllerService
);
}
} catch (const ClientDisconnected&) {
@@ -195,28 +196,25 @@ namespace DebugServer::Gdb
Connection GdbRspDebugServer::waitForConnection() {
if (::listen(this->serverSocketFileDescriptor.value(), 3) != 0) {
throw Exception("Failed to listen on server socket");
throw Exception{"Failed to listen on server socket"};
}
const auto eventFileDescriptor = this->epollInstance.waitForEvent();
if (
!eventFileDescriptor.has_value()
|| eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()
|| *eventFileDescriptor == this->interruptEventNotifier.getFileDescriptor()
) {
this->interruptEventNotifier.clear();
throw DebugServerInterrupted();
throw DebugServerInterrupted{};
}
return Connection(
this->serverSocketFileDescriptor.value(),
this->interruptEventNotifier
);
return {this->serverSocketFileDescriptor.value(), this->interruptEventNotifier};
}
std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() {
auto* debugSession = this->getActiveDebugSession();
const auto rawPackets = debugSession->connection.readRawPackets();
assert(!rawPackets.empty());
if (rawPackets.size() > 1) {
const auto& firstRawPacket = rawPackets.front();
@@ -224,99 +222,110 @@ namespace DebugServer::Gdb
if (firstRawPacket.size() == 5 && firstRawPacket[1] == 0x03) {
// Interrupt packet that came in too quickly before another packet
debugSession->pendingInterrupt = true;
}
// We only process the last packet - any others will probably be duplicates from an impatient client.
Logger::warning("Multiple packets received from GDB - only the most recent will be processed");
} else {
Logger::warning("Multiple packets received from GDB - only the most recent will be processed");
}
}
return this->resolveCommandPacket(rawPackets.back());
}
std::unique_ptr<CommandPacket> GdbRspDebugServer::resolveCommandPacket(const RawPacket& rawPacket) {
if (rawPacket.size() < 2) {
throw Exception{"Invalid raw packet - no data"};
}
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
// Interrupt request
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
}
if (rawPacket[1] == 'c') {
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
}
if (rawPacket[1] == 's') {
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
}
if (rawPacket[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
}
if (rawPacket[1] == 'z') {
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
}
if (rawPacket[1] == 'D') {
return std::make_unique<CommandPackets::Detach>(rawPacket);
}
const auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()};
if (rawPacketString.size() >= 2) {
/*
* First byte of the raw packet will be 0x24 ('$'), so std::string::find() should return 1, not 0, when
* looking for a command identifier string.
*/
if (rawPacketString.find("qSupported") == 1) {
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
/*
* First byte of the raw packet will be 0x24 ('$'), so std::string::find() should return 1, not 0, when
* looking for a command identifier string.
*/
if (rawPacketString.find("qSupported") == 0) {
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
}
if (rawPacketString.find("qRcmd") == 0) {
// This is a monitor packet
auto monitorCommand = std::make_unique<CommandPackets::Monitor>(rawPacket);
if (monitorCommand->command == "help") {
return std::make_unique<CommandPackets::HelpMonitorInfo>(std::move(*(monitorCommand.release())));
}
if (rawPacketString[1] == 'c') {
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
if (monitorCommand->command == "version") {
return std::make_unique<CommandPackets::BloomVersion>(std::move(*(monitorCommand.release())));
}
if (rawPacketString[1] == 's') {
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
if (monitorCommand->command == "version machine") {
return std::make_unique<CommandPackets::BloomVersionMachine>(std::move(*(monitorCommand.release())));
}
if (rawPacketString[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
if (monitorCommand->command == "reset") {
return std::make_unique<CommandPackets::ResetTarget>(std::move(*(monitorCommand.release())));
}
if (rawPacketString[1] == 'z') {
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
}
if (rawPacketString.find("qRcmd") == 1) {
// This is a monitor packet
auto monitorCommand = std::make_unique<CommandPackets::Monitor>(rawPacket);
if (monitorCommand->command == "help") {
return std::make_unique<CommandPackets::HelpMonitorInfo>(std::move(*(monitorCommand.release())));
}
if (monitorCommand->command == "version") {
return std::make_unique<CommandPackets::BloomVersion>(std::move(*(monitorCommand.release())));
}
if (monitorCommand->command == "version machine") {
return std::make_unique<CommandPackets::BloomVersionMachine>(std::move(*(monitorCommand.release())));
}
if (monitorCommand->command == "reset") {
return std::make_unique<CommandPackets::ResetTarget>(std::move(*(monitorCommand.release())));
}
if (monitorCommand->command.find("svd") == 0) {
return std::make_unique<CommandPackets::GenerateSvd>(std::move(*(monitorCommand.release())));
}
if (monitorCommand->command.find("eeprom fill") == 0) {
return std::make_unique<CommandPackets::EepromFill>(std::move(*(monitorCommand.release())));
}
#ifndef EXCLUDE_INSIGHT
if (monitorCommand->command.find("insight") == 0) {
return std::make_unique<CommandPackets::ActivateInsight>(std::move(*(monitorCommand.release())));
}
#endif
return monitorCommand;
if (monitorCommand->command.find("insight") == 0) {
return std::make_unique<CommandPackets::ActivateInsight>(std::move(*(monitorCommand.release())));
}
#endif
return monitorCommand;
}
return std::make_unique<CommandPacket>(rawPacket);
}
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped& stoppedEvent) {
using Services::StringService;
void GdbRspDebugServer::onTargetStateChanged(const Events::TargetStateChanged& event) {
using Targets::TargetExecutionState;
auto* debugSession = this->getActiveDebugSession();
if (debugSession == nullptr) {
return;
}
if (event.newState.executionState == event.previousState.executionState) {
// Execution state hasn't changed. Probably just a mode change. Ignore...
return;
}
const auto executionState = event.newState.executionState.load();
try {
if (debugSession != nullptr && debugSession->waitingForBreak) {
this->handleTargetStoppedGdbResponse(stoppedEvent.programCounter);
if (executionState == TargetExecutionState::STOPPED && debugSession->waitingForBreak) {
this->handleTargetStoppedGdbResponse(event.newState.programCounter.load().value());
return;
}
if (executionState == TargetExecutionState::RUNNING || executionState == TargetExecutionState::STEPPING) {
this->handleTargetResumedGdbResponse();
return;
}
} catch (const ClientDisconnected&) {
@@ -335,41 +344,12 @@ namespace DebugServer::Gdb
// Server was interrupted
Logger::debug("GDB RSP interrupted");
return;
} catch (const Exception& exception) {
Logger::error("Failed to handle target execution stopped event - " + exception.getMessage());
}
}
void GdbRspDebugServer::onTargetExecutionResumed(const Events::TargetExecutionResumed&) {
auto* debugSession = this->getActiveDebugSession();
try {
if (debugSession != nullptr) {
this->handleTargetResumedGdbResponse();
}
} catch (const ClientDisconnected&) {
Logger::info("GDB RSP client disconnected");
this->endDebugSession();
return;
} catch (const ClientCommunicationError& exception) {
Logger::error(
"GDB RSP client communication error - " + exception.getMessage() + " - closing connection"
);
this->endDebugSession();
return;
} catch (const DebugServerInterrupted&) {
// Server was interrupted
Logger::debug("GDB RSP interrupted");
return;
} catch (const Exception& exception) {
Logger::error("Failed to handle target execution resumed event - " + exception.getMessage());
}
}
void GdbRspDebugServer::handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress) {
auto* debugSession = this->getActiveDebugSession();
@@ -377,7 +357,7 @@ namespace DebugServer::Gdb
debugSession->terminateRangeSteppingSession(this->targetControllerService);
}
debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::TRAP));
debugSession->connection.writePacket(ResponsePackets::TargetStopped{Signal::TRAP});
debugSession->waitingForBreak = false;
}
@@ -392,7 +372,7 @@ namespace DebugServer::Gdb
debugSession->terminateRangeSteppingSession(this->targetControllerService);
}
debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::INTERRUPTED));
debugSession->connection.writePacket(ResponsePackets::TargetStopped{Signal::INTERRUPTED});
debugSession->pendingInterrupt = false;
debugSession->waitingForBreak = false;
}

View File

@@ -15,6 +15,7 @@
#include "src/Helpers/EpollInstance.hpp"
#include "src/Helpers/EventFdNotifier.hpp"
#include "src/Services/TargetControllerService.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "Connection.hpp"
#include "TargetDescriptor.hpp"
@@ -24,8 +25,7 @@
#include "Feature.hpp"
#include "CommandPackets/CommandPacket.hpp"
#include "src/EventManager/Events/TargetExecutionStopped.hpp"
#include "src/EventManager/Events/TargetExecutionResumed.hpp"
#include "src/EventManager/Events/TargetStateChanged.hpp"
namespace DebugServer::Gdb
{
@@ -43,6 +43,7 @@ namespace DebugServer::Gdb
public:
explicit GdbRspDebugServer(
const DebugServerConfig& debugServerConfig,
const Targets::TargetDescriptor& targetDescriptor,
EventListener& eventListener,
EventFdNotifier& eventNotifier
);
@@ -78,14 +79,10 @@ namespace DebugServer::Gdb
void run() override;
protected:
/**
* User project configuration specific to the GDB RSP debug server.
*/
GdbDebugServerConfig debugServerConfig;
/**
* The DebugServerComponent's event listener.
*/
const Targets::TargetDescriptor& targetDescriptor;
EventListener& eventListener;
/**
@@ -188,17 +185,7 @@ namespace DebugServer::Gdb
*/
virtual const TargetDescriptor& getGdbTargetDescriptor() = 0;
/**
* If the GDB client is currently waiting for the target execution to stop, this event handler will issue
* a "stop reply" packet to the client once the target execution stops.
*/
void onTargetExecutionStopped(const Events::TargetExecutionStopped& stoppedEvent);
/**
* Services any pending interrupts.
*/
void onTargetExecutionResumed(const Events::TargetExecutionResumed&);
void onTargetStateChanged(const Events::TargetStateChanged& event);
virtual void handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress);
virtual void handleTargetResumedGdbResponse();
};

View File

@@ -32,65 +32,6 @@ namespace DebugServer::Gdb
Packet& operator = (const Packet& other) = default;
Packet& operator = (Packet&& other) = default;
/**
* Generates a raw packet.
*
* @return
*/
[[nodiscard]] RawPacket toRawPacket() const {
std::vector<unsigned char> packet = {'$'};
for (const auto& byte : this->data) {
// Escape $ and # characters
switch (byte) {
case '$':
case '#': {
packet.push_back('}');
packet.push_back(byte ^ 0x20);
}
default: {
packet.push_back(byte);
}
}
}
auto dataSum = std::accumulate(packet.begin() + 1, packet.end(), 0);
packet.push_back('#');
auto checkSum = QStringLiteral("%1").arg(dataSum % 256, 2, 16, QLatin1Char('0')).toStdString();
if (checkSum.size() < 2) {
packet.push_back('0');
if (checkSum.size() < 1) {
packet.push_back('0');
} else {
packet.push_back(static_cast<unsigned char>(checkSum[0]));
}
}
packet.push_back(static_cast<unsigned char>(checkSum[0]));
packet.push_back(static_cast<unsigned char>(checkSum[1]));
return packet;
}
/**
* Converts data in hexadecimal form to raw data.
*
* @param hexData
* @return
*/
static std::vector<unsigned char> hexToData(const std::string& hexData) {
std::vector<unsigned char> output;
for (auto i = 0; i < hexData.size(); i += 2) {
auto hexByte = std::string((hexData.begin() + i), (hexData.begin() + i + 2));
output.push_back(static_cast<unsigned char>(std::stoi(hexByte, nullptr, 16)));
}
return output;
}
protected:
std::vector<unsigned char> data;

View File

@@ -15,7 +15,7 @@ namespace DebugServer::Gdb
*/
struct ProgrammingSession
{
Targets::TargetMemoryAddress startAddress = 0x00;
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemoryBuffer buffer;
ProgrammingSession(

View File

@@ -1,7 +1,6 @@
#pragma once
#include <cstdint>
#include <string>
namespace DebugServer::Gdb
{
@@ -10,19 +9,17 @@ namespace DebugServer::Gdb
/*
* GDB defines a set of registers for each target architecture.
*
* Each register in the set is assigned an ID, which is used to identify the registers. Although the mapping of
* Each register in the set is assigned an ID, which is used to identify the register. Although the mapping of
* registers to IDs is hardcoded in GDB, GDB server implementations are expected to be aware of this mapping.
*/
struct RegisterDescriptor
{
GdbRegisterId id;
std::uint16_t size;
std::string name;
RegisterDescriptor(GdbRegisterId id, std::uint16_t size, const std::string& name)
RegisterDescriptor(GdbRegisterId id, std::uint16_t size)
: id(id)
, size(size)
, name(name)
{};
bool operator == (const RegisterDescriptor& other) const {
@@ -50,22 +47,3 @@ namespace DebugServer::Gdb
}
};
}
namespace std
{
/**
* Hashing function for RegisterDescriptor type.
*
* This is required in order to use RegisterDescriptor as a key in an std::unordered_map (see the BiMap
* class).
*/
template<>
class hash<DebugServer::Gdb::RegisterDescriptor>
{
public:
std::size_t operator () (const 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.id);
}
};
}

View File

@@ -4,10 +4,6 @@ Supported Bloom commands:
version Outputs Bloom's version information.
version machine Outputs Bloom's version information in JSON format.
@ACTIVATE_INSIGHT_HELP_TEXT@
svd Generates the System View Description (SVD) XML for the current target and saves it into a
file located in the current project directory.
svd --out Generates the System View Description (SVD) XML for the current target and sends it to GDB, as
command output.
reset Resets the target and holds it in a stopped state.

View File

@@ -0,0 +1,41 @@
#include "ResponsePacket.hpp"
namespace DebugServer::Gdb::ResponsePackets
{
RawPacket ResponsePacket::toRawPacket() const {
std::vector<unsigned char> packet = {'$'};
for (const auto& byte : this->data) {
// Escape $ and # characters
switch (byte) {
case '$':
case '#': {
packet.push_back('}');
packet.push_back(byte ^ 0x20);
}
default: {
packet.push_back(byte);
}
}
}
auto dataSum = std::accumulate(packet.begin() + 1, packet.end(), 0);
packet.push_back('#');
auto checkSum = QStringLiteral("%1").arg(dataSum % 256, 2, 16, QLatin1Char{'0'}).toStdString();
if (checkSum.size() < 2) {
packet.push_back('0');
if (checkSum.size() < 1) {
packet.push_back('0');
} else {
packet.push_back(static_cast<unsigned char>(checkSum[0]));
}
}
packet.push_back(static_cast<unsigned char>(checkSum[0]));
packet.push_back(static_cast<unsigned char>(checkSum[1]));
return packet;
}
}

View File

@@ -20,7 +20,14 @@ namespace DebugServer::Gdb::ResponsePackets
}
explicit ResponsePacket(const std::string& data) {
this->data = std::vector<unsigned char>(data.begin(), data.end());
this->data = std::vector<unsigned char>{data.begin(), data.end()};
}
/**
* Generates a raw packet.
*
* @return
*/
[[nodiscard]] RawPacket toRawPacket() const;
};
}

View File

@@ -7,7 +7,7 @@ namespace DebugServer::Gdb::ResponsePackets
)
: supportedFeatures(supportedFeatures)
{
auto output = std::string("qSupported:");
auto output = std::string{"qSupported:"};
static const auto gdbFeatureMapping = getGdbFeatureToNameMapping();
for (const auto& supportedFeature : this->supportedFeatures) {

View File

@@ -25,7 +25,9 @@ namespace DebugServer::Gdb::ResponsePackets
: signal(signal)
, stopReason(stopReason)
{
std::string packetData = "T" + Services::StringService::toHex(static_cast<unsigned char>(this->signal));
auto packetData = std::string{"T"} + Services::StringService::toHex(
static_cast<unsigned char>(this->signal)
);
if (this->stopReason.has_value()) {
static const auto stopReasonMapping = getStopReasonToNameMapping();

View File

@@ -11,9 +11,9 @@ namespace DebugServer::Gdb
};
static inline BiMap<StopReason, std::string> getStopReasonToNameMapping() {
return BiMap<StopReason, std::string>({
return BiMap<StopReason, std::string>{
{StopReason::HARDWARE_BREAKPOINT, "hwbreak"},
{StopReason::SOFTWARE_BREAKPOINT, "swbreak"},
});
};
}
}

View File

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

View File

@@ -9,85 +9,61 @@
#include "src/Helpers/BiMap.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "RegisterDescriptor.hpp"
namespace DebugServer::Gdb
{
using GdbMemoryAddress = std::uint32_t;
/**
* GDB target descriptor.
*/
class TargetDescriptor
{
public:
Targets::TargetDescriptor targetDescriptor;
std::map<GdbRegisterId, RegisterDescriptor> gdbRegisterDescriptorsById;
explicit 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
);
std::map<GdbRegisterId, const Targets::TargetRegisterDescriptor*> targetRegisterDescriptorsByGdbId;
virtual ~TargetDescriptor() = default;
std::uint32_t getMemoryOffset(Targets::TargetMemoryType memoryType) const;
/**
* Helper method to extract the target memory type (Flash, RAM, etc) from a GDB memory address.
* For targets with multiple address spaces (e.g. AVR), GDB encodes address space information into memory
* addresses, by applying a mask.
*
* This function should identify the encoded address space within a GDB memory address, and return the
* relevant address space descriptor.
*
* @param address
* @return
*/
Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) const;
virtual const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptorFromGdbAddress(
GdbMemoryAddress address
) const = 0;
/**
* 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.
* This function should translate a GDB memory address to a target memory address. This should strip any
* GDB-specific masks and return an address that can be used within Bloom.
*
* @param registerDescriptorId
* @param address
* @return
*/
std::optional<GdbRegisterId> getGdbRegisterIdFromTargetRegisterDescriptorId(
Targets::TargetRegisterDescriptorId targetRegisterDescriptorId
) const;
virtual Targets::TargetMemoryAddress translateGdbAddress(GdbMemoryAddress address) const = 0;
/**
* Should retrieve the mapped target register descriptor ID for a given GDB register ID.
* This function should translate a target memory address to a GDB memory address. It should encode any
* additional data expected by GDB.
*
* 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
* @param address
* @param addressSpaceDescriptor
* @param memorySegmentDescriptor
* @return
*/
std::optional<Targets::TargetRegisterDescriptorId> getTargetRegisterDescriptorIdFromGdbRegisterId(
GdbRegisterId gdbRegisterId
) const;
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
* 0x00800000. Flash addresses are left unchanged. EEPROM addressing is not supported in GDB (for AVR targets).
*
* memoryOffsetsByType is a mapping of memory types to these known constants (which we're calling offsets).
* Because these offsets vary by target, the mapping lives here, in the GDB target descriptor.
*/
BiMap<Targets::TargetMemoryType, std::uint32_t> memoryOffsetsByType;
/**
* 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;
virtual GdbMemoryAddress translateTargetMemoryAddress(
Targets::TargetMemoryAddress address,
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
) const = 0;
};
}

View File

@@ -7,6 +7,8 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/CmsisDapInterface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Command.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Response.cpp
# Microchip EDBG implementation
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrCommand.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp
@@ -27,6 +29,8 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp
# Microchip EDBG debug tools
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/EdbgDevice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AtmelICE/AtmelIce.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/PowerDebugger/PowerDebugger.cpp
@@ -37,7 +41,12 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedNano/XplainedNano.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp
# RISC-V debug tools
${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkInterface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp
# RISC-V Debug Translator implementation
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/RiscVDebugSpec/DebugTranslator.cpp
)

View File

@@ -2,14 +2,16 @@
#include "TargetInterfaces/TargetPowerManagementInterface.hpp"
#include "TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp"
#include "TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp"
#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp"
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp"
#include "TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp"
#include "TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp"
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
#include "src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp"
#include "TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
#include "TargetInterfaces/RiscV/RiscVProgramInterface.hpp"
#include "TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp"
#include "src/Targets/RiscV/TargetDescriptionFile.hpp"
#include "src/Targets/RiscV/RiscVTargetConfig.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
@@ -44,6 +46,8 @@ public:
*/
virtual void close() = 0;
virtual bool isInitialised() const = 0;
virtual std::string getName() = 0;
virtual std::string getSerialNumber() = 0;
@@ -75,11 +79,9 @@ public:
*
* @return
*/
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
) {
return nullptr;
}
@@ -94,8 +96,9 @@ public:
*
* @return
*/
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface(
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface(
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
) {
return nullptr;
}
@@ -110,7 +113,10 @@ public:
*
* @return
*/
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface() {
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface(
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
const Targets::RiscV::RiscVTargetConfig& targetConfig
) {
return nullptr;
}
@@ -127,19 +133,31 @@ public:
*
* @return
*/
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface() {
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface(
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
const Targets::RiscV::RiscVTargetConfig& targetConfig
) {
return nullptr;
}
[[nodiscard]] bool isInitialised() const {
return this->initialised;
/**
* The RISC-V debug spec does not define a target ID. But vendors typically assign each model with an ID and
* provide a means to extract it from the connected target, via the debug tool.
*
* For example, WCH debug tools return the target ID in response to the target activation command. For more, see
* the implementation of the WCH-Link protocol.
*
* Bloom uses the target ID for verification purposes. We simply compare it to the one we have in the TDF and shout
* if they don't match.
*
* Note: the caller of this function will not manage the lifetime of the returned instance.
*
* @return
*/
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVIdentificationInterface* getRiscVIdentificationInterface(
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
const Targets::RiscV::RiscVTargetConfig& targetConfig
) {
return nullptr;
}
protected:
void setInitialised(bool initialised) {
this->initialised = initialised;
}
private:
bool initialised = false;
};

View File

@@ -38,14 +38,14 @@ namespace DebugToolDrivers::Microchip
this->setConfiguration(this->configurationIndex.value());
}
auto cmsisHidInterface = Usb::HidInterface(
auto cmsisHidInterface = Usb::HidInterface{
this->cmsisHidInterfaceNumber,
this->getEndpointMaxPacketSize(
this->getFirstEndpointAddress(this->cmsisHidInterfaceNumber, LIBUSB_ENDPOINT_IN)
),
this->vendorId,
this->productId
);
};
cmsisHidInterface.init();
@@ -57,7 +57,7 @@ namespace DebugToolDrivers::Microchip
* Because of this, we have to enforce a minimum time gap between commands. See comment
* in CmsisDapInterface class declaration for more info.
*/
this->edbgInterface->setMinimumCommandTimeGap(std::chrono::milliseconds(35));
this->edbgInterface->setMinimumCommandTimeGap(std::chrono::milliseconds{35});
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
@@ -70,9 +70,7 @@ namespace DebugToolDrivers::Microchip
);
}
this->edbgAvrIspInterface = std::make_unique<EdbgAvrIspInterface>(this->edbgInterface.get());
this->setInitialised(true);
this->initialised = true;
}
void EdbgDevice::close() {
@@ -82,21 +80,50 @@ namespace DebugToolDrivers::Microchip
this->edbgInterface->getUsbHidInterface().close();
UsbDevice::close();
this->initialised = false;
}
TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface(
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
bool EdbgDevice::isInitialised() const {
return this->initialised;
}
std::string EdbgDevice::getSerialNumber() {
using namespace CommandFrames::Discovery;
using ResponseFrames::Discovery::ResponseId;
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
Query{QueryContext::SERIAL_NUMBER}
);
if (responseFrame.id != ResponseId::OK) {
throw DeviceInitializationFailure{
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
};
}
const auto data = responseFrame.getPayloadData();
return std::string{data.begin(), data.end()};
}
std::string EdbgDevice::getFirmwareVersionString() {
// TODO: Implement this
return "UNKNOWN";
}
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* EdbgDevice::getTargetPowerManagementInterface()
{
return this->targetPowerManagementInterface.get();
}
TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface(
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
) {
if (this->edbgAvr8Interface == nullptr) {
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(
this->edbgInterface.get(),
targetConfig,
targetFamily,
targetParameters,
targetRegisterDescriptorsById
targetDescriptionFile,
targetConfig
);
this->configureAvr8Interface();
@@ -105,40 +132,30 @@ namespace DebugToolDrivers::Microchip
return this->edbgAvr8Interface.get();
}
std::string EdbgDevice::getSerialNumber() {
using namespace CommandFrames::Discovery;
using ResponseFrames::Discovery::ResponseId;
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (responseFrame.id != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
TargetInterfaces::Microchip::Avr8::AvrIspInterface* EdbgDevice::getAvrIspInterface(
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
) {
if (this->edbgAvrIspInterface == nullptr) {
this->edbgAvrIspInterface = std::make_unique<EdbgAvrIspInterface>(
this->edbgInterface.get(),
targetDescriptionFile
);
this->configureAvr8Interface();
}
const auto data = responseFrame.getPayloadData();
return std::string(data.begin(), data.end());
}
std::string EdbgDevice::getFirmwareVersionString() {
// TODO: Implement this
return "UNKNOWN";
return this->edbgAvrIspInterface.get();
}
void EdbgDevice::startSession() {
using namespace CommandFrames::HouseKeeping;
using ResponseFrames::HouseKeeping::ResponseId;
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(StartSession{});
if (responseFrame.id == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with EDBG device!");
throw DeviceInitializationFailure{"Failed to start session with EDBG device!"};
}
this->sessionStarted = true;
@@ -148,13 +165,10 @@ namespace DebugToolDrivers::Microchip
using namespace CommandFrames::HouseKeeping;
using ResponseFrames::HouseKeeping::ResponseId;
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(EndSession{});
if (responseFrame.id == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with EDBG device!");
throw DeviceFailure{"Failed to end session with EDBG device!"};
}
this->sessionStarted = false;

View File

@@ -50,22 +50,7 @@ namespace DebugToolDrivers::Microchip
*/
void close() override;
TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
) override;
TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface(
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
) override {
return this->edbgAvrIspInterface.get();
}
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* getTargetPowerManagementInterface() override {
return this->targetPowerManagementInterface.get();
}
[[nodiscard]] bool isInitialised() const override;
/**
* Retrieves the device serial number via the "Discovery" EDBG sub-protocol.
@@ -81,6 +66,18 @@ namespace DebugToolDrivers::Microchip
*/
std::string getFirmwareVersionString() override;
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* getTargetPowerManagementInterface() override;
TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
) override;
TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface(
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
) override;
/**
* Starts a session with the EDBG device using the "Housekeeping" EDBG sub-protocol.
*/
@@ -92,6 +89,8 @@ namespace DebugToolDrivers::Microchip
void endSession();
protected:
bool initialised = false;
/**
* The USB interface number of the CMSIS-DAP HID interface.
*/
@@ -136,9 +135,9 @@ namespace DebugToolDrivers::Microchip
* ISP cannot be used for debugging operations. The EdbgAvrIspInterface class does *not* implement
* the Avr8DebugInterface.
*
* Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWire
* targets. We use the interface to inspect and update the "debugWire enable" (DWEN) fuse-bit, before making a
* second connection attempt via the debugWire interface.
* Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWIRE
* targets. We use the interface to inspect and update the "debugWIRE enable" (DWEN) fuse-bit, before making a
* second connection attempt via the debugWIRE interface.
*/
std::unique_ptr<Microchip::Protocols::Edbg::Avr::EdbgAvrIspInterface> edbgAvrIspInterface = nullptr;

View File

@@ -30,10 +30,10 @@ namespace DebugToolDrivers::Microchip
);
if (!nonEdbgDevices.empty()) {
throw DeviceNotFound(
throw DeviceNotFound{
"The connected MPLAB PICkit 4 device is not in \"AVR mode\". Please follow the instructions at "
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
);
};
}
throw exception;

View File

@@ -38,10 +38,10 @@ namespace DebugToolDrivers::Microchip
}
if (!nonEdbgDevices.empty()) {
throw DeviceNotFound(
throw DeviceNotFound{
"The connected MPLAB Snap device is not in \"AVR mode\". Please follow the instructions at "
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
);
};
}
throw exception;

Some files were not shown because too many files have changed in this diff Show More