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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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 . ')';
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp
Normal file
37
src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
)));
|
||||
}
|
||||
)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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()
|
||||
)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace DebugServer::Gdb::Exceptions
|
||||
class DebugServerInterrupted: public ::Exceptions::Exception
|
||||
{
|
||||
public:
|
||||
explicit DebugServerInterrupted() = default;
|
||||
DebugServerInterrupted() = default;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace DebugServer::Gdb
|
||||
*/
|
||||
struct ProgrammingSession
|
||||
{
|
||||
Targets::TargetMemoryAddress startAddress = 0x00;
|
||||
Targets::TargetMemoryAddress startAddress;
|
||||
Targets::TargetMemoryBuffer buffer;
|
||||
|
||||
ProgrammingSession(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
41
src/DebugServer/Gdb/ResponsePackets/ResponsePacket.cpp
Normal file
41
src/DebugServer/Gdb/ResponsePackets/ResponsePacket.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"},
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user