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 ?string $key = null;
|
||||||
public ?int $startAddress = null;
|
public ?int $startAddress = null;
|
||||||
public ?int $size = null;
|
public ?int $size = null;
|
||||||
|
public ?int $unitSize = null;
|
||||||
public ?string $endianness = null;
|
public ?string $endianness = null;
|
||||||
|
|
||||||
/** @var MemorySegment[] */
|
/** @var MemorySegment[] */
|
||||||
public array $memorySegments = [];
|
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->key = $key;
|
||||||
$this->startAddress = $startAddress;
|
$this->startAddress = $startAddress;
|
||||||
$this->size = $size;
|
$this->size = $size;
|
||||||
|
$this->unitSize = $unitSize;
|
||||||
$this->endianness = $endianness;
|
$this->endianness = $endianness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,10 +93,7 @@ class MemorySegment
|
|||||||
&& $endAddress !== null
|
&& $endAddress !== null
|
||||||
&& $other->startAddress !== null
|
&& $other->startAddress !== null
|
||||||
&& $otherEndAddress !== null
|
&& $otherEndAddress !== null
|
||||||
&& (
|
&& $this->startAddress <= $otherEndAddress && $other->startAddress <= $endAddress
|
||||||
($other->startAddress <= $this->startAddress && $otherEndAddress >= $this->startAddress)
|
|
||||||
|| ($other->startAddress >= $this->startAddress && $other->startAddress <= $endAddress)
|
|
||||||
)
|
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -323,6 +323,7 @@ class AtdfService
|
|||||||
isset($attributes['id']) ? strtolower($attributes['id']) : null,
|
isset($attributes['id']) ? strtolower($attributes['id']) : null,
|
||||||
$this->stringService->tryStringToInt($attributes['start'] ?? null),
|
$this->stringService->tryStringToInt($attributes['start'] ?? null),
|
||||||
$this->stringService->tryStringToInt($attributes['size'] ?? null),
|
$this->stringService->tryStringToInt($attributes['size'] ?? null),
|
||||||
|
null,
|
||||||
$attributes['endianness'] ?? null,
|
$attributes['endianness'] ?? null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ class ValidationService
|
|||||||
if ($register->size === null) {
|
if ($register->size === null) {
|
||||||
$failures[] = 'Missing size';
|
$failures[] = 'Missing size';
|
||||||
|
|
||||||
} elseif ($register->size < 1) {
|
} elseif ($register->size < 1 || $register->size > 8) {
|
||||||
$failures[] = 'Invalid size (' . $register->size . ')';
|
$failures[] = 'Invalid size (' . $register->size . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ class FromXmlService
|
|||||||
$attributes['key'] ?? null,
|
$attributes['key'] ?? null,
|
||||||
$this->stringService->tryStringToInt($attributes['start'] ?? null),
|
$this->stringService->tryStringToInt($attributes['start'] ?? null),
|
||||||
$this->stringService->tryStringToInt($attributes['size'] ?? null),
|
$this->stringService->tryStringToInt($attributes['size'] ?? null),
|
||||||
|
$this->stringService->tryStringToInt($attributes['unit-size'] ?? null),
|
||||||
$attributes['endianness'] ?? null,
|
$attributes['endianness'] ?? null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ class ToXmlService
|
|||||||
$element->setAttribute('start', $this->stringService->tryIntToHex($addressSpace->startAddress, 8));
|
$element->setAttribute('start', $this->stringService->tryIntToHex($addressSpace->startAddress, 8));
|
||||||
$element->setAttribute('size', $addressSpace->size);
|
$element->setAttribute('size', $addressSpace->size);
|
||||||
|
|
||||||
|
if (!empty($addressSpace->unitSize)) {
|
||||||
|
$element->setAttribute('unit-size', $addressSpace->unitSize);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($addressSpace->endianness)) {
|
if (!empty($addressSpace->endianness)) {
|
||||||
$element->setAttribute('endianness', strtolower($addressSpace->endianness));
|
$element->setAttribute('endianness', strtolower($addressSpace->endianness));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
#include "src/Services/ProcessService.hpp"
|
#include "src/Services/ProcessService.hpp"
|
||||||
#include "src/Helpers/BiMap.hpp"
|
#include "src/Helpers/BiMap.hpp"
|
||||||
|
|
||||||
#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp"
|
|
||||||
|
|
||||||
#include "src/Exceptions/InvalidConfig.hpp"
|
#include "src/Exceptions/InvalidConfig.hpp"
|
||||||
|
|
||||||
using namespace Exceptions;
|
using namespace Exceptions;
|
||||||
@@ -75,12 +73,11 @@ int Application::run() {
|
|||||||
this->checkBloomVersion();
|
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
|
* We use a QTimer to dispatch our events on an interval.
|
||||||
* process our events. To address this, 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.
|
* 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);
|
QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Application::dispatchEvents);
|
||||||
eventDispatchTimer->start(100);
|
eventDispatchTimer->start(100);
|
||||||
|
|
||||||
@@ -225,17 +222,15 @@ void Application::triggerShutdown() {
|
|||||||
|
|
||||||
void Application::loadProjectSettings() {
|
void Application::loadProjectSettings() {
|
||||||
const auto projectSettingsPath = Services::PathService::projectSettingsPath();
|
const auto projectSettingsPath = Services::PathService::projectSettingsPath();
|
||||||
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
|
auto jsonSettingsFile = QFile{QString::fromStdString(projectSettingsPath)};
|
||||||
|
|
||||||
if (jsonSettingsFile.exists()) {
|
if (jsonSettingsFile.exists()) {
|
||||||
try {
|
try {
|
||||||
if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
throw Exception("Failed to open settings file.");
|
throw Exception{"Failed to open settings file."};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->projectSettings = ProjectSettings(
|
this->projectSettings = ProjectSettings{QJsonDocument::fromJson(jsonSettingsFile.readAll()).object()};
|
||||||
QJsonDocument::fromJson(jsonSettingsFile.readAll()).object()
|
|
||||||
);
|
|
||||||
jsonSettingsFile.close();
|
jsonSettingsFile.close();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -256,45 +251,43 @@ void Application::saveProjectSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto projectSettingsPath = Services::PathService::projectSettingsPath();
|
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);
|
Logger::debug("Saving project settings to " + projectSettingsPath);
|
||||||
|
|
||||||
QDir().mkpath(QString::fromStdString(Services::PathService::projectSettingsDirPath()));
|
QDir{}.mkpath(QString::fromStdString(Services::PathService::projectSettingsDirPath()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auto jsonDocument = QJsonDocument(this->projectSettings->toJson());
|
const auto jsonDocument = QJsonDocument{this->projectSettings->toJson()};
|
||||||
|
|
||||||
if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
|
if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
|
||||||
throw Exception(
|
throw Exception{
|
||||||
"Failed to open/create settings file (" + projectSettingsPath + "). Check file permissions."
|
"Failed to open/create settings file (" + projectSettingsPath + "). Check file permissions."
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonSettingsFile.write(jsonDocument.toJson());
|
jsonSettingsFile.write(jsonDocument.toJson());
|
||||||
jsonSettingsFile.close();
|
jsonSettingsFile.close();
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error(
|
Logger::error("Failed to save project settings - " + exception.getMessage());
|
||||||
"Failed to save project settings - " + exception.getMessage()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::loadProjectConfiguration() {
|
void Application::loadProjectConfiguration() {
|
||||||
auto configFile = QFile(QString::fromStdString(Services::PathService::projectConfigPath()));
|
auto configFile = QFile{QString::fromStdString(Services::PathService::projectConfigPath())};
|
||||||
|
|
||||||
if (!configFile.exists()) {
|
if (!configFile.exists()) {
|
||||||
throw Exception(
|
throw Exception{
|
||||||
"Bloom configuration file (bloom.yaml) not found. Working directory: "
|
"Bloom configuration file (bloom.yaml) not found. Working directory: "
|
||||||
+ Services::PathService::projectDirPath()
|
+ Services::PathService::projectDirPath()
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
throw InvalidConfig(
|
throw InvalidConfig{
|
||||||
"Failed to open Bloom configuration file. Working directory: " + Services::PathService::projectDirPath()
|
"Failed to open Bloom configuration file. Working directory: " + Services::PathService::projectDirPath()
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -310,9 +303,9 @@ void Application::loadProjectConfiguration() {
|
|||||||
// Validate the selected environment
|
// Validate the selected environment
|
||||||
const auto selectedEnvironmentIt = this->projectConfig->environments.find(this->selectedEnvironmentName);
|
const auto selectedEnvironmentIt = this->projectConfig->environments.find(this->selectedEnvironmentName);
|
||||||
if (selectedEnvironmentIt == this->projectConfig->environments.end()) {
|
if (selectedEnvironmentIt == this->projectConfig->environments.end()) {
|
||||||
throw InvalidConfig(
|
throw InvalidConfig{
|
||||||
"Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration."
|
"Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration."
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->environmentConfig = selectedEnvironmentIt->second;
|
this->environmentConfig = selectedEnvironmentIt->second;
|
||||||
@@ -328,7 +321,7 @@ void Application::loadProjectConfiguration() {
|
|||||||
this->debugServerConfig = this->projectConfig->debugServerConfig.value();
|
this->debugServerConfig = this->projectConfig->debugServerConfig.value();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw InvalidConfig("Debug server configuration missing.");
|
throw InvalidConfig{"Debug server configuration missing."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,18 +333,20 @@ int Application::presentHelpText() {
|
|||||||
Logger::silence();
|
Logger::silence();
|
||||||
|
|
||||||
// The file help.txt is included in Bloom's binary, as a resource. See the root-level CMakeLists.txt for more.
|
// 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)) {
|
if (!helpFile.open(QIODevice::ReadOnly)) {
|
||||||
// This should never happen - if it does, something has gone very wrong
|
// 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()
|
"Failed to open help file - please report this issue at " + Services::PathService::homeDomainName()
|
||||||
+ "/report-issue"
|
+ "/report-issue"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Bloom v" << Application::VERSION.toString() << "\n";
|
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;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,14 +371,14 @@ int Application::presentVersionText() {
|
|||||||
int Application::presentVersionMachineText() {
|
int Application::presentVersionMachineText() {
|
||||||
Logger::silence();
|
Logger::silence();
|
||||||
|
|
||||||
std::cout << QJsonDocument(QJsonObject({
|
std::cout << QJsonDocument{QJsonObject{
|
||||||
{"version", QString::fromStdString(Application::VERSION.toString())},
|
{"version", QString::fromStdString(Application::VERSION.toString())},
|
||||||
{"components", QJsonObject({
|
{"components", QJsonObject({
|
||||||
{"major", Application::VERSION.major},
|
{"major", Application::VERSION.major},
|
||||||
{"minor", Application::VERSION.minor},
|
{"minor", Application::VERSION.minor},
|
||||||
{"patch", Application::VERSION.patch},
|
{"patch", Application::VERSION.patch},
|
||||||
})},
|
})},
|
||||||
})).toJson().toStdString();
|
}}.toJson().toStdString();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -393,38 +388,38 @@ int Application::presentCapabilitiesMachine() {
|
|||||||
|
|
||||||
Logger::silence();
|
Logger::silence();
|
||||||
|
|
||||||
static const auto targetFamilyNames = BiMap<TargetFamily, QString>({
|
static const auto targetFamilyNames = BiMap<TargetFamily, QString>{
|
||||||
{TargetFamily::AVR_8, "AVR8"},
|
{TargetFamily::AVR_8, "AVR8"},
|
||||||
{TargetFamily::RISC_V, "RISC-V"},
|
{TargetFamily::RISC_V, "RISC-V"},
|
||||||
});
|
};
|
||||||
|
|
||||||
auto supportedTargets = QJsonArray();
|
auto supportedTargets = QJsonArray{};
|
||||||
|
|
||||||
for (const auto& [configValue, descriptor] : Services::TargetService::briefDescriptorsByConfigValue()) {
|
for (const auto& [configValue, descriptor] : Services::TargetService::briefDescriptorsByConfigValue()) {
|
||||||
supportedTargets.push_back(QJsonObject({
|
supportedTargets.push_back(QJsonObject{
|
||||||
{"name" , QString::fromStdString(descriptor.name)},
|
{"name" , QString::fromStdString(descriptor.name)},
|
||||||
{"family" , targetFamilyNames.at(descriptor.family)},
|
{"family" , targetFamilyNames.at(descriptor.family)},
|
||||||
{"configurationValue" , QString::fromStdString(configValue)},
|
{"configurationValue" , QString::fromStdString(configValue)},
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << QJsonDocument(QJsonObject({
|
std::cout << QJsonDocument{QJsonObject{
|
||||||
{"targets", supportedTargets},
|
{"targets", supportedTargets},
|
||||||
#ifndef EXCLUDE_INSIGHT
|
#ifndef EXCLUDE_INSIGHT
|
||||||
{"insight", true},
|
{"insight", true},
|
||||||
#else
|
#else
|
||||||
{"insight", false},
|
{"insight", false},
|
||||||
#endif
|
#endif
|
||||||
})).toJson().toStdString();
|
}}.toJson().toStdString();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Application::initProject() {
|
int Application::initProject() {
|
||||||
auto configFile = QFile(QString::fromStdString(Services::PathService::projectConfigPath()));
|
auto configFile = QFile{QString::fromStdString(Services::PathService::projectConfigPath())};
|
||||||
|
|
||||||
if (configFile.exists()) {
|
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.
|
* 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")
|
QString::fromStdString(Services::PathService::compiledResourcesPath()+ "/resources/bloom.template.yaml")
|
||||||
);
|
};
|
||||||
|
|
||||||
if (!templateConfigFile.open(QIODevice::ReadOnly)) {
|
if (!templateConfigFile.open(QIODevice::ReadOnly)) {
|
||||||
throw Exception(
|
throw Exception{
|
||||||
"Failed to open template configuration file - please report this issue at "
|
"Failed to open template configuration file - please report this issue at "
|
||||||
+ Services::PathService::homeDomainName() + "/report-issue"
|
+ Services::PathService::homeDomainName() + "/report-issue"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!configFile.open(QIODevice::ReadWrite)) {
|
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());
|
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
|
* 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.
|
* 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()) {
|
if (this->signalHandlerThread.joinable()) {
|
||||||
@@ -497,7 +492,7 @@ void Application::startTargetController() {
|
|||||||
>();
|
>();
|
||||||
|
|
||||||
if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) {
|
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) {
|
if (tcThreadState == ThreadState::STARTING || tcThreadState == ThreadState::READY) {
|
||||||
EventManager::triggerEvent(std::make_shared<Events::ShutdownTargetController>());
|
EventManager::triggerEvent(std::make_shared<Events::ShutdownTargetController>());
|
||||||
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>(
|
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->debugServer = std::make_unique<DebugServer::DebugServerComponent>(
|
||||||
this->debugServerConfig.value()
|
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<
|
const auto dsStateChangeEvent = this->applicationEventListener->waitForEvent<
|
||||||
Events::DebugServerThreadStateChanged
|
Events::DebugServerThreadStateChanged
|
||||||
>();
|
>();
|
||||||
|
|
||||||
if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) {
|
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) {
|
if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) {
|
||||||
EventManager::triggerEvent(std::make_shared<Events::ShutdownDebugServer>());
|
EventManager::triggerEvent(std::make_shared<Events::ShutdownDebugServer>());
|
||||||
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
|
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
|
||||||
std::chrono::milliseconds(5000)
|
std::chrono::milliseconds{5000}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,12 +558,14 @@ void Application::dispatchEvents() {
|
|||||||
void Application::checkBloomVersion() {
|
void Application::checkBloomVersion() {
|
||||||
const auto currentVersionNumber = Application::VERSION;
|
const auto currentVersionNumber = Application::VERSION;
|
||||||
|
|
||||||
auto* networkAccessManager = new QNetworkAccessManager(this);
|
auto* networkAccessManager = new QNetworkAccessManager{this};
|
||||||
auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Services::PathService::homeDomainName() + "/latest-version"));
|
auto queryVersionEndpointUrl = QUrl{
|
||||||
|
QString::fromStdString(Services::PathService::homeDomainName() + "/latest-version")
|
||||||
|
};
|
||||||
queryVersionEndpointUrl.setScheme("http");
|
queryVersionEndpointUrl.setScheme("http");
|
||||||
queryVersionEndpointUrl.setQuery(QUrlQuery({
|
queryVersionEndpointUrl.setQuery(QUrlQuery{
|
||||||
{"currentVersionNumber", QString::fromStdString(currentVersionNumber.toString())}
|
{"currentVersionNumber", QString::fromStdString(currentVersionNumber.toString())}
|
||||||
}));
|
});
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
networkAccessManager,
|
networkAccessManager,
|
||||||
@@ -591,7 +584,7 @@ void Application::checkBloomVersion() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl));
|
networkAccessManager->get(QNetworkRequest{queryVersionEndpointUrl});
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef EXCLUDE_INSIGHT
|
#ifndef EXCLUDE_INSIGHT
|
||||||
|
|||||||
@@ -43,15 +43,9 @@ class Application: public QObject, public Thread
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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);
|
explicit Application(std::vector<std::string>&& arguments);
|
||||||
|
|
||||||
/**
|
|
||||||
* Main entry-point for the Bloom program.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
int run();
|
int run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/GdbDebugServerConfig.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/GdbDebugServerConfig.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/Connection.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/Connection.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/DebugSession.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/ResponsePackets/SupportedFeaturesResponse.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/CommandPacket.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/CommandPacket.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/SupportedFeaturesQuery.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/HelpMonitorInfo.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.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/Detach.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/EepromFill.cpp
|
|
||||||
|
|
||||||
# AVR GDB RSP Server
|
# AVR GDB RSP Server
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp
|
${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/VContContinueExecution.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContStepExecution.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/VContRangeStep.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/EepromFill.cpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (NOT EXCLUDE_INSIGHT)
|
if (NOT EXCLUDE_INSIGHT)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace DebugServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (const std::exception& exception) {
|
} 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();
|
this->shutdown();
|
||||||
@@ -44,7 +44,7 @@ namespace DebugServer
|
|||||||
"avr-gdb-rsp",
|
"avr-gdb-rsp",
|
||||||
[this] () -> std::unique_ptr<ServerInterface> {
|
[this] () -> std::unique_ptr<ServerInterface> {
|
||||||
if (this->targetDescriptor.family != Targets::TargetFamily::AVR_8) {
|
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>(
|
return std::make_unique<DebugServer::Gdb::AvrGdb::AvrGdbRsp>(
|
||||||
@@ -75,7 +75,7 @@ namespace DebugServer
|
|||||||
const auto selectedServerIt = availableServersByName.find(this->debugServerConfig.name);
|
const auto selectedServerIt = availableServersByName.find(this->debugServerConfig.name);
|
||||||
|
|
||||||
if (selectedServerIt == availableServersByName.end()) {
|
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();
|
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();
|
this->shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace DebugServer
|
|||||||
/**
|
/**
|
||||||
* The current target descriptor.
|
* 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
|
* 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/VContStepExecution.hpp"
|
||||||
#include "CommandPackets/VContRangeStep.hpp"
|
#include "CommandPackets/VContRangeStep.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp"
|
||||||
|
#include "CommandPackets/EepromFill.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb
|
namespace DebugServer::Gdb::AvrGdb
|
||||||
{
|
{
|
||||||
using namespace Exceptions;
|
using namespace Exceptions;
|
||||||
@@ -30,7 +33,7 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
EventListener& eventListener,
|
EventListener& eventListener,
|
||||||
EventFdNotifier& eventNotifier
|
EventFdNotifier& eventNotifier
|
||||||
)
|
)
|
||||||
: GdbRspDebugServer(debugServerConfig, eventListener, eventNotifier)
|
: GdbRspDebugServer(debugServerConfig, targetDescriptor, eventListener, eventNotifier)
|
||||||
, gdbTargetDescriptor(targetDescriptor)
|
, gdbTargetDescriptor(targetDescriptor)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -38,7 +41,6 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
this->activeDebugSession.emplace(
|
this->activeDebugSession.emplace(
|
||||||
std::move(connection),
|
std::move(connection),
|
||||||
this->getSupportedFeatures(),
|
this->getSupportedFeatures(),
|
||||||
this->gdbTargetDescriptor,
|
|
||||||
this->debugServerConfig
|
this->debugServerConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -57,30 +59,34 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
return this->activeDebugSession.has_value() ? &*(this->activeDebugSession) : nullptr;
|
return this->activeDebugSession.has_value() ? &*(this->activeDebugSession) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket(
|
std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket(const RawPacket& rawPacket) {
|
||||||
const RawPacket& rawPacket
|
using Gdb::CommandPackets::Monitor;
|
||||||
) {
|
|
||||||
using AvrGdb::CommandPackets::ReadRegister;
|
using CommandPackets::ReadRegister;
|
||||||
using AvrGdb::CommandPackets::ReadRegisters;
|
using CommandPackets::ReadRegisters;
|
||||||
using AvrGdb::CommandPackets::WriteRegister;
|
using CommandPackets::WriteRegister;
|
||||||
using AvrGdb::CommandPackets::ReadMemory;
|
using CommandPackets::ReadMemory;
|
||||||
using AvrGdb::CommandPackets::WriteMemory;
|
using CommandPackets::WriteMemory;
|
||||||
using AvrGdb::CommandPackets::ReadMemoryMap;
|
using CommandPackets::ReadMemoryMap;
|
||||||
using AvrGdb::CommandPackets::FlashErase;
|
using CommandPackets::FlashErase;
|
||||||
using AvrGdb::CommandPackets::FlashWrite;
|
using CommandPackets::FlashWrite;
|
||||||
using AvrGdb::CommandPackets::FlashDone;
|
using CommandPackets::FlashDone;
|
||||||
using AvrGdb::CommandPackets::VContSupportedActionsQuery;
|
using CommandPackets::VContSupportedActionsQuery;
|
||||||
using AvrGdb::CommandPackets::VContContinueExecution;
|
using CommandPackets::VContContinueExecution;
|
||||||
using AvrGdb::CommandPackets::VContStepExecution;
|
using CommandPackets::VContStepExecution;
|
||||||
using AvrGdb::CommandPackets::VContRangeStep;
|
using CommandPackets::VContRangeStep;
|
||||||
|
using CommandPackets::EepromFill;
|
||||||
|
|
||||||
|
if (rawPacket.size() < 2) {
|
||||||
|
throw Exception{"Invalid raw packet - no data"};
|
||||||
|
}
|
||||||
|
|
||||||
if (rawPacket.size() >= 2) {
|
|
||||||
if (rawPacket[1] == 'p') {
|
if (rawPacket[1] == 'p') {
|
||||||
return std::make_unique<ReadRegister>(rawPacket);
|
return std::make_unique<ReadRegister>(rawPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacket[1] == 'g') {
|
if (rawPacket[1] == 'g') {
|
||||||
return std::make_unique<ReadRegisters>(rawPacket);
|
return std::make_unique<ReadRegisters>(rawPacket, this->gdbTargetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacket[1] == 'P') {
|
if (rawPacket[1] == 'P') {
|
||||||
@@ -95,14 +101,15 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
|
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rawPacketString = std::string(rawPacket.begin() + 1, rawPacket.end());
|
if (rawPacket.size() > 1) {
|
||||||
|
const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()};
|
||||||
|
|
||||||
if (rawPacketString.find("qXfer:memory-map:read::") == 0) {
|
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) {
|
if (rawPacketString.find("vFlashErase") == 0) {
|
||||||
return std::make_unique<FlashErase>(rawPacket);
|
return std::make_unique<FlashErase>(rawPacket, this->gdbTargetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacketString.find("vFlashWrite") == 0) {
|
if (rawPacketString.find("vFlashWrite") == 0) {
|
||||||
@@ -110,7 +117,7 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacketString.find("vFlashDone") == 0) {
|
if (rawPacketString.find("vFlashDone") == 0) {
|
||||||
return std::make_unique<FlashDone>(rawPacket);
|
return std::make_unique<FlashDone>(rawPacket, this->gdbTargetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacketString.find("vCont?") == 0) {
|
if (rawPacketString.find("vCont?") == 0) {
|
||||||
@@ -127,7 +134,19 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
|
|
||||||
if (this->debugServerConfig.rangeStepping) {
|
if (this->debugServerConfig.rangeStepping) {
|
||||||
if (rawPacketString.find("vCont;r") == 0) {
|
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));
|
Logger::debug("Attempting single step from 0x" + StringService::toHex(programAddress));
|
||||||
|
|
||||||
activeRangeSteppingSession->singleStepping = true;
|
activeRangeSteppingSession->singleStepping = true;
|
||||||
this->targetControllerService.stepTargetExecution(std::nullopt);
|
this->targetControllerService.stepTargetExecution();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,10 +216,7 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
Logger::debug("Continuing range stepping");
|
Logger::debug("Continuing range stepping");
|
||||||
|
|
||||||
activeRangeSteppingSession->singleStepping = false;
|
activeRangeSteppingSession->singleStepping = false;
|
||||||
this->targetControllerService.continueTargetExecution(
|
this->targetControllerService.resumeTargetExecution();
|
||||||
std::nullopt,
|
|
||||||
activeRangeSteppingSession->range.endAddress
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,9 +229,7 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
* We have to end the range stepping session and report the stop to GDB.
|
* 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");
|
Logger::debug("Target stopped within stepping range, but for an unknown reason");
|
||||||
|
|
||||||
this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService);
|
this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report the stop to GDB
|
// Report the stop to GDB
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp"
|
#include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
using Services::TargetControllerService;
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
@@ -22,47 +22,38 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
using ::Exceptions::Exception;
|
using ::Exceptions::Exception;
|
||||||
using Exceptions::InvalidCommandOption;
|
using Exceptions::InvalidCommandOption;
|
||||||
|
|
||||||
EepromFill::EepromFill(Monitor&& monitorPacket)
|
EepromFill::EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||||
: Monitor(std::move(monitorPacket))
|
: Monitor(std::move(monitorPacket))
|
||||||
|
, eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor)
|
||||||
|
, eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor)
|
||||||
{
|
{
|
||||||
const auto fillValueOptionIt = this->commandOptions.find("value");
|
const auto fillValueOptionIt = this->commandOptions.find("value");
|
||||||
|
if (fillValueOptionIt != this->commandOptions.end() && fillValueOptionIt->second.has_value()) {
|
||||||
if (fillValueOptionIt == this->commandOptions.end() || !fillValueOptionIt->second.has_value()) {
|
this->fillValue = Services::StringService::dataFromHex(*(fillValueOptionIt->second));
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fillValueByteArray = QByteArray::fromHex(QByteArray::fromStdString(*fillValueOptionIt->second));
|
void EepromFill::handle(
|
||||||
this->fillValue = Targets::TargetMemoryBuffer(fillValueByteArray.begin(), fillValueByteArray.end());
|
Gdb::DebugSession& debugSession,
|
||||||
}
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
void EepromFill::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
Logger::info("Handling EepromFill packet");
|
Logger::info("Handling EepromFill packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
|
const auto eepromSize = this->eepromMemorySegmentDescriptor.size();
|
||||||
|
|
||||||
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 fillValueSize = this->fillValue.size();
|
const auto fillValueSize = this->fillValue.size();
|
||||||
|
|
||||||
if (fillValueSize == 0) {
|
if (fillValueSize == 0) {
|
||||||
throw InvalidCommandOption("Fill value required");
|
throw InvalidCommandOption{"Fill value required"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fillValueSize > eepromSize) {
|
if (fillValueSize > eepromSize) {
|
||||||
throw InvalidCommandOption(
|
throw InvalidCommandOption{
|
||||||
"Fill value size (" + std::to_string(fillValueSize) + " bytes) exceeds EEPROM size ("
|
"Fill value size (" + std::to_string(fillValueSize) + " bytes) exceeds EEPROM size ("
|
||||||
+ std::to_string(eepromSize) + " bytes)"
|
+ std::to_string(eepromSize) + " bytes)"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((eepromSize % fillValueSize) != 0) {
|
if ((eepromSize % fillValueSize) != 0) {
|
||||||
@@ -74,7 +65,7 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM");
|
Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM");
|
||||||
|
|
||||||
auto data = Targets::TargetMemoryBuffer();
|
auto data = Targets::TargetMemoryBuffer{};
|
||||||
data.reserve(eepromSize);
|
data.reserve(eepromSize);
|
||||||
|
|
||||||
// Repeat this->fillValue until we've filled `data`
|
// Repeat this->fillValue until we've filled `data`
|
||||||
@@ -92,24 +83,25 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
Logger::debug("Filling EEPROM with values: " + hexValues);
|
Logger::debug("Filling EEPROM with values: " + hexValues);
|
||||||
|
|
||||||
targetControllerService.writeMemory(
|
targetControllerService.writeMemory(
|
||||||
Targets::TargetMemoryType::EEPROM,
|
this->eepromAddressSpaceDescriptor,
|
||||||
eepromDescriptor.addressRange.startAddress,
|
this->eepromMemorySegmentDescriptor,
|
||||||
|
this->eepromMemorySegmentDescriptor.addressRange.startAddress,
|
||||||
std::move(data)
|
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"
|
"Filled " + std::to_string(eepromSize) + " bytes of EEPROM, with values: " + hexValues + "\n"
|
||||||
)));
|
)});
|
||||||
|
|
||||||
} catch (const InvalidCommandOption& exception) {
|
} catch (const InvalidCommandOption& exception) {
|
||||||
Logger::error(exception.getMessage());
|
Logger::error(exception.getMessage());
|
||||||
debugSession.connection.writePacket(
|
debugSession.connection.writePacket(
|
||||||
ResponsePacket(Services::StringService::toHex(exception.getMessage() + "\n"))
|
ResponsePacket{Services::StringService::toHex(exception.getMessage() + "\n")}
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to fill EEPROM - " + exception.getMessage());
|
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,30 +15,40 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
using namespace Exceptions;
|
using namespace Exceptions;
|
||||||
|
|
||||||
FlashDone::FlashDone(const RawPacket& rawPacket)
|
FlashDone::FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: 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");
|
Logger::info("Handling FlashDone packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (debugSession.programmingSession.has_value()) {
|
if (!debugSession.programmingSession.has_value()) {
|
||||||
const auto& programmingSession = debugSession.programmingSession.value();
|
throw Exception{"No active programming session"};
|
||||||
|
}
|
||||||
|
|
||||||
Logger::info(
|
Logger::info(
|
||||||
"Flushing " + std::to_string(programmingSession.buffer.size()) + " bytes to target's program memory"
|
"Flushing " + std::to_string(debugSession.programmingSession->buffer.size())
|
||||||
|
+ " bytes to target's program memory"
|
||||||
);
|
);
|
||||||
|
|
||||||
targetControllerService.enableProgrammingMode();
|
targetControllerService.enableProgrammingMode();
|
||||||
|
|
||||||
targetControllerService.writeMemory(
|
targetControllerService.writeMemory(
|
||||||
Targets::TargetMemoryType::FLASH,
|
this->programMemoryAddressSpaceDescriptor,
|
||||||
programmingSession.startAddress,
|
this->programMemorySegmentDescriptor,
|
||||||
std::move(programmingSession.buffer)
|
debugSession.programmingSession->startAddress,
|
||||||
|
std::move(debugSession.programmingSession->buffer)
|
||||||
);
|
);
|
||||||
|
|
||||||
debugSession.programmingSession.reset();
|
debugSession.programmingSession.reset();
|
||||||
}
|
|
||||||
|
|
||||||
Logger::warning("Program memory updated");
|
Logger::warning("Program memory updated");
|
||||||
targetControllerService.disableProgrammingMode();
|
targetControllerService.disableProgrammingMode();
|
||||||
@@ -47,7 +57,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
targetControllerService.resetTarget();
|
targetControllerService.resetTarget();
|
||||||
Logger::info("Target reset complete");
|
Logger::info("Target reset complete");
|
||||||
|
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to handle FlashDone packet - " + exception.getMessage());
|
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());
|
Logger::error("Failed to disable programming mode - " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
#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
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
@@ -16,10 +17,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
class FlashDone: public Gdb::CommandPackets::CommandPacket
|
class FlashDone: public Gdb::CommandPackets::CommandPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit FlashDone(const RawPacket& rawPacket);
|
const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||||
|
|
||||||
|
explicit FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -15,40 +16,40 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
using namespace Exceptions;
|
using namespace Exceptions;
|
||||||
|
|
||||||
FlashErase::FlashErase(const RawPacket& rawPacket)
|
FlashErase::FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
|
, programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor)
|
||||||
|
, programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor)
|
||||||
{
|
{
|
||||||
const auto packetString = QString::fromLocal8Bit(
|
using Services::StringService;
|
||||||
reinterpret_cast<const char*>(this->data.data() + 12),
|
|
||||||
static_cast<int>(this->data.size() - 12)
|
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
|
* The flash erase ('vFlashErase') packet consists of two segments, an address and a length, separated by a
|
||||||
* comma.
|
* comma.
|
||||||
|
*
|
||||||
|
* Example: $vFlashErase:00000000,00004f00#f4
|
||||||
*/
|
*/
|
||||||
const auto packetSegments = packetString.split(",");
|
const auto command = std::string{this->data.begin() + 12, this->data.end()};
|
||||||
if (packetSegments.size() != 2) {
|
|
||||||
throw Exception(
|
const auto delimiterPos = command.find_first_of(',');
|
||||||
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
|
if (delimiterPos == std::string::npos) {
|
||||||
);
|
throw Exception{"Invalid packet"};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool conversionStatus = false;
|
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
||||||
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
|
this->bytes = StringService::toUint32(command.substr(delimiterPos + 1), 16);
|
||||||
|
|
||||||
if (!conversionStatus) {
|
|
||||||
throw Exception("Failed to parse start address from flash erase packet data");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
void FlashErase::handle(
|
||||||
|
Gdb::DebugSession& debugSession,
|
||||||
if (!conversionStatus) {
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
throw Exception("Failed to parse length from flash erase packet data");
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
}
|
TargetControllerService& targetControllerService
|
||||||
}
|
) {
|
||||||
|
|
||||||
void FlashErase::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
|
||||||
Logger::info("Handling FlashErase packet");
|
Logger::info("Handling FlashErase packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -57,9 +58,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
Logger::warning("Erasing program memory, in preparation for programming");
|
Logger::warning("Erasing program memory, in preparation for programming");
|
||||||
|
|
||||||
// We don't erase a specific address range - we just erase the entire program memory.
|
// 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) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to erase flash memory - " + exception.getMessage());
|
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());
|
Logger::error("Failed to disable programming mode - " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
#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
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
@@ -19,11 +20,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
public:
|
public:
|
||||||
std::uint32_t startAddress = 0;
|
std::uint32_t startAddress = 0;
|
||||||
std::uint32_t bytes = 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(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#include "FlashWrite.hpp"
|
#include "FlashWrite.hpp"
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -20,44 +19,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
FlashWrite::FlashWrite(const RawPacket& rawPacket)
|
FlashWrite::FlashWrite(const RawPacket& rawPacket)
|
||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
{
|
{
|
||||||
|
using Services::StringService;
|
||||||
|
|
||||||
if (this->data.size() < 15) {
|
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.
|
* The flash write ('vFlashWrite') packet consists of two segments: an address and a buffer, seperated by a
|
||||||
*
|
* colon.
|
||||||
* 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()) {
|
if (delimiterIt == this->data.end()) {
|
||||||
throw Exception("Failed to find colon delimiter in write flash packet.");
|
throw Exception{"Failed to find colon delimiter in write flash packet."};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool conversionStatus = false;
|
this->startAddress = StringService::toUint32(std::string{this->data.begin() + 12, delimiterIt}, 16);
|
||||||
this->startAddress = QByteArray(
|
this->buffer = Targets::TargetMemoryBuffer{delimiterIt + 1, this->data.end()};
|
||||||
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());
|
void FlashWrite::handle(
|
||||||
}
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
void FlashWrite::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
|
TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
Logger::info("Handling FlashWrite packet");
|
Logger::info("Handling FlashWrite packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this->buffer.empty()) {
|
if (this->buffer.empty()) {
|
||||||
throw Exception("Received empty buffer from GDB");
|
throw Exception{"Received empty buffer from GDB"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!debugSession.programmingSession.has_value()) {
|
if (!debugSession.programmingSession.has_value()) {
|
||||||
debugSession.programmingSession = ProgrammingSession(this->startAddress, this->buffer);
|
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto& programmingSession = debugSession.programmingSession.value();
|
auto& programmingSession = debugSession.programmingSession.value();
|
||||||
@@ -65,7 +61,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
const auto expectedStartAddress = (currentEndAddress + 1);
|
const auto expectedStartAddress = (currentEndAddress + 1);
|
||||||
|
|
||||||
if (this->startAddress < expectedStartAddress) {
|
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) {
|
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) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to handle FlashWrite packet - " + exception.getMessage());
|
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());
|
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(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "ReadMemory.hpp"
|
#include "ReadMemory.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||||
|
|
||||||
@@ -18,141 +20,139 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
using Exceptions::Exception;
|
using Exceptions::Exception;
|
||||||
|
|
||||||
ReadMemory::ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
ReadMemory::ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: ReadMemory(rawPacket, gdbTargetDescriptor, ReadMemory::extractPacketData(rawPacket))
|
||||||
{
|
{}
|
||||||
if (this->data.size() < 4) {
|
|
||||||
throw Exception("Invalid packet length");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto packetString = QString::fromLocal8Bit(
|
void ReadMemory::handle(
|
||||||
reinterpret_cast<const char*>(this->data.data() + 1),
|
Gdb::DebugSession& debugSession,
|
||||||
static_cast<int>(this->data.size() - 1)
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
);
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
|
TargetControllerService& targetControllerService
|
||||||
/*
|
) {
|
||||||
* 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) {
|
|
||||||
Logger::info("Handling ReadMemory packet");
|
Logger::info("Handling ReadMemory packet");
|
||||||
|
|
||||||
try {
|
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) {
|
if (this->bytes == 0) {
|
||||||
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>()));
|
debugSession.connection.writePacket(ResponsePacket{Targets::TargetMemoryBuffer{}});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& memoryDescriptor = memoryDescriptorIt->second;
|
const auto addressRange = Targets::TargetMemoryAddressRange{
|
||||||
|
this->startAddress,
|
||||||
|
this->startAddress + this->bytes - 1
|
||||||
|
};
|
||||||
|
|
||||||
if (this->memoryType == Targets::TargetMemoryType::EEPROM) {
|
const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
|
||||||
// GDB sends EEPROM addresses in relative form - we convert them to absolute form, here.
|
addressRange
|
||||||
this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress;
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* GDB will sometimes request an excess of up to two bytes outside the memory segment address range, even
|
||||||
* accessing them.
|
* 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)
|
if (accessibleBytes < this->bytes && (this->bytes - accessibleBytes) > 2) {
|
||||||
? 0x00
|
|
||||||
: memoryDescriptor.addressRange.startAddress;
|
|
||||||
|
|
||||||
const auto permittedEndAddress = memoryDescriptor.addressRange.endAddress + 2;
|
|
||||||
|
|
||||||
if (
|
|
||||||
this->startAddress < permittedStartAddress
|
|
||||||
|| (this->startAddress + (this->bytes - 1)) > permittedEndAddress
|
|
||||||
) {
|
|
||||||
/*
|
/*
|
||||||
* GDB can be configured to generate backtraces past the main function and the internal entry point
|
* GDB has requested memory that, at least partially, does not reside in any known memory segment.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* This means that GDB will attempt to walk down the stack to identify every frame. The problem is that
|
* This could be a result of GDB being configured to generate backtraces past the main function and
|
||||||
* GDB doesn't really know where the stack begins, so it ends up in a loop, continually issuing read
|
* the internal entry point of the application. This means that GDB will attempt to walk down the stack
|
||||||
* memory commands. This has exposed an issue on our end - we need to validate the requested memory
|
* to identify every frame. The problem is that GDB doesn't really know where the stack begins, so it
|
||||||
* address range and reject any request for a range that's not within the target's memory. We do this
|
* probes the target by continuously issuing read memory commands until the server responds with an
|
||||||
* here.
|
* 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
|
* 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
|
* 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.
|
* GDB with an error response.
|
||||||
*/
|
*/
|
||||||
Logger::debug(
|
Logger::debug(
|
||||||
"GDB requested access to memory which is outside the target's memory range - returning error "
|
"GDB requested access to memory which does not reside within any memory segment - returning error "
|
||||||
"response"
|
"response"
|
||||||
);
|
);
|
||||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
auto buffer = Targets::TargetMemoryBuffer(this->bytes, 0x00);
|
||||||
* 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 memoryBuffer = Targets::TargetMemoryBuffer();
|
{
|
||||||
|
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||||
|
|
||||||
if (bytesToRead > 0) {
|
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
|
||||||
memoryBuffer = targetControllerService.readMemory(
|
const auto segmentStartAddress = std::max(
|
||||||
this->memoryType,
|
|
||||||
this->startAddress,
|
this->startAddress,
|
||||||
bytesToRead
|
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) {
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
|
||||||
// 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)));
|
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to read memory from target - " + exception.getMessage());
|
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 <optional>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
#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/Targets/TargetMemory.hpp"
|
||||||
|
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
@@ -17,26 +18,32 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
class ReadMemory: public Gdb::CommandPackets::CommandPacket
|
class ReadMemory: public Gdb::CommandPackets::CommandPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||||
* Start address of the memory operation.
|
|
||||||
*/
|
|
||||||
Targets::TargetMemoryAddress startAddress = 0;
|
|
||||||
|
|
||||||
/**
|
Targets::TargetMemoryAddress startAddress;
|
||||||
* The type of memory to read from.
|
Targets::TargetMemorySize bytes;
|
||||||
*/
|
|
||||||
Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH;
|
|
||||||
|
|
||||||
/**
|
ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||||
* Number of bytes to read.
|
|
||||||
*/
|
|
||||||
Targets::TargetMemorySize bytes = 0;
|
|
||||||
|
|
||||||
explicit ReadMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) 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/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
@@ -12,111 +13,74 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
using Exceptions::Exception;
|
using Exceptions::Exception;
|
||||||
|
|
||||||
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket)
|
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
|
, eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor)
|
||||||
|
, programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor)
|
||||||
|
, eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor)
|
||||||
{
|
{
|
||||||
if (this->data.size() < 26) {
|
using Services::StringService;
|
||||||
throw Exception("Invalid packet length");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto packetString = QString::fromLocal8Bit(
|
if (this->data.size() < 26) {
|
||||||
reinterpret_cast<const char*>(this->data.data() + 23), // +23 to exclude the "qXfer:memory-map:read::"
|
throw Exception{"Invalid packet length"};
|
||||||
static_cast<int>(this->data.size() - 23)
|
}
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The read memory map ('qXfer:memory-map:read::...') packet consists of two segments, an offset and a 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.
|
* 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) {
|
const auto delimiterPos = command.find_first_of(',');
|
||||||
throw Exception(
|
if (delimiterPos == std::string::npos) {
|
||||||
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
|
throw Exception{"Invalid packet"};
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool conversionStatus = false;
|
this->offset = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
||||||
this->offset = packetSegments.at(0).toUInt(&conversionStatus, 16);
|
this->length = StringService::toUint32(command.substr(delimiterPos + 1), 16);
|
||||||
|
|
||||||
if (!conversionStatus) {
|
|
||||||
throw Exception("Failed to parse offset from read memory map packet data");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->length = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
void ReadMemoryMap::handle(
|
||||||
|
Gdb::DebugSession& debugSession,
|
||||||
if (!conversionStatus) {
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
throw Exception("Failed to parse read length from read memory map packet data");
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
}
|
TargetControllerService& targetControllerService
|
||||||
}
|
) {
|
||||||
|
|
||||||
void ReadMemoryMap::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
|
||||||
Logger::info("Handling ReadMemoryMap packet");
|
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
|
* We include register and EEPROM memory in our RAM section. This allows GDB to access registers and EEPROM
|
||||||
* data via memory read/write packets.
|
* 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 ramSectionEndAddress = gdbTargetDescriptor.translateTargetMemoryAddress(
|
||||||
const auto ramSectionSize = eepromEndAddress - ramGdbOffset;
|
this->eepromMemorySegmentDescriptor.addressRange.endAddress,
|
||||||
|
this->eepromAddressSpaceDescriptor,
|
||||||
const auto flashSize = flashDescriptor.size();
|
this->eepromMemorySegmentDescriptor
|
||||||
const auto flashPageSize = flashDescriptor.pageSize.value();
|
);
|
||||||
|
const auto ramSectionStartAddress = TargetDescriptor::SRAM_ADDRESS_MASK;
|
||||||
|
const auto ramSectionSize = ramSectionEndAddress - ramSectionStartAddress + 1;
|
||||||
|
|
||||||
const auto memoryMap =
|
const auto memoryMap =
|
||||||
std::string("<memory-map>")
|
std::string{"<memory-map>"}
|
||||||
+ "<memory type=\"ram\" start=\"" + std::to_string(ramGdbOffset) + "\" length=\"" + std::to_string(ramSectionSize) + "\"/>"
|
+ "<memory type=\"ram\" start=\"" + std::to_string(ramSectionStartAddress) + "\" length=\"" + std::to_string(ramSectionSize) + "\"/>"
|
||||||
+ "<memory type=\"flash\" start=\"" + std::to_string(flashGdbOffset) + "\" length=\"" + std::to_string(flashSize) + "\">"
|
+ "<memory type=\"flash\" start=\"0\" length=\"" + std::to_string(this->programMemorySegmentDescriptor.size()) + "\">"
|
||||||
+ "<property name=\"blocksize\">" + std::to_string(flashPageSize) + "</property>"
|
+ "<property name=\"blocksize\">" + std::to_string(this->programMemorySegmentDescriptor.pageSize.value()) + "</property>"
|
||||||
+ "</memory>"
|
+ "</memory>"
|
||||||
+ "</memory-map>";
|
+ "</memory-map>";
|
||||||
|
|
||||||
|
auto responseData = std::vector<unsigned char>{'l'};
|
||||||
|
|
||||||
if (this->offset < memoryMap.size() && this->length > 0) {
|
if (this->offset < memoryMap.size() && this->length > 0) {
|
||||||
auto memoryMapData = std::vector<unsigned char>(
|
responseData.insert(
|
||||||
|
responseData.end(),
|
||||||
memoryMap.begin() + this->offset,
|
memoryMap.begin() + this->offset,
|
||||||
memoryMap.begin() + std::min(
|
memoryMap.begin() + this->offset + std::min(
|
||||||
static_cast<long>(this->offset + this->length),
|
static_cast<long>(this->length),
|
||||||
static_cast<long>(memoryMap.size())
|
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 <cstdint>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
#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
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
@@ -13,6 +17,10 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
class ReadMemoryMap: public Gdb::CommandPackets::CommandPacket
|
class ReadMemoryMap: public Gdb::CommandPackets::CommandPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The offset of the memory map, from which to read.
|
* The offset of the memory map, from which to read.
|
||||||
*/
|
*/
|
||||||
@@ -23,10 +31,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
std::uint32_t length = 0;
|
std::uint32_t length = 0;
|
||||||
|
|
||||||
explicit ReadMemoryMap(const RawPacket& rawPacket);
|
explicit ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#include "ReadRegister.hpp"
|
#include "ReadRegister.hpp"
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
|
||||||
|
|
||||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||||
#include "src/Services/StringService.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
@@ -24,16 +24,23 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
ReadRegister::ReadRegister(const RawPacket& rawPacket)
|
ReadRegister::ReadRegister(const RawPacket& rawPacket)
|
||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
{
|
{
|
||||||
|
using Services::StringService;
|
||||||
|
|
||||||
if (this->data.size() < 2) {
|
if (this->data.size() < 2) {
|
||||||
throw Exception("Invalid packet length");
|
throw Exception{"Invalid packet length"};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->registerId = static_cast<GdbRegisterId>(
|
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");
|
Logger::info("Handling ReadRegister packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -47,49 +54,60 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
const auto programCounter = targetControllerService.getProgramCounter();
|
const auto programCounter = targetControllerService.getProgramCounter();
|
||||||
|
|
||||||
debugSession.connection.writePacket(
|
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),
|
||||||
static_cast<unsigned char>(programCounter >> 8),
|
static_cast<unsigned char>(programCounter >> 8),
|
||||||
static_cast<unsigned char>(programCounter >> 16),
|
static_cast<unsigned char>(programCounter >> 16),
|
||||||
static_cast<unsigned char>(programCounter >> 24),
|
static_cast<unsigned char>(programCounter >> 24),
|
||||||
})))
|
})}
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
|
if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) {
|
||||||
const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(this->registerId);
|
/*
|
||||||
|
* 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
|
this->registerId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!targetRegisterDescriptorId.has_value()) {
|
if (
|
||||||
throw Exception("GDB requested an invalid/unknown register");
|
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;
|
auto registerValue = targetControllerService.readRegister(*(targetRegisterDescriptorIt->second));
|
||||||
|
std::reverse(registerValue.begin(), registerValue.end()); // MSB to LSB
|
||||||
// GDB expects register values to be in LSB.
|
|
||||||
std::reverse(registerValue.begin(), registerValue.end());
|
|
||||||
|
|
||||||
|
const auto& gdbRegisterDescriptor = gdbRegisterDescriptorIt->second;
|
||||||
if (registerValue.size() < gdbRegisterDescriptor.size) {
|
if (registerValue.size() < gdbRegisterDescriptor.size) {
|
||||||
/*
|
// The register on the target is smaller than the size expected by GDB.
|
||||||
* The register on the target is smaller than the size expected by GDB.
|
|
||||||
*
|
|
||||||
* Insert the rest of the bytes.
|
|
||||||
*/
|
|
||||||
registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00);
|
registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession.connection.writePacket(
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(registerValue)});
|
||||||
ResponsePacket(Services::StringService::toHex(registerValue))
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to read general registers - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#include "ReadRegisters.hpp"
|
#include "ReadRegisters.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
|
||||||
|
|
||||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||||
#include "src/Targets/TargetRegister.hpp"
|
|
||||||
#include "src/Targets/TargetMemory.hpp"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
#include "src/Services/StringService.hpp"
|
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
@@ -16,93 +17,78 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
{
|
{
|
||||||
using Services::TargetControllerService;
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
using Targets::TargetRegister;
|
|
||||||
using Targets::TargetRegisterDescriptorIds;
|
|
||||||
|
|
||||||
using ResponsePackets::ResponsePacket;
|
using ResponsePackets::ResponsePacket;
|
||||||
using ResponsePackets::ErrorResponsePacket;
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
|
|
||||||
using Exceptions::Exception;
|
using Exceptions::Exception;
|
||||||
|
|
||||||
ReadRegisters::ReadRegisters(const RawPacket& rawPacket)
|
ReadRegisters::ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: 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");
|
Logger::info("Handling ReadRegisters packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
|
auto buffer = Targets::TargetMemoryBuffer(39, 0x00);
|
||||||
auto descriptorIds = TargetRegisterDescriptorIds();
|
|
||||||
|
|
||||||
// Read all target registers mapped to a GDB register
|
auto gpRegDescriptors = Targets::TargetRegisterDescriptors{};
|
||||||
for (const auto& [gdbRegisterId, gdbRegisterDescriptor] : targetDescriptor.gdbRegisterDescriptorsById) {
|
std::transform(
|
||||||
const auto registerDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
|
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.begin(),
|
||||||
gdbRegisterId
|
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end(),
|
||||||
|
std::back_inserter(gpRegDescriptors),
|
||||||
|
[] (const auto& pair) {
|
||||||
|
return pair.second;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (registerDescriptorId.has_value()) {
|
|
||||||
descriptorIds.insert(*registerDescriptorId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Targets::TargetRegisters registerSet;
|
|
||||||
Targets::TargetMemoryAddress programCounter;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto atomicSession = targetControllerService.makeAtomicSession();
|
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||||
|
|
||||||
registerSet = targetControllerService.readRegisters(descriptorIds);
|
for (auto& [regDesc, regVal] : targetControllerService.readRegisters(gpRegDescriptors)) {
|
||||||
programCounter = targetControllerService.getProgramCounter();
|
if (regDesc.type != Targets::TargetRegisterType::GENERAL_PURPOSE_REGISTER) {
|
||||||
|
// Status register (SREG)
|
||||||
|
assert(regVal.size() == 1);
|
||||||
|
buffer[32] = regVal[0];
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto bufferOffset = regDesc.startAddress
|
||||||
|
- this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress;
|
||||||
|
|
||||||
|
assert((buffer.size() - bufferOffset) >= regVal.size());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sort each register by their respective GDB register ID - this will leave us with a collection of
|
* GDB expects register values in LSB form, which is why we use reverse iterators below.
|
||||||
* registers in the order expected by the GDB client.
|
*
|
||||||
|
* This isn't really necessary though, as all of the registers that are handled here are
|
||||||
|
* single-byte registers.
|
||||||
*/
|
*/
|
||||||
std::sort(
|
std::copy(regVal.rbegin(), regVal.rend(), buffer.begin() + bufferOffset);
|
||||||
registerSet.begin(),
|
|
||||||
registerSet.end(),
|
|
||||||
[this, &targetDescriptor] (const TargetRegister& regA, const TargetRegister& regB) {
|
|
||||||
return targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regA.descriptorId).value() <
|
|
||||||
targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regB.descriptorId).value();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure that
|
|
||||||
* each register value size matches the size in the associated GDB register descriptor and implode the
|
|
||||||
* values.
|
|
||||||
*/
|
|
||||||
auto registers = std::vector<unsigned char>();
|
|
||||||
for (auto& reg : registerSet) {
|
|
||||||
std::reverse(reg.value.begin(), reg.value.end());
|
|
||||||
|
|
||||||
const auto gdbRegisterId = targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(
|
|
||||||
reg.descriptorId
|
|
||||||
).value();
|
|
||||||
const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(gdbRegisterId);
|
|
||||||
|
|
||||||
if (reg.value.size() < gdbRegisterDescriptor.size) {
|
|
||||||
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
|
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)
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
|
||||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter));
|
|
||||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 8));
|
|
||||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 16));
|
|
||||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 24));
|
|
||||||
|
|
||||||
debugSession.connection.writePacket(
|
|
||||||
ResponsePacket(Services::StringService::toHex(registers))
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to read registers - " + exception.getMessage());
|
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/CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
|
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
|
||||||
|
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||||
|
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
@@ -15,10 +17,14 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
class ReadRegisters: public Gdb::CommandPackets::CommandPacket
|
class ReadRegisters: public Gdb::CommandPackets::CommandPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ReadRegisters(const RawPacket& rawPacket);
|
const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor;
|
||||||
|
|
||||||
|
explicit ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: 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");
|
Logger::info("Handling VContContinueExecution packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
targetControllerService.continueTargetExecution(std::nullopt, std::nullopt);
|
targetControllerService.resumeTargetExecution();
|
||||||
debugSession.waitingForBreak = true;
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to continue execution on target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp"
|
||||||
#include "src/Services/Avr8InstructionService.hpp"
|
#include "src/Services/Avr8InstructionService.hpp"
|
||||||
#include "src/Services/StringService.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Services/PathService.hpp"
|
#include "src/Services/PathService.hpp"
|
||||||
@@ -13,43 +14,42 @@
|
|||||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
using Services::TargetControllerService;
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
using ResponsePackets::ErrorResponsePacket;
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
|
|
||||||
using ::Exceptions::Exception;
|
using ::Exceptions::Exception;
|
||||||
|
|
||||||
VContRangeStep::VContRangeStep(const RawPacket& rawPacket)
|
VContRangeStep::VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
|
, programAddressSpaceDescriptor(gdbTargetDescriptor.programAddressSpaceDescriptor)
|
||||||
|
, programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor)
|
||||||
{
|
{
|
||||||
|
using Services::StringService;
|
||||||
|
|
||||||
if (this->data.size() < 10) {
|
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 delimiterPos = command.find(',');
|
||||||
const auto threadIdDelimiterPosition = commandData.find(':');
|
const auto threadIdDelimiterPos = command.find(':');
|
||||||
|
if (delimiterPos == std::string::npos || delimiterPos >= (command.size() - 1)) {
|
||||||
if (delimiterPosition == std::string::npos || delimiterPosition >= (commandData.size() - 1)) {
|
throw Exception{"Invalid VContRangeStep packet"};
|
||||||
throw Exception("Invalid VContRangeStep packet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& delimiterIt = commandData.begin() + static_cast<decltype(commandData)::difference_type>(
|
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
||||||
delimiterPosition
|
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::Avr8InstructionService;
|
||||||
using Services::StringService;
|
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));
|
Logger::debug("Requested stepping range end address (exclusive): 0x" + StringService::toHex(this->endAddress));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
|
const auto stepAddressRange = Targets::TargetMemoryAddressRange{this->startAddress, this->endAddress};
|
||||||
const auto& programMemoryAddressRange = targetDescriptor.memoryDescriptorsByType.at(
|
const auto stepByteSize = stepAddressRange.size() - 1; // -1 because the end address is exclusive
|
||||||
targetDescriptor.programMemoryType
|
const auto& programMemoryAddressRange = this->programMemorySegmentDescriptor.addressRange;
|
||||||
).addressRange;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this->startAddress > this->endAddress
|
stepAddressRange.startAddress > stepAddressRange.endAddress
|
||||||
|| (this->startAddress % 2) != 0
|
|| (stepAddressRange.startAddress % 2) != 0
|
||||||
|| (this->endAddress % 2) != 0
|
|| (stepAddressRange.endAddress % 2) != 0
|
||||||
|| this->startAddress < programMemoryAddressRange.startAddress
|
|| !programMemoryAddressRange.contains(stepAddressRange)
|
||||||
|| this->endAddress > programMemoryAddressRange.endAddress
|
|
||||||
) {
|
) {
|
||||||
throw Exception("Invalid address range in VContRangeStep");
|
throw Exception{"Invalid address range in VContRangeStep"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debugSession.activeRangeSteppingSession.has_value()) {
|
if (debugSession.activeRangeSteppingSession.has_value()) {
|
||||||
@@ -81,26 +79,30 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
debugSession.terminateRangeSteppingSession(targetControllerService);
|
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.
|
// Single step requested. No need for a range step here.
|
||||||
targetControllerService.stepTargetExecution(std::nullopt);
|
targetControllerService.stepTargetExecution();
|
||||||
debugSession.waitingForBreak = true;
|
debugSession.waitingForBreak = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto addressRange = Targets::TargetMemoryAddressRange(this->startAddress, this->endAddress);
|
auto rangeSteppingSession = RangeSteppingSession{stepAddressRange, {}};
|
||||||
auto rangeSteppingSession = RangeSteppingSession(addressRange, {});
|
|
||||||
|
|
||||||
const auto instructionsByAddress = Avr8InstructionService::fetchInstructions(
|
const auto instructionsByAddress = Decoder::decode(
|
||||||
addressRange,
|
stepAddressRange.startAddress,
|
||||||
targetDescriptor,
|
targetControllerService.readMemory(
|
||||||
targetControllerService
|
this->programAddressSpaceDescriptor,
|
||||||
|
this->programMemorySegmentDescriptor,
|
||||||
|
stepAddressRange.startAddress,
|
||||||
|
stepByteSize
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Logger::debug(
|
Logger::debug(
|
||||||
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instructions within stepping range "
|
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instruction(s) within stepping range "
|
||||||
"(byte addresses) 0x" + StringService::toHex(addressRange.startAddress) + " -> 0x"
|
"(byte addresses) 0x" + StringService::toHex(stepAddressRange.startAddress) + " -> 0x"
|
||||||
+ StringService::toHex(addressRange.endAddress) + ", in preparation for new range stepping session"
|
+ StringService::toHex(stepAddressRange.endAddress) + ", in preparation for new range stepping "
|
||||||
|
"session"
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const auto& [instructionAddress, instruction] : instructionsByAddress) {
|
for (const auto& [instructionAddress, instruction] : instructionsByAddress) {
|
||||||
@@ -144,10 +146,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!programMemoryAddressRange.contains(*destinationAddress)) {
|
||||||
*destinationAddress < programMemoryAddressRange.startAddress
|
|
||||||
|| *destinationAddress > programMemoryAddressRange.endAddress
|
|
||||||
) {
|
|
||||||
/*
|
/*
|
||||||
* This instruction may jump to an invalid address. Someone screwed up here - could be
|
* 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
|
* something wrong in Bloom (opcode decoding bug, incorrect program memory address range in
|
||||||
@@ -166,8 +165,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
*destinationAddress < addressRange.startAddress
|
*destinationAddress < stepAddressRange.startAddress
|
||||||
|| *destinationAddress >= addressRange.endAddress
|
|| *destinationAddress >= stepAddressRange.endAddress
|
||||||
) {
|
) {
|
||||||
/*
|
/*
|
||||||
* This instruction may jump to an address outside the requested stepping range.
|
* 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);
|
debugSession.startRangeSteppingSession(std::move(rangeSteppingSession), targetControllerService);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -195,12 +200,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
* we should continue. See that member function for more.
|
* we should continue. See that member function for more.
|
||||||
*/
|
*/
|
||||||
debugSession.activeRangeSteppingSession->singleStepping = true;
|
debugSession.activeRangeSteppingSession->singleStepping = true;
|
||||||
targetControllerService.stepTargetExecution(std::nullopt);
|
targetControllerService.stepTargetExecution();
|
||||||
debugSession.waitingForBreak = true;
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to start new range stepping session - " + exception.getMessage());
|
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/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"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
@@ -16,13 +19,18 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
class VContRangeStep: public Gdb::CommandPackets::CommandPacket
|
class VContRangeStep: public Gdb::CommandPackets::CommandPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
const Targets::TargetAddressSpaceDescriptor& programAddressSpaceDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||||
|
|
||||||
Targets::TargetMemoryAddress startAddress;
|
Targets::TargetMemoryAddress startAddress;
|
||||||
Targets::TargetMemoryAddress endAddress;
|
Targets::TargetMemoryAddress endAddress;
|
||||||
|
|
||||||
explicit VContRangeStep(const RawPacket& rawPacket);
|
explicit VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: 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");
|
Logger::info("Handling VContStepExecution packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
targetControllerService.stepTargetExecution(std::nullopt);
|
targetControllerService.stepTargetExecution();
|
||||||
debugSession.waitingForBreak = true;
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to step execution on target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,15 +12,20 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: 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");
|
Logger::info("Handling VContSupportedActionsQuery packet");
|
||||||
|
|
||||||
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
|
// 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
|
debugSession.serverConfig.rangeStepping
|
||||||
? "vCont;c;C;s;S;r"
|
? "vCont;c;C;s;S;r"
|
||||||
: "vCont;c;C;s;S"
|
: "vCont;c;C;s;S"
|
||||||
)
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -16,124 +17,116 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
using namespace Exceptions;
|
using namespace Exceptions;
|
||||||
|
|
||||||
WriteMemory::WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
WriteMemory::WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||||
: CommandPacket(rawPacket)
|
: WriteMemory(rawPacket, gdbTargetDescriptor, WriteMemory::extractPacketData(rawPacket))
|
||||||
{
|
{}
|
||||||
if (this->data.size() < 4) {
|
|
||||||
throw Exception("Invalid packet length");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto packetString = QString::fromLocal8Bit(
|
void WriteMemory::handle(
|
||||||
reinterpret_cast<const char*>(this->data.data() + 1),
|
Gdb::DebugSession& debugSession,
|
||||||
static_cast<int>(this->data.size() - 1)
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
);
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
|
TargetControllerService& targetControllerService
|
||||||
/*
|
) {
|
||||||
* 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) {
|
|
||||||
Logger::info("Handling WriteMemory packet");
|
Logger::info("Handling WriteMemory packet");
|
||||||
|
|
||||||
try {
|
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) {
|
if (this->buffer.size() == 0) {
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& memoryDescriptor = memoryDescriptorIt->second;
|
const auto addressRange = Targets::TargetMemoryAddressRange{
|
||||||
|
|
||||||
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,
|
|
||||||
this->startAddress,
|
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) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to write memory to target - " + exception.getMessage());
|
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 <optional>
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
#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/Targets/TargetMemory.hpp"
|
||||||
|
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
{
|
{
|
||||||
@@ -17,26 +18,29 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
class WriteMemory: public Gdb::CommandPackets::CommandPacket
|
class WriteMemory: public Gdb::CommandPackets::CommandPacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||||
* Start address of the memory operation.
|
Targets::TargetMemoryAddress startAddress;
|
||||||
*/
|
Targets::TargetMemorySize bytes;
|
||||||
Targets::TargetMemoryAddress startAddress = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of memory to read from.
|
|
||||||
*/
|
|
||||||
Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data to write.
|
|
||||||
*/
|
|
||||||
Targets::TargetMemoryBuffer buffer;
|
Targets::TargetMemoryBuffer buffer;
|
||||||
|
|
||||||
explicit WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
explicit WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) 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/OkResponsePacket.hpp"
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Targets/TargetRegister.hpp"
|
|
||||||
|
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -13,9 +11,6 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
{
|
{
|
||||||
using Services::TargetControllerService;
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
using Targets::TargetRegister;
|
|
||||||
using Targets::TargetRegisterDescriptors;
|
|
||||||
|
|
||||||
using ResponsePackets::ResponsePacket;
|
using ResponsePackets::ResponsePacket;
|
||||||
using ResponsePackets::OkResponsePacket;
|
using ResponsePackets::OkResponsePacket;
|
||||||
using ResponsePackets::ErrorResponsePacket;
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
@@ -25,87 +20,88 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
WriteRegister::WriteRegister(const RawPacket& rawPacket)
|
WriteRegister::WriteRegister(const RawPacket& rawPacket)
|
||||||
: CommandPacket(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
|
// 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) {
|
const auto delimiterPos = command.find_first_of('=');
|
||||||
throw Exception("Invalid WriteRegister command packet - insufficient data in packet.");
|
if (delimiterPos == std::string::npos) {
|
||||||
|
throw Exception{"Invalid packet"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.find('=') == std::string::npos) {
|
this->registerId = static_cast<GdbRegisterId>(StringService::toUint32(command.substr(0, delimiterPos), 16));
|
||||||
throw Exception("Invalid WriteRegister command packet - unexpected format");
|
this->registerValue = Services::StringService::dataFromHex(command.substr(delimiterPos + 1));
|
||||||
}
|
|
||||||
|
|
||||||
const auto packetSegments = QString::fromStdString(packet).split("=");
|
|
||||||
this->registerId = static_cast<GdbRegisterId>(packetSegments.front().mid(1).toUInt(nullptr, 16));
|
|
||||||
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
|
|
||||||
|
|
||||||
if (this->registerValue.empty()) {
|
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());
|
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");
|
Logger::info("Handling WriteRegister packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID) {
|
if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID) {
|
||||||
|
if (this->registerValue.size() != 4) {
|
||||||
|
throw Exception{"Invalid PC value register size"};
|
||||||
|
}
|
||||||
|
|
||||||
targetControllerService.setProgramCounter(
|
targetControllerService.setProgramCounter(
|
||||||
static_cast<Targets::TargetMemoryAddress>(
|
static_cast<Targets::TargetMemoryAddress>(
|
||||||
(this->registerValue.size() >= 1 ? this->registerValue[0] : 0x00) << 24
|
this->registerValue[0] << 24
|
||||||
| (this->registerValue.size() >= 2 ? this->registerValue[1] : 0x00) << 16
|
| this->registerValue[1] << 16
|
||||||
| (this->registerValue.size() >= 3 ? this->registerValue[2] : 0x00) << 8
|
| this->registerValue[2] << 8
|
||||||
| (this->registerValue.size() >= 4 ? this->registerValue[3] : 0x00)
|
| this->registerValue[3]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& gdbTargetDescriptor = debugSession.gdbTargetDescriptor;
|
if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) {
|
||||||
const auto descriptorId = gdbTargetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
|
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
|
this->registerId
|
||||||
);
|
);
|
||||||
|
if (
|
||||||
if (!descriptorId.has_value()) {
|
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|
||||||
throw Exception("Invalid/unknown register");
|
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
|
||||||
|
) {
|
||||||
|
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& descriptor = gdbTargetDescriptor.targetDescriptor.registerDescriptorsById.at(*descriptorId);
|
targetControllerService.writeRegister(*(targetRegisterDescriptorIt->second), this->registerValue);
|
||||||
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
if (this->registerValue.size() > descriptor.size) {
|
|
||||||
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
|
|
||||||
for (auto i = this->registerValue.size() - 1; i >= descriptor.size; --i) {
|
|
||||||
if (this->registerValue.at(i) != 0x00) {
|
|
||||||
// If we reach a non-zero byte, we cannot trim anymore without changing the data
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->registerValue.erase(this->registerValue.begin() + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->registerValue.size() > descriptor.size) {
|
|
||||||
const auto& gdbRegisterDescriptor = gdbTargetDescriptor.gdbRegisterDescriptorsById.at(
|
|
||||||
this->registerId
|
|
||||||
);
|
|
||||||
throw Exception(
|
|
||||||
"Cannot set value for " + gdbRegisterDescriptor.name + " - value size exceeds register size."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetControllerService.writeRegisters({
|
|
||||||
TargetRegister(descriptor.id, this->registerValue)
|
|
||||||
});
|
|
||||||
|
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to write registers - " + exception.getMessage());
|
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/CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
|
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
|
||||||
|
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||||
|
|
||||||
#include "src/Targets/TargetMemory.hpp"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
Gdb::DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
|
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,14 +5,8 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
DebugSession::DebugSession(
|
DebugSession::DebugSession(
|
||||||
Connection&& connection,
|
Connection&& connection,
|
||||||
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
||||||
const TargetDescriptor& targetDescriptor,
|
|
||||||
const GdbDebugServerConfig& serverConfig
|
const GdbDebugServerConfig& serverConfig
|
||||||
)
|
)
|
||||||
: Gdb::DebugSession(
|
: Gdb::DebugSession(std::move(connection), supportedFeatures, serverConfig)
|
||||||
std::move(connection),
|
|
||||||
supportedFeatures,
|
|
||||||
targetDescriptor,
|
|
||||||
serverConfig
|
|
||||||
)
|
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
DebugSession(
|
DebugSession(
|
||||||
Connection&& connection,
|
Connection&& connection,
|
||||||
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
||||||
const TargetDescriptor& targetDescriptor,
|
|
||||||
const GdbDebugServerConfig& serverConfig
|
const GdbDebugServerConfig& serverConfig
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
#include "TargetDescriptor.hpp"
|
#include "TargetDescriptor.hpp"
|
||||||
|
|
||||||
#include <numeric>
|
|
||||||
|
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb
|
namespace DebugServer::Gdb::AvrGdb
|
||||||
{
|
{
|
||||||
@@ -13,149 +10,110 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
using Exceptions::Exception;
|
using Exceptions::Exception;
|
||||||
|
|
||||||
TargetDescriptor::TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor)
|
TargetDescriptor::TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor)
|
||||||
: DebugServer::Gdb::TargetDescriptor(
|
: programAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("prog"))
|
||||||
targetDescriptor,
|
, 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"))
|
||||||
{
|
{
|
||||||
{Targets::TargetMemoryType::FLASH, 0},
|
|
||||||
{Targets::TargetMemoryType::RAM, 0x00800000U},
|
|
||||||
{Targets::TargetMemoryType::EEPROM, 0x00810000U},
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
{
|
|
||||||
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:
|
* For AVR targets, GDB defines 35 registers in total:
|
||||||
*
|
*
|
||||||
* - Register ID 0 through 31 are general purpose registers
|
* - Register ID 0 through 31 are general purpose registers
|
||||||
* - Register ID 32 is the status register (SREG)
|
* - Register ID 32 is the status register (SREG)
|
||||||
* - Register ID 33 is the stack pointer register
|
* - Register ID 33 is the stack pointer register (SP)
|
||||||
* - Register ID 34 is the program counter
|
* - Register ID 34 is the program counter (PC)
|
||||||
*
|
|
||||||
* For AVR targets, we don't have a target register descriptor for the program counter, so we don't map that
|
|
||||||
* GDB register ID (34) to anything here. Instead, the register command packet handlers (ReadRegisters,
|
|
||||||
* WriteRegister, etc) will handle any operations involving that GDB register.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// General purpose registers
|
// Create the GDB register descriptors and populate the mappings for the general purpose registers (ID 0->31)
|
||||||
GdbRegisterId gdbRegisterId = 0;
|
for (const auto& [key, descriptor] : this->cpuGpRegisterGroupDescriptor.registerDescriptorsByKey) {
|
||||||
for (const auto descriptorId : generalPurposeTargetRegisterDescriptorIds) {
|
if (descriptor.type != TargetRegisterType::GENERAL_PURPOSE_REGISTER) {
|
||||||
auto gdbRegisterDescriptor = RegisterDescriptor(
|
continue;
|
||||||
gdbRegisterId,
|
|
||||||
1,
|
|
||||||
"General Purpose Register " + std::to_string(gdbRegisterId)
|
|
||||||
);
|
|
||||||
|
|
||||||
this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(descriptorId, gdbRegisterDescriptor.id);
|
|
||||||
this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(gdbRegisterDescriptor.id, descriptorId);
|
|
||||||
|
|
||||||
this->gdbRegisterDescriptorsById.emplace(gdbRegisterDescriptor.id, std::move(gdbRegisterDescriptor));
|
|
||||||
|
|
||||||
gdbRegisterId++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& statusTargetRegisterDescriptor = this->targetDescriptor.registerDescriptorsById.at(
|
const auto gdbRegisterId = static_cast<GdbRegisterId>(
|
||||||
*(statusTargetRegisterDescriptorIds.begin())
|
descriptor.startAddress - this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress
|
||||||
);
|
);
|
||||||
|
|
||||||
auto statusGdbRegisterDescriptor = RegisterDescriptor(
|
this->gdbRegisterDescriptorsById.emplace(gdbRegisterId, RegisterDescriptor{gdbRegisterId, 1});
|
||||||
|
this->targetRegisterDescriptorsByGdbId.emplace(gdbRegisterId, &descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->gdbRegisterDescriptorsById.emplace(
|
||||||
TargetDescriptor::STATUS_GDB_REGISTER_ID,
|
TargetDescriptor::STATUS_GDB_REGISTER_ID,
|
||||||
1,
|
RegisterDescriptor{TargetDescriptor::STATUS_GDB_REGISTER_ID, 1}
|
||||||
"Status Register"
|
|
||||||
);
|
);
|
||||||
|
this->targetRegisterDescriptorsByGdbId.emplace(
|
||||||
if (statusTargetRegisterDescriptor.size > statusGdbRegisterDescriptor.size) {
|
TargetDescriptor::STATUS_GDB_REGISTER_ID,
|
||||||
throw Exception("AVR8 status target register size exceeds the GDB register size.");
|
&(targetDescriptor.getPeripheralDescriptor("cpu").getRegisterGroupDescriptor("cpu")
|
||||||
}
|
.getRegisterDescriptor("sreg"))
|
||||||
|
|
||||||
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)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We acknowledge the GDB program counter register here, but we don't map it to any target register descriptors.
|
* 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,
|
||||||
* This is because we can't access the program counter on AVR targets in the same way we do with other
|
|
||||||
* registers. We don't have a register descriptor for the program counter. We have to treat it as a special
|
|
||||||
* case in the register access command packet handlers. See CommandPackets::ReadRegister,
|
|
||||||
* CommandPackets::WriteRegister, etc for more.
|
* CommandPackets::WriteRegister, etc for more.
|
||||||
*/
|
*/
|
||||||
auto programCounterGdbRegisterDescriptor = RegisterDescriptor(
|
this->gdbRegisterDescriptorsById.emplace(
|
||||||
TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID,
|
TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID,
|
||||||
4,
|
RegisterDescriptor{TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID, 2}
|
||||||
"Program Counter"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this->gdbRegisterDescriptorsById.emplace(
|
this->gdbRegisterDescriptorsById.emplace(
|
||||||
programCounterGdbRegisterDescriptor.id,
|
TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID,
|
||||||
std::move(programCounterGdbRegisterDescriptor)
|
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/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
|
namespace DebugServer::Gdb::AvrGdb
|
||||||
{
|
{
|
||||||
class TargetDescriptor: public DebugServer::Gdb::TargetDescriptor
|
class TargetDescriptor: public DebugServer::Gdb::TargetDescriptor
|
||||||
{
|
{
|
||||||
public:
|
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 STATUS_GDB_REGISTER_ID = 32;
|
||||||
static constexpr auto STACK_POINTER_GDB_REGISTER_ID = 33;
|
static constexpr auto STACK_POINTER_GDB_REGISTER_ID = 33;
|
||||||
static constexpr auto PROGRAM_COUNTER_GDB_REGISTER_ID = 34;
|
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);
|
explicit TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor);
|
||||||
|
|
||||||
private:
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptorFromGdbAddress(
|
||||||
/**
|
GdbMemoryAddress address
|
||||||
* For AVR targets, avr-gdb defines 35 registers in total:
|
) const override;
|
||||||
*
|
|
||||||
* Register number 0 through 31 are general purpose registers
|
Targets::TargetMemoryAddress translateGdbAddress(GdbMemoryAddress address) const override;
|
||||||
* Register number 32 is the status register (SREG)
|
GdbMemoryAddress translateTargetMemoryAddress(
|
||||||
* Register number 33 is the stack pointer register
|
Targets::TargetMemoryAddress address,
|
||||||
* Register number 34 is the program counter register
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
*
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
* This function will prepare the appropriate GDB register numbers and mappings.
|
) const override;
|
||||||
*/
|
|
||||||
void loadRegisterMappings();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,24 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: Monitor(std::move(monitorPacket))
|
: 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");
|
Logger::info("Handling ActivateInsight packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
EventManager::triggerEvent(std::make_shared<Events::InsightActivationRequested>());
|
EventManager::triggerEvent(std::make_shared<Events::InsightActivationRequested>());
|
||||||
|
|
||||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
debugSession.connection.writePacket(
|
||||||
"The Insight GUI will be with you shortly.\n"
|
ResponsePacket{Services::StringService::toHex("The Insight GUI will be with you shortly.\n")}
|
||||||
)));
|
);
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to activate Insight - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,15 +22,20 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: Monitor(std::move(monitorPacket))
|
: 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");
|
Logger::info("Handling BloomVersion packet");
|
||||||
|
|
||||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||||
std::string(
|
std::string{
|
||||||
"Bloom v" + Application::VERSION.toString() + "\n"
|
"Bloom v" + Application::VERSION.toString() + "\n"
|
||||||
+ Services::PathService::homeDomainName() + "\n"
|
+ Services::PathService::homeDomainName() + "\n"
|
||||||
+ "Nav Mohammed\n"
|
+ "Nav Mohammed\n"
|
||||||
)
|
}
|
||||||
)));
|
)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,18 +21,28 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: Monitor(std::move(monitorPacket))
|
: 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");
|
Logger::info("Handling BloomVersionMachine packet");
|
||||||
|
|
||||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||||
QJsonDocument(QJsonObject({
|
QJsonDocument{
|
||||||
|
QJsonObject{
|
||||||
{"version", QString::fromStdString(Application::VERSION.toString())},
|
{"version", QString::fromStdString(Application::VERSION.toString())},
|
||||||
{"components", QJsonObject({
|
{
|
||||||
|
"components",
|
||||||
|
QJsonObject{
|
||||||
{"major", Application::VERSION.major},
|
{"major", Application::VERSION.major},
|
||||||
{"minor", Application::VERSION.minor},
|
{"minor", Application::VERSION.minor},
|
||||||
{"patch", Application::VERSION.patch},
|
{"patch", Application::VERSION.patch},
|
||||||
})},
|
}
|
||||||
})).toJson().toStdString()
|
},
|
||||||
)));
|
}
|
||||||
|
}.toJson().toStdString()
|
||||||
|
)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "src/DebugServer/Gdb/Signal.hpp"
|
#include "src/DebugServer/Gdb/Signal.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/Exceptions/ClientCommunicationError.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::CommandPackets
|
namespace DebugServer::Gdb::CommandPackets
|
||||||
@@ -20,36 +21,49 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
using ResponsePackets::EmptyResponsePacket;
|
using ResponsePackets::EmptyResponsePacket;
|
||||||
using ResponsePackets::ErrorResponsePacket;
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
|
|
||||||
void CommandPacket::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
CommandPacket::CommandPacket(const RawPacket& rawPacket) {
|
||||||
const auto packetString = std::string(this->data.begin(), this->data.end());
|
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()) {
|
if (packetString.empty()) {
|
||||||
Logger::error("Empty GDB RSP packet received.");
|
Logger::error("Empty GDB RSP packet received.");
|
||||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packetString[0] == '?') {
|
if (packetString[0] == '?') {
|
||||||
// Status report
|
// Status report
|
||||||
debugSession.connection.writePacket(TargetStopped(Signal::TRAP));
|
debugSession.connection.writePacket(TargetStopped{Signal::TRAP});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packetString.find("vMustReplyEmpty") == 0) {
|
if (packetString.find("vMustReplyEmpty") == 0) {
|
||||||
Logger::info("Handling vMustReplyEmpty");
|
Logger::info("Handling vMustReplyEmpty");
|
||||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packetString.find("qAttached") == 0) {
|
if (packetString.find("qAttached") == 0) {
|
||||||
Logger::info("Handling qAttached");
|
Logger::info("Handling qAttached");
|
||||||
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>({1})));
|
debugSession.connection.writePacket(ResponsePacket{std::vector<unsigned char>({1})});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
|
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
|
||||||
|
|
||||||
// Respond with an empty packet
|
// GDB expects an empty response for all unsupported commands
|
||||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/Packet.hpp"
|
|
||||||
#include "src/DebugServer/Gdb/DebugSession.hpp"
|
#include "src/DebugServer/Gdb/DebugSession.hpp"
|
||||||
|
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
|
||||||
|
#include "src/Targets/TargetDescriptor.hpp"
|
||||||
#include "src/Services/TargetControllerService.hpp"
|
#include "src/Services/TargetControllerService.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::CommandPackets
|
namespace DebugServer::Gdb::CommandPackets
|
||||||
{
|
{
|
||||||
/**
|
class CommandPacket
|
||||||
* 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
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CommandPacket(const RawPacket& rawPacket)
|
explicit CommandPacket(const RawPacket& rawPacket);
|
||||||
: Packet(rawPacket)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should handle the command for the current active debug session.
|
* Should handle the command for the current active debug session.
|
||||||
@@ -50,7 +24,12 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
*/
|
*/
|
||||||
virtual void handle(
|
virtual void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
);
|
);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<unsigned char> data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -17,21 +18,34 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
{
|
{
|
||||||
if (this->data.size() > 2) {
|
if (this->data.size() > 2) {
|
||||||
this->fromAddress = static_cast<Targets::TargetMemoryAddress>(
|
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");
|
Logger::info("Handling ContinueExecution packet");
|
||||||
|
|
||||||
try {
|
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;
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to continue execution on target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: 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");
|
Logger::info("Handling Detach packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -28,11 +33,11 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
targetControllerService.shutdown();
|
targetControllerService.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to shut down TargetController - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) 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))
|
: 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");
|
Logger::info("Handling HelpMonitorInfo packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -33,26 +38,24 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
* The file GdbHelpMonitorInfo.txt is included in the binary image as a resource.
|
* The file GdbHelpMonitorInfo.txt is included in the binary image as a resource.
|
||||||
* See src/DebugServer/CMakeLists.txt for more.
|
* See src/DebugServer/CMakeLists.txt for more.
|
||||||
*/
|
*/
|
||||||
auto helpFile = QFile(
|
auto helpFile = QFile{
|
||||||
QString::fromStdString(":/compiled/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt")
|
QString::fromStdString(":/compiled/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt")
|
||||||
);
|
};
|
||||||
|
|
||||||
if (!helpFile.open(QIODevice::ReadOnly)) {
|
if (!helpFile.open(QIODevice::ReadOnly)) {
|
||||||
throw Exception(
|
throw Exception{
|
||||||
"Failed to open GDB monitor info help file - please report this issue at "
|
"Failed to open GDB monitor info help file - please report this issue at "
|
||||||
+ Services::PathService::homeDomainName() + "/report-issue"
|
+ Services::PathService::homeDomainName() + "/report-issue"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession.connection.writePacket(
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||||
ResponsePacket(Services::StringService::toHex(
|
"\n" + QTextStream{&helpFile}.readAll().toUtf8().toStdString() + "\n"
|
||||||
"\n" + QTextStream(&helpFile).readAll().toUtf8().toStdString() + "\n"
|
)});
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error(exception.getMessage());
|
Logger::error(exception.getMessage());
|
||||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,10 +16,15 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
using ::Exceptions::Exception;
|
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");
|
Logger::info("Handling InterruptExecution packet");
|
||||||
|
|
||||||
if (targetControllerService.getTargetState() == Targets::TargetState::STOPPED) {
|
if (targetControllerService.getTargetState().executionState == Targets::TargetExecutionState::STOPPED) {
|
||||||
debugSession.pendingInterrupt = true;
|
debugSession.pendingInterrupt = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -32,7 +37,7 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
}
|
}
|
||||||
|
|
||||||
debugSession.waitingForBreak = false;
|
debugSession.waitingForBreak = false;
|
||||||
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
|
debugSession.connection.writePacket(TargetStopped{Signal::INTERRUPTED});
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to interrupt execution - " + exception.getMessage());
|
Logger::error("Failed to interrupt execution - " + exception.getMessage());
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::CommandPackets
|
namespace DebugServer::Gdb::CommandPackets
|
||||||
@@ -17,23 +18,28 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto decodedCommand = Packet::hexToData(
|
const auto decodedCommand = Services::StringService::dataFromHex(
|
||||||
std::string(this->data.begin() + 6, this->data.end())
|
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->command.erase(this->command.find_last_not_of(" ") + 1);
|
||||||
|
|
||||||
this->commandOptions = this->extractCommandOptions(this->command);
|
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.");
|
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) {
|
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) {
|
for (std::string::size_type cmdIndex = 1; cmdIndex < command.size(); ++cmdIndex) {
|
||||||
const auto cmdChar = command.at(cmdIndex);
|
const auto cmdChar = command.at(cmdIndex);
|
||||||
@@ -43,17 +49,17 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto option = std::string();
|
auto option = std::string{};
|
||||||
auto optionValue = std::optional<std::string>();
|
auto optionValue = std::optional<std::string>{};
|
||||||
|
|
||||||
bool quoted = false;
|
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) {
|
for (optIndex = cmdIndex + 1; optIndex < command.size(); ++optIndex) {
|
||||||
const auto optChar = command.at(optIndex);
|
const auto optChar = command.at(optIndex);
|
||||||
|
|
||||||
if (!option.empty() && ((!quoted && optChar == ' ') || (quoted && optChar == '"'))) {
|
if (!option.empty() && ((!quoted && optChar == ' ') || (quoted && optChar == '"'))) {
|
||||||
output.insert(std::pair(option, optionValue));
|
output.emplace(option, optionValue);
|
||||||
|
|
||||||
option.clear();
|
option.clear();
|
||||||
optionValue.reset();
|
optionValue.reset();
|
||||||
@@ -74,7 +80,7 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (optChar == '=') {
|
if (optChar == '=') {
|
||||||
optionValue = std::string();
|
optionValue = std::string{};
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +89,7 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!option.empty()) {
|
if (!option.empty()) {
|
||||||
output.insert(std::pair(option, optionValue));
|
output.emplace(option, optionValue);
|
||||||
cmdIndex = optIndex;
|
cmdIndex = optIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
|
|||||||
@@ -25,12 +25,13 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
{
|
{
|
||||||
if (this->data.size() < 6) {
|
if (this->data.size() < 6) {
|
||||||
throw Exception("Unexpected RemoveBreakpoint packet size");
|
throw Exception{"Unexpected RemoveBreakpoint packet size"};
|
||||||
}
|
}
|
||||||
|
|
||||||
// z0 = SW breakpoint, z1 = HW breakpoint
|
// z0 = SW breakpoint, z1 = HW breakpoint
|
||||||
this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ?
|
this->type = (this->data[1] == '0')
|
||||||
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
? BreakpointType::SOFTWARE_BREAKPOINT
|
||||||
|
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
||||||
|
|
||||||
const auto packetData = QString::fromLocal8Bit(
|
const auto packetData = QString::fromLocal8Bit(
|
||||||
reinterpret_cast<const char*>(this->data.data() + 2),
|
reinterpret_cast<const char*>(this->data.data() + 2),
|
||||||
@@ -39,29 +40,34 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
auto packetSegments = packetData.split(",");
|
auto packetSegments = packetData.split(",");
|
||||||
if (packetSegments.size() < 3) {
|
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;
|
bool conversionStatus = true;
|
||||||
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||||
|
|
||||||
if (!conversionStatus) {
|
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");
|
Logger::info("Handling RemoveBreakpoint packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
|
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
|
||||||
|
|
||||||
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
|
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: Monitor(std::move(monitorPacket))
|
: 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");
|
Logger::info("Handling ResetTarget packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -29,15 +34,15 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
targetControllerService.resetTarget();
|
targetControllerService.resetTarget();
|
||||||
Logger::info("Target reset complete");
|
Logger::info("Target reset complete");
|
||||||
|
|
||||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||||
"Target reset complete\n"
|
"Target reset complete\n"
|
||||||
"Current PC: 0x" + Services::StringService::toHex(targetControllerService.getProgramCounter()) + "\n"
|
"Current PC: 0x" + Services::StringService::toHex(targetControllerService.getProgramCounter()) + "\n"
|
||||||
"Use the 'continue' command to begin execution\n"
|
"Use the 'continue' command to begin execution\n"
|
||||||
)));
|
)});
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to reset target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
{
|
{
|
||||||
if (this->data.size() < 6) {
|
if (this->data.size() < 6) {
|
||||||
throw Exception("Unexpected SetBreakpoint packet size");
|
throw Exception{"Unexpected SetBreakpoint packet size"};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Z0 = SW breakpoint, Z1 = HW breakpoint
|
// Z0 = SW breakpoint, Z1 = HW breakpoint
|
||||||
this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ?
|
this->type = (this->data[1] == '0')
|
||||||
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
? BreakpointType::SOFTWARE_BREAKPOINT
|
||||||
|
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
||||||
|
|
||||||
auto packetData = QString::fromLocal8Bit(
|
auto packetData = QString::fromLocal8Bit(
|
||||||
reinterpret_cast<const char*>(this->data.data() + 2),
|
reinterpret_cast<const char*>(this->data.data() + 2),
|
||||||
@@ -41,18 +42,23 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
auto packetSegments = packetData.split(",");
|
auto packetSegments = packetData.split(",");
|
||||||
if (packetSegments.size() < 3) {
|
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;
|
bool conversionStatus = true;
|
||||||
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||||
|
|
||||||
if (!conversionStatus) {
|
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");
|
Logger::info("Handling SetBreakpoint packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -61,16 +67,16 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
"Rejecting breakpoint at address " + std::to_string(this->address)
|
"Rejecting breakpoint at address " + std::to_string(this->address)
|
||||||
+ " - unsupported breakpoint type"
|
+ " - unsupported breakpoint type"
|
||||||
);
|
);
|
||||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession.setExternalBreakpoint(this->address, targetControllerService);
|
debugSession.setExternalBreakpoint(this->address, targetControllerService);
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -18,21 +19,31 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
{
|
{
|
||||||
if (this->data.size() > 2) {
|
if (this->data.size() > 2) {
|
||||||
this->fromAddress = static_cast<Targets::TargetMemoryAddress>(
|
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");
|
Logger::info("Handling StepExecution packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
targetControllerService.stepTargetExecution(this->fromAddress);
|
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||||
|
if (this->fromAddress.has_value()) {
|
||||||
|
targetControllerService.setProgramCounter(*(this->fromAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
targetControllerService.stepTargetExecution();
|
||||||
debugSession.waitingForBreak = true;
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to step execution on target - " + exception.getMessage());
|
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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) 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");
|
Logger::info("Handling QuerySupport packet");
|
||||||
|
|
||||||
if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
|
if (
|
||||||
|
!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
|
||||||
&& !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
|
&& !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
|
||||||
) {
|
) {
|
||||||
// All GDB clients are expected to support 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
|
// 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(
|
void handle(
|
||||||
DebugSession& debugSession,
|
DebugSession& debugSession,
|
||||||
|
const TargetDescriptor& gdbTargetDescriptor,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
|
|||||||
@@ -45,23 +45,23 @@ namespace DebugServer::Gdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string Connection::getIpAddress() const {
|
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) {
|
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> Connection::readRawPackets() {
|
||||||
std::vector<RawPacket> output;
|
auto output = std::vector<RawPacket>{};
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const auto bytes = this->read();
|
const auto bytes = this->read();
|
||||||
|
|
||||||
std::size_t bufferSize = bytes.size();
|
auto bufferSize = bytes.size();
|
||||||
for (std::size_t byteIndex = 0; byteIndex < bufferSize; byteIndex++) {
|
for (auto byteIndex = std::size_t{0}; byteIndex < bufferSize; byteIndex++) {
|
||||||
auto byte = bytes[byteIndex];
|
auto byte = bytes[byteIndex];
|
||||||
|
|
||||||
if (byte == 0x03) {
|
if (byte == 0x03) {
|
||||||
@@ -131,7 +131,7 @@ namespace DebugServer::Gdb
|
|||||||
Logger::debug(
|
Logger::debug(
|
||||||
"Read GDB packet: "
|
"Read GDB packet: "
|
||||||
+ Services::StringService::replaceUnprintable(
|
+ 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;
|
int attempts = 0;
|
||||||
const auto rawPacket = packet.toRawPacket();
|
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 {
|
do {
|
||||||
if (attempts > 10) {
|
if (attempts > 10) {
|
||||||
throw ClientCommunicationError(
|
throw ClientCommunicationError{
|
||||||
"Failed to write GDB response packet - client failed to acknowledge receipt - retry limit reached"
|
"Failed to write GDB response packet - client failed to acknowledge receipt - retry limit reached"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->write(rawPacket);
|
this->write(rawPacket);
|
||||||
@@ -177,7 +177,7 @@ namespace DebugServer::Gdb
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (socketFileDescriptor < 0) {
|
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;
|
this->socketFileDescriptor = socketFileDescriptor;
|
||||||
@@ -219,7 +219,7 @@ namespace DebugServer::Gdb
|
|||||||
if (eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()) {
|
if (eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()) {
|
||||||
// Interrupted
|
// Interrupted
|
||||||
this->interruptEventNotifier.clear();
|
this->interruptEventNotifier.clear();
|
||||||
throw DebugServerInterrupted();
|
throw DebugServerInterrupted{};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto bytesToRead = bytes.value_or(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE);
|
const auto bytesToRead = bytes.value_or(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE);
|
||||||
@@ -232,14 +232,14 @@ namespace DebugServer::Gdb
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (bytesRead < 0) {
|
if (bytesRead < 0) {
|
||||||
throw ClientCommunicationError(
|
throw ClientCommunicationError{
|
||||||
"Failed to read data from GDB client - error code: " + std::to_string(errno)
|
"Failed to read data from GDB client - error code: " + std::to_string(errno)
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesRead == 0) {
|
if (bytesRead == 0) {
|
||||||
// Client has disconnected
|
// Client has disconnected
|
||||||
throw ClientDisconnected();
|
throw ClientDisconnected{};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesRead != output.size()) {
|
if (bytesRead != output.size()) {
|
||||||
@@ -250,7 +250,7 @@ namespace DebugServer::Gdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
|
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()) {
|
if (!bytes.empty()) {
|
||||||
return bytes.front();
|
return bytes.front();
|
||||||
@@ -263,19 +263,18 @@ namespace DebugServer::Gdb
|
|||||||
if (::write(this->socketFileDescriptor.value(), buffer.data(), buffer.size()) == -1) {
|
if (::write(this->socketFileDescriptor.value(), buffer.data(), buffer.size()) == -1) {
|
||||||
if (errno == EPIPE || errno == ECONNRESET) {
|
if (errno == EPIPE || errno == ECONNRESET) {
|
||||||
// Connection was closed
|
// 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: "
|
"Failed to write " + std::to_string(buffer.size()) + " bytes to GDP client socket - error no: "
|
||||||
+ std::to_string(errno)
|
+ std::to_string(errno)
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::disableReadInterrupts() {
|
void Connection::disableReadInterrupts() {
|
||||||
this->epollInstance.removeEntry(this->interruptEventNotifier.getFileDescriptor());
|
this->epollInstance.removeEntry(this->interruptEventNotifier.getFileDescriptor());
|
||||||
|
|
||||||
this->readInterruptEnabled = false;
|
this->readInterruptEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
static constexpr auto ABSOLUTE_MAXIMUM_PACKET_READ_SIZE = 2097000; // 2MiB
|
static constexpr auto ABSOLUTE_MAXIMUM_PACKET_READ_SIZE = 2097000; // 2MiB
|
||||||
|
|
||||||
explicit Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier);
|
Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier);
|
||||||
|
|
||||||
Connection() = delete;
|
Connection() = delete;
|
||||||
Connection(const Connection&) = delete;
|
Connection(const Connection&) = delete;
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ namespace DebugServer::Gdb
|
|||||||
DebugSession::DebugSession(
|
DebugSession::DebugSession(
|
||||||
Connection&& connection,
|
Connection&& connection,
|
||||||
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
||||||
const TargetDescriptor& targetDescriptor,
|
|
||||||
const GdbDebugServerConfig& serverConfig
|
const GdbDebugServerConfig& serverConfig
|
||||||
)
|
)
|
||||||
: connection(std::move(connection))
|
: connection(std::move(connection))
|
||||||
, supportedFeatures(supportedFeatures)
|
, supportedFeatures(supportedFeatures)
|
||||||
, gdbTargetDescriptor(targetDescriptor)
|
|
||||||
, serverConfig(serverConfig)
|
, serverConfig(serverConfig)
|
||||||
{
|
{
|
||||||
this->supportedFeatures.insert({
|
this->supportedFeatures.emplace(
|
||||||
Feature::PACKET_SIZE, std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE)
|
Feature::PACKET_SIZE,
|
||||||
});
|
std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE)
|
||||||
|
);
|
||||||
|
|
||||||
EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>());
|
EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>());
|
||||||
}
|
}
|
||||||
@@ -35,18 +34,15 @@ namespace DebugServer::Gdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address);
|
const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address);
|
||||||
|
|
||||||
if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) {
|
if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) {
|
||||||
// We already have an external breakpoint at this address
|
// We already have an external breakpoint at this address
|
||||||
this->internalBreakpointsByAddress.insert(std::pair(address, externalBreakpointIt->second));
|
this->internalBreakpointsByAddress.emplace(address, externalBreakpointIt->second);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->internalBreakpointsByAddress.insert(
|
this->internalBreakpointsByAddress.emplace(
|
||||||
std::pair(
|
|
||||||
address,
|
address,
|
||||||
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
|
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,15 +74,13 @@ namespace DebugServer::Gdb
|
|||||||
|
|
||||||
if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) {
|
if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) {
|
||||||
// We already have an internal breakpoint at this address
|
// We already have an internal breakpoint at this address
|
||||||
this->externalBreakpointsByAddress.insert(std::pair(address, internalBreakpointIt->second));
|
this->externalBreakpointsByAddress.emplace(address, internalBreakpointIt->second);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->externalBreakpointsByAddress.insert(
|
this->externalBreakpointsByAddress.emplace(
|
||||||
std::pair(
|
|
||||||
address,
|
address,
|
||||||
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
|
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures;
|
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.
|
* The current server configuration.
|
||||||
*/
|
*/
|
||||||
@@ -92,7 +87,6 @@ namespace DebugServer::Gdb
|
|||||||
DebugSession(
|
DebugSession(
|
||||||
Connection&& connection,
|
Connection&& connection,
|
||||||
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
||||||
const TargetDescriptor& targetDescriptor,
|
|
||||||
const GdbDebugServerConfig& serverConfig
|
const GdbDebugServerConfig& serverConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ namespace DebugServer::Gdb::Exceptions
|
|||||||
class DebugServerInterrupted: public ::Exceptions::Exception
|
class DebugServerInterrupted: public ::Exceptions::Exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit DebugServerInterrupted() = default;
|
DebugServerInterrupted() = default;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,7 @@
|
|||||||
#include "CommandPackets/HelpMonitorInfo.hpp"
|
#include "CommandPackets/HelpMonitorInfo.hpp"
|
||||||
#include "CommandPackets/BloomVersion.hpp"
|
#include "CommandPackets/BloomVersion.hpp"
|
||||||
#include "CommandPackets/BloomVersionMachine.hpp"
|
#include "CommandPackets/BloomVersionMachine.hpp"
|
||||||
#include "CommandPackets/GenerateSvd.hpp"
|
|
||||||
#include "CommandPackets/Detach.hpp"
|
#include "CommandPackets/Detach.hpp"
|
||||||
#include "CommandPackets/EepromFill.hpp"
|
|
||||||
|
|
||||||
#ifndef EXCLUDE_INSIGHT
|
#ifndef EXCLUDE_INSIGHT
|
||||||
#include "CommandPackets/ActivateInsight.hpp"
|
#include "CommandPackets/ActivateInsight.hpp"
|
||||||
@@ -51,10 +49,12 @@ namespace DebugServer::Gdb
|
|||||||
|
|
||||||
GdbRspDebugServer::GdbRspDebugServer(
|
GdbRspDebugServer::GdbRspDebugServer(
|
||||||
const DebugServerConfig& debugServerConfig,
|
const DebugServerConfig& debugServerConfig,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
EventListener& eventListener,
|
EventListener& eventListener,
|
||||||
EventFdNotifier& eventNotifier
|
EventFdNotifier& eventNotifier
|
||||||
)
|
)
|
||||||
: debugServerConfig(GdbDebugServerConfig(debugServerConfig))
|
: debugServerConfig(GdbDebugServerConfig(debugServerConfig))
|
||||||
|
, targetDescriptor(targetDescriptor)
|
||||||
, eventListener(eventListener)
|
, eventListener(eventListener)
|
||||||
, interruptEventNotifier(eventNotifier)
|
, interruptEventNotifier(eventNotifier)
|
||||||
{}
|
{}
|
||||||
@@ -63,28 +63,27 @@ namespace DebugServer::Gdb
|
|||||||
this->socketAddress.sin_family = AF_INET;
|
this->socketAddress.sin_family = AF_INET;
|
||||||
this->socketAddress.sin_port = htons(this->debugServerConfig.listeningPortNumber);
|
this->socketAddress.sin_port = htons(this->debugServerConfig.listeningPortNumber);
|
||||||
|
|
||||||
if (::inet_pton(
|
if (
|
||||||
|
::inet_pton(
|
||||||
AF_INET,
|
AF_INET,
|
||||||
this->debugServerConfig.listeningAddress.c_str(),
|
this->debugServerConfig.listeningAddress.c_str(),
|
||||||
&(this->socketAddress.sin_addr)
|
&(this->socketAddress.sin_addr)
|
||||||
) == 0
|
) == 0
|
||||||
) {
|
) {
|
||||||
// Invalid IP address
|
// Invalid IP address
|
||||||
throw InvalidConfig(
|
throw InvalidConfig{
|
||||||
"Invalid IP address provided in config file: (\"" + this->debugServerConfig.listeningAddress
|
"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) {
|
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;
|
const auto enableReuseAddressSocketOption = int{1};
|
||||||
|
if (
|
||||||
if (::setsockopt(
|
::setsockopt(
|
||||||
socketFileDescriptor,
|
socketFileDescriptor,
|
||||||
SOL_SOCKET,
|
SOL_SOCKET,
|
||||||
SO_REUSEADDR,
|
SO_REUSEADDR,
|
||||||
@@ -95,14 +94,17 @@ namespace DebugServer::Gdb
|
|||||||
Logger::error("Failed to set socket SO_REUSEADDR option.");
|
Logger::error("Failed to set socket SO_REUSEADDR option.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::bind(
|
if (
|
||||||
|
::bind(
|
||||||
socketFileDescriptor,
|
socketFileDescriptor,
|
||||||
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
|
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
|
||||||
sizeof(this->socketAddress)
|
sizeof(this->socketAddress)
|
||||||
) < 0
|
) < 0
|
||||||
) {
|
) {
|
||||||
throw Exception("Failed to bind address. The selected port number ("
|
throw Exception{
|
||||||
+ std::to_string(this->debugServerConfig.listeningPortNumber) + ") may be in use.");
|
"Failed to bind address. The selected port number ("
|
||||||
|
+ std::to_string(this->debugServerConfig.listeningPortNumber) + ") may be in use."
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->serverSocketFileDescriptor = socketFileDescriptor;
|
this->serverSocketFileDescriptor = socketFileDescriptor;
|
||||||
@@ -120,12 +122,8 @@ namespace DebugServer::Gdb
|
|||||||
Logger::info("GDB RSP address: " + this->debugServerConfig.listeningAddress);
|
Logger::info("GDB RSP address: " + this->debugServerConfig.listeningAddress);
|
||||||
Logger::info("GDB RSP port: " + std::to_string(this->debugServerConfig.listeningPortNumber));
|
Logger::info("GDB RSP port: " + std::to_string(this->debugServerConfig.listeningPortNumber));
|
||||||
|
|
||||||
this->eventListener.registerCallbackForEventType<Events::TargetExecutionStopped>(
|
this->eventListener.registerCallbackForEventType<Events::TargetStateChanged>(
|
||||||
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
|
std::bind(&GdbRspDebugServer::onTargetStateChanged, this, std::placeholders::_1)
|
||||||
);
|
|
||||||
|
|
||||||
this->eventListener.registerCallbackForEventType<Events::TargetExecutionResumed>(
|
|
||||||
std::bind(&GdbRspDebugServer::onTargetExecutionResumed, this, std::placeholders::_1)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Services::ProcessService::isManagedByClion()) {
|
if (Services::ProcessService::isManagedByClion()) {
|
||||||
@@ -149,7 +147,6 @@ namespace DebugServer::Gdb
|
|||||||
Logger::info("Waiting for GDB RSP connection");
|
Logger::info("Waiting for GDB RSP connection");
|
||||||
|
|
||||||
auto connection = this->waitForConnection();
|
auto connection = this->waitForConnection();
|
||||||
|
|
||||||
Logger::info("Accepted GDP RSP connection from " + connection.getIpAddress());
|
Logger::info("Accepted GDP RSP connection from " + connection.getIpAddress());
|
||||||
|
|
||||||
this->startDebugSession(std::move(connection));
|
this->startDebugSession(std::move(connection));
|
||||||
@@ -159,9 +156,13 @@ namespace DebugServer::Gdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto commandPacket = this->waitForCommandPacket();
|
const auto commandPacket = this->waitForCommandPacket();
|
||||||
|
|
||||||
if (commandPacket) {
|
if (commandPacket) {
|
||||||
commandPacket->handle(*(this->getActiveDebugSession()), this->targetControllerService);
|
commandPacket->handle(
|
||||||
|
*(this->getActiveDebugSession()),
|
||||||
|
this->getGdbTargetDescriptor(),
|
||||||
|
this->targetDescriptor,
|
||||||
|
this->targetControllerService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const ClientDisconnected&) {
|
} catch (const ClientDisconnected&) {
|
||||||
@@ -195,28 +196,25 @@ namespace DebugServer::Gdb
|
|||||||
|
|
||||||
Connection GdbRspDebugServer::waitForConnection() {
|
Connection GdbRspDebugServer::waitForConnection() {
|
||||||
if (::listen(this->serverSocketFileDescriptor.value(), 3) != 0) {
|
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();
|
const auto eventFileDescriptor = this->epollInstance.waitForEvent();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!eventFileDescriptor.has_value()
|
!eventFileDescriptor.has_value()
|
||||||
|| eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()
|
|| *eventFileDescriptor == this->interruptEventNotifier.getFileDescriptor()
|
||||||
) {
|
) {
|
||||||
this->interruptEventNotifier.clear();
|
this->interruptEventNotifier.clear();
|
||||||
throw DebugServerInterrupted();
|
throw DebugServerInterrupted{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return Connection(
|
return {this->serverSocketFileDescriptor.value(), this->interruptEventNotifier};
|
||||||
this->serverSocketFileDescriptor.value(),
|
|
||||||
this->interruptEventNotifier
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() {
|
std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() {
|
||||||
auto* debugSession = this->getActiveDebugSession();
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
const auto rawPackets = debugSession->connection.readRawPackets();
|
const auto rawPackets = debugSession->connection.readRawPackets();
|
||||||
|
assert(!rawPackets.empty());
|
||||||
|
|
||||||
if (rawPackets.size() > 1) {
|
if (rawPackets.size() > 1) {
|
||||||
const auto& firstRawPacket = rawPackets.front();
|
const auto& firstRawPacket = rawPackets.front();
|
||||||
@@ -224,53 +222,56 @@ namespace DebugServer::Gdb
|
|||||||
if (firstRawPacket.size() == 5 && firstRawPacket[1] == 0x03) {
|
if (firstRawPacket.size() == 5 && firstRawPacket[1] == 0x03) {
|
||||||
// Interrupt packet that came in too quickly before another packet
|
// Interrupt packet that came in too quickly before another packet
|
||||||
debugSession->pendingInterrupt = true;
|
debugSession->pendingInterrupt = true;
|
||||||
}
|
|
||||||
|
|
||||||
// We only process the last packet - any others will probably be duplicates from an impatient client.
|
} else {
|
||||||
Logger::warning("Multiple packets received from GDB - only the most recent will be processed");
|
Logger::warning("Multiple packets received from GDB - only the most recent will be processed");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this->resolveCommandPacket(rawPackets.back());
|
return this->resolveCommandPacket(rawPackets.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CommandPacket> GdbRspDebugServer::resolveCommandPacket(const RawPacket& rawPacket) {
|
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) {
|
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
||||||
// Interrupt request
|
// Interrupt request
|
||||||
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
|
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') {
|
if (rawPacket[1] == 'D') {
|
||||||
return std::make_unique<CommandPackets::Detach>(rawPacket);
|
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
|
* 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.
|
* looking for a command identifier string.
|
||||||
*/
|
*/
|
||||||
if (rawPacketString.find("qSupported") == 1) {
|
if (rawPacketString.find("qSupported") == 0) {
|
||||||
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacketString[1] == 'c') {
|
if (rawPacketString.find("qRcmd") == 0) {
|
||||||
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawPacketString[1] == 's') {
|
|
||||||
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawPacketString[1] == 'Z') {
|
|
||||||
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawPacketString[1] == 'z') {
|
|
||||||
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawPacketString.find("qRcmd") == 1) {
|
|
||||||
// This is a monitor packet
|
// This is a monitor packet
|
||||||
auto monitorCommand = std::make_unique<CommandPackets::Monitor>(rawPacket);
|
auto monitorCommand = std::make_unique<CommandPackets::Monitor>(rawPacket);
|
||||||
|
|
||||||
@@ -290,13 +291,6 @@ namespace DebugServer::Gdb
|
|||||||
return std::make_unique<CommandPackets::ResetTarget>(std::move(*(monitorCommand.release())));
|
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
|
#ifndef EXCLUDE_INSIGHT
|
||||||
if (monitorCommand->command.find("insight") == 0) {
|
if (monitorCommand->command.find("insight") == 0) {
|
||||||
return std::make_unique<CommandPackets::ActivateInsight>(std::move(*(monitorCommand.release())));
|
return std::make_unique<CommandPackets::ActivateInsight>(std::move(*(monitorCommand.release())));
|
||||||
@@ -304,19 +298,34 @@ namespace DebugServer::Gdb
|
|||||||
#endif
|
#endif
|
||||||
return monitorCommand;
|
return monitorCommand;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<CommandPacket>(rawPacket);
|
return std::make_unique<CommandPacket>(rawPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped& stoppedEvent) {
|
void GdbRspDebugServer::onTargetStateChanged(const Events::TargetStateChanged& event) {
|
||||||
using Services::StringService;
|
using Targets::TargetExecutionState;
|
||||||
|
|
||||||
auto* debugSession = this->getActiveDebugSession();
|
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 {
|
try {
|
||||||
if (debugSession != nullptr && debugSession->waitingForBreak) {
|
if (executionState == TargetExecutionState::STOPPED && debugSession->waitingForBreak) {
|
||||||
this->handleTargetStoppedGdbResponse(stoppedEvent.programCounter);
|
this->handleTargetStoppedGdbResponse(event.newState.programCounter.load().value());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executionState == TargetExecutionState::RUNNING || executionState == TargetExecutionState::STEPPING) {
|
||||||
|
this->handleTargetResumedGdbResponse();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const ClientDisconnected&) {
|
} catch (const ClientDisconnected&) {
|
||||||
@@ -335,41 +344,12 @@ namespace DebugServer::Gdb
|
|||||||
// Server was interrupted
|
// Server was interrupted
|
||||||
Logger::debug("GDB RSP interrupted");
|
Logger::debug("GDB RSP interrupted");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to handle target execution stopped event - " + exception.getMessage());
|
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) {
|
void GdbRspDebugServer::handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress) {
|
||||||
auto* debugSession = this->getActiveDebugSession();
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
|
|
||||||
@@ -377,7 +357,7 @@ namespace DebugServer::Gdb
|
|||||||
debugSession->terminateRangeSteppingSession(this->targetControllerService);
|
debugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::TRAP));
|
debugSession->connection.writePacket(ResponsePackets::TargetStopped{Signal::TRAP});
|
||||||
debugSession->waitingForBreak = false;
|
debugSession->waitingForBreak = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +372,7 @@ namespace DebugServer::Gdb
|
|||||||
debugSession->terminateRangeSteppingSession(this->targetControllerService);
|
debugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
}
|
}
|
||||||
|
|
||||||
debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::INTERRUPTED));
|
debugSession->connection.writePacket(ResponsePackets::TargetStopped{Signal::INTERRUPTED});
|
||||||
debugSession->pendingInterrupt = false;
|
debugSession->pendingInterrupt = false;
|
||||||
debugSession->waitingForBreak = false;
|
debugSession->waitingForBreak = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "src/Helpers/EpollInstance.hpp"
|
#include "src/Helpers/EpollInstance.hpp"
|
||||||
#include "src/Helpers/EventFdNotifier.hpp"
|
#include "src/Helpers/EventFdNotifier.hpp"
|
||||||
#include "src/Services/TargetControllerService.hpp"
|
#include "src/Services/TargetControllerService.hpp"
|
||||||
|
#include "src/Targets/TargetDescriptor.hpp"
|
||||||
|
|
||||||
#include "Connection.hpp"
|
#include "Connection.hpp"
|
||||||
#include "TargetDescriptor.hpp"
|
#include "TargetDescriptor.hpp"
|
||||||
@@ -24,8 +25,7 @@
|
|||||||
#include "Feature.hpp"
|
#include "Feature.hpp"
|
||||||
#include "CommandPackets/CommandPacket.hpp"
|
#include "CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
#include "src/EventManager/Events/TargetExecutionStopped.hpp"
|
#include "src/EventManager/Events/TargetStateChanged.hpp"
|
||||||
#include "src/EventManager/Events/TargetExecutionResumed.hpp"
|
|
||||||
|
|
||||||
namespace DebugServer::Gdb
|
namespace DebugServer::Gdb
|
||||||
{
|
{
|
||||||
@@ -43,6 +43,7 @@ namespace DebugServer::Gdb
|
|||||||
public:
|
public:
|
||||||
explicit GdbRspDebugServer(
|
explicit GdbRspDebugServer(
|
||||||
const DebugServerConfig& debugServerConfig,
|
const DebugServerConfig& debugServerConfig,
|
||||||
|
const Targets::TargetDescriptor& targetDescriptor,
|
||||||
EventListener& eventListener,
|
EventListener& eventListener,
|
||||||
EventFdNotifier& eventNotifier
|
EventFdNotifier& eventNotifier
|
||||||
);
|
);
|
||||||
@@ -78,14 +79,10 @@ namespace DebugServer::Gdb
|
|||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
|
||||||
* User project configuration specific to the GDB RSP debug server.
|
|
||||||
*/
|
|
||||||
GdbDebugServerConfig debugServerConfig;
|
GdbDebugServerConfig debugServerConfig;
|
||||||
|
|
||||||
/**
|
const Targets::TargetDescriptor& targetDescriptor;
|
||||||
* The DebugServerComponent's event listener.
|
|
||||||
*/
|
|
||||||
EventListener& eventListener;
|
EventListener& eventListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -188,17 +185,7 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
virtual const TargetDescriptor& getGdbTargetDescriptor() = 0;
|
virtual const TargetDescriptor& getGdbTargetDescriptor() = 0;
|
||||||
|
|
||||||
/**
|
void onTargetStateChanged(const Events::TargetStateChanged& event);
|
||||||
* 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&);
|
|
||||||
|
|
||||||
virtual void handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress);
|
virtual void handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress);
|
||||||
virtual void handleTargetResumedGdbResponse();
|
virtual void handleTargetResumedGdbResponse();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,65 +32,6 @@ namespace DebugServer::Gdb
|
|||||||
Packet& operator = (const Packet& other) = default;
|
Packet& operator = (const Packet& other) = default;
|
||||||
Packet& operator = (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:
|
protected:
|
||||||
std::vector<unsigned char> data;
|
std::vector<unsigned char> data;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
struct ProgrammingSession
|
struct ProgrammingSession
|
||||||
{
|
{
|
||||||
Targets::TargetMemoryAddress startAddress = 0x00;
|
Targets::TargetMemoryAddress startAddress;
|
||||||
Targets::TargetMemoryBuffer buffer;
|
Targets::TargetMemoryBuffer buffer;
|
||||||
|
|
||||||
ProgrammingSession(
|
ProgrammingSession(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace DebugServer::Gdb
|
namespace DebugServer::Gdb
|
||||||
{
|
{
|
||||||
@@ -10,19 +9,17 @@ namespace DebugServer::Gdb
|
|||||||
/*
|
/*
|
||||||
* GDB defines a set of registers for each target architecture.
|
* 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.
|
* registers to IDs is hardcoded in GDB, GDB server implementations are expected to be aware of this mapping.
|
||||||
*/
|
*/
|
||||||
struct RegisterDescriptor
|
struct RegisterDescriptor
|
||||||
{
|
{
|
||||||
GdbRegisterId id;
|
GdbRegisterId id;
|
||||||
std::uint16_t size;
|
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)
|
: id(id)
|
||||||
, size(size)
|
, size(size)
|
||||||
, name(name)
|
|
||||||
{};
|
{};
|
||||||
|
|
||||||
bool operator == (const RegisterDescriptor& other) const {
|
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 Outputs Bloom's version information.
|
||||||
version machine Outputs Bloom's version information in JSON format.
|
version machine Outputs Bloom's version information in JSON format.
|
||||||
@ACTIVATE_INSIGHT_HELP_TEXT@
|
@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.
|
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) {
|
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)
|
: supportedFeatures(supportedFeatures)
|
||||||
{
|
{
|
||||||
auto output = std::string("qSupported:");
|
auto output = std::string{"qSupported:"};
|
||||||
static const auto gdbFeatureMapping = getGdbFeatureToNameMapping();
|
static const auto gdbFeatureMapping = getGdbFeatureToNameMapping();
|
||||||
|
|
||||||
for (const auto& supportedFeature : this->supportedFeatures) {
|
for (const auto& supportedFeature : this->supportedFeatures) {
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ namespace DebugServer::Gdb::ResponsePackets
|
|||||||
: signal(signal)
|
: signal(signal)
|
||||||
, stopReason(stopReason)
|
, 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()) {
|
if (this->stopReason.has_value()) {
|
||||||
static const auto stopReasonMapping = getStopReasonToNameMapping();
|
static const auto stopReasonMapping = getStopReasonToNameMapping();
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ namespace DebugServer::Gdb
|
|||||||
};
|
};
|
||||||
|
|
||||||
static inline BiMap<StopReason, std::string> getStopReasonToNameMapping() {
|
static inline BiMap<StopReason, std::string> getStopReasonToNameMapping() {
|
||||||
return BiMap<StopReason, std::string>({
|
return BiMap<StopReason, std::string>{
|
||||||
{StopReason::HARDWARE_BREAKPOINT, "hwbreak"},
|
{StopReason::HARDWARE_BREAKPOINT, "hwbreak"},
|
||||||
{StopReason::SOFTWARE_BREAKPOINT, "swbreak"},
|
{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/Helpers/BiMap.hpp"
|
||||||
#include "src/Targets/TargetDescriptor.hpp"
|
#include "src/Targets/TargetDescriptor.hpp"
|
||||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||||
#include "src/Targets/TargetRegister.hpp"
|
|
||||||
#include "src/Targets/TargetMemory.hpp"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
#include "RegisterDescriptor.hpp"
|
#include "RegisterDescriptor.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb
|
namespace DebugServer::Gdb
|
||||||
{
|
{
|
||||||
|
using GdbMemoryAddress = std::uint32_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GDB target descriptor.
|
* GDB target descriptor.
|
||||||
*/
|
*/
|
||||||
class TargetDescriptor
|
class TargetDescriptor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Targets::TargetDescriptor targetDescriptor;
|
|
||||||
std::map<GdbRegisterId, RegisterDescriptor> gdbRegisterDescriptorsById;
|
std::map<GdbRegisterId, RegisterDescriptor> gdbRegisterDescriptorsById;
|
||||||
|
std::map<GdbRegisterId, const Targets::TargetRegisterDescriptor*> targetRegisterDescriptorsByGdbId;
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
virtual ~TargetDescriptor() = default;
|
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
|
* @param address
|
||||||
* @return
|
* @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
|
* This function should translate a GDB memory address to a target memory address. This should strip any
|
||||||
* target register descriptor ID isn't mapped to any GDB register.
|
* GDB-specific masks and return an address that can be used within Bloom.
|
||||||
*
|
*
|
||||||
* @param registerDescriptorId
|
* @param address
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
std::optional<GdbRegisterId> getGdbRegisterIdFromTargetRegisterDescriptorId(
|
virtual Targets::TargetMemoryAddress translateGdbAddress(GdbMemoryAddress address) const = 0;
|
||||||
Targets::TargetRegisterDescriptorId targetRegisterDescriptorId
|
|
||||||
) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @param address
|
||||||
* register on our end. For example, for AVR targets, the GDB register ID 34 maps to the program counter. But
|
* @param addressSpaceDescriptor
|
||||||
* the program counter is not treated like any other register in Bloom (there's no TargetRegisterDescriptor for
|
* @param memorySegmentDescriptor
|
||||||
* it). So in that case, the GDB register ID is not mapped to any target register descriptor ID.
|
|
||||||
*
|
|
||||||
* @param gdbRegisterId
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
std::optional<Targets::TargetRegisterDescriptorId> getTargetRegisterDescriptorIdFromGdbRegisterId(
|
virtual GdbMemoryAddress translateTargetMemoryAddress(
|
||||||
GdbRegisterId gdbRegisterId
|
Targets::TargetMemoryAddress address,
|
||||||
) const;
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
protected:
|
) const = 0;
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/CmsisDapInterface.cpp
|
${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/Command.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Response.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/AvrCommand.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrResponse.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/EdbgAvr8Session.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.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/EdbgDevice.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AtmelICE/AtmelIce.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AtmelICE/AtmelIce.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/PowerDebugger/PowerDebugger.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/XplainedNano/XplainedNano.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.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/Protocols/WchLink/WchLinkInterface.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.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/TargetPowerManagementInterface.hpp"
|
||||||
|
|
||||||
#include "TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp"
|
#include "TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp"
|
||||||
#include "TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp"
|
#include "TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp"
|
||||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp"
|
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||||
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
|
#include "src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp"
|
||||||
#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp"
|
|
||||||
|
|
||||||
#include "TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
#include "TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
||||||
#include "TargetInterfaces/RiscV/RiscVProgramInterface.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"
|
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||||
|
|
||||||
@@ -44,6 +46,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
|
||||||
|
virtual bool isInitialised() const = 0;
|
||||||
|
|
||||||
virtual std::string getName() = 0;
|
virtual std::string getName() = 0;
|
||||||
|
|
||||||
virtual std::string getSerialNumber() = 0;
|
virtual std::string getSerialNumber() = 0;
|
||||||
@@ -75,11 +79,9 @@ public:
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
||||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
|
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
|
|
||||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
|
|
||||||
) {
|
) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -94,8 +96,9 @@ public:
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface(
|
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface(
|
||||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
|
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||||
|
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||||
) {
|
) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -110,7 +113,10 @@ public:
|
|||||||
*
|
*
|
||||||
* @return
|
* @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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,19 +133,31 @@ public:
|
|||||||
*
|
*
|
||||||
* @return
|
* @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;
|
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());
|
this->setConfiguration(this->configurationIndex.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cmsisHidInterface = Usb::HidInterface(
|
auto cmsisHidInterface = Usb::HidInterface{
|
||||||
this->cmsisHidInterfaceNumber,
|
this->cmsisHidInterfaceNumber,
|
||||||
this->getEndpointMaxPacketSize(
|
this->getEndpointMaxPacketSize(
|
||||||
this->getFirstEndpointAddress(this->cmsisHidInterfaceNumber, LIBUSB_ENDPOINT_IN)
|
this->getFirstEndpointAddress(this->cmsisHidInterfaceNumber, LIBUSB_ENDPOINT_IN)
|
||||||
),
|
),
|
||||||
this->vendorId,
|
this->vendorId,
|
||||||
this->productId
|
this->productId
|
||||||
);
|
};
|
||||||
|
|
||||||
cmsisHidInterface.init();
|
cmsisHidInterface.init();
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ namespace DebugToolDrivers::Microchip
|
|||||||
* Because of this, we have to enforce a minimum time gap between commands. See comment
|
* Because of this, we have to enforce a minimum time gap between commands. See comment
|
||||||
* in CmsisDapInterface class declaration for more info.
|
* 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.
|
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
|
||||||
if (!this->sessionStarted) {
|
if (!this->sessionStarted) {
|
||||||
@@ -70,9 +70,7 @@ namespace DebugToolDrivers::Microchip
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->edbgAvrIspInterface = std::make_unique<EdbgAvrIspInterface>(this->edbgInterface.get());
|
this->initialised = true;
|
||||||
|
|
||||||
this->setInitialised(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EdbgDevice::close() {
|
void EdbgDevice::close() {
|
||||||
@@ -82,21 +80,50 @@ namespace DebugToolDrivers::Microchip
|
|||||||
|
|
||||||
this->edbgInterface->getUsbHidInterface().close();
|
this->edbgInterface->getUsbHidInterface().close();
|
||||||
UsbDevice::close();
|
UsbDevice::close();
|
||||||
|
this->initialised = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface(
|
bool EdbgDevice::isInitialised() const {
|
||||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
|
return this->initialised;
|
||||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
}
|
||||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
|
|
||||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
|
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) {
|
if (this->edbgAvr8Interface == nullptr) {
|
||||||
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(
|
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(
|
||||||
this->edbgInterface.get(),
|
this->edbgInterface.get(),
|
||||||
targetConfig,
|
targetDescriptionFile,
|
||||||
targetFamily,
|
targetConfig
|
||||||
targetParameters,
|
|
||||||
targetRegisterDescriptorsById
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this->configureAvr8Interface();
|
this->configureAvr8Interface();
|
||||||
@@ -105,40 +132,30 @@ namespace DebugToolDrivers::Microchip
|
|||||||
return this->edbgAvr8Interface.get();
|
return this->edbgAvr8Interface.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EdbgDevice::getSerialNumber() {
|
TargetInterfaces::Microchip::Avr8::AvrIspInterface* EdbgDevice::getAvrIspInterface(
|
||||||
using namespace CommandFrames::Discovery;
|
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||||
using ResponseFrames::Discovery::ResponseId;
|
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||||
|
) {
|
||||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
if (this->edbgAvrIspInterface == nullptr) {
|
||||||
Query(QueryContext::SERIAL_NUMBER)
|
this->edbgAvrIspInterface = std::make_unique<EdbgAvrIspInterface>(
|
||||||
|
this->edbgInterface.get(),
|
||||||
|
targetDescriptionFile
|
||||||
);
|
);
|
||||||
|
|
||||||
if (responseFrame.id != ResponseId::OK) {
|
this->configureAvr8Interface();
|
||||||
throw DeviceInitializationFailure(
|
|
||||||
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto data = responseFrame.getPayloadData();
|
return this->edbgAvrIspInterface.get();
|
||||||
return std::string(data.begin(), data.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string EdbgDevice::getFirmwareVersionString() {
|
|
||||||
// TODO: Implement this
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EdbgDevice::startSession() {
|
void EdbgDevice::startSession() {
|
||||||
using namespace CommandFrames::HouseKeeping;
|
using namespace CommandFrames::HouseKeeping;
|
||||||
using ResponseFrames::HouseKeeping::ResponseId;
|
using ResponseFrames::HouseKeeping::ResponseId;
|
||||||
|
|
||||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(StartSession{});
|
||||||
StartSession()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (responseFrame.id == ResponseId::FAILED) {
|
if (responseFrame.id == ResponseId::FAILED) {
|
||||||
// Failed response returned!
|
// Failed response returned!
|
||||||
throw DeviceInitializationFailure("Failed to start session with EDBG device!");
|
throw DeviceInitializationFailure{"Failed to start session with EDBG device!"};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->sessionStarted = true;
|
this->sessionStarted = true;
|
||||||
@@ -148,13 +165,10 @@ namespace DebugToolDrivers::Microchip
|
|||||||
using namespace CommandFrames::HouseKeeping;
|
using namespace CommandFrames::HouseKeeping;
|
||||||
using ResponseFrames::HouseKeeping::ResponseId;
|
using ResponseFrames::HouseKeeping::ResponseId;
|
||||||
|
|
||||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(EndSession{});
|
||||||
EndSession()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (responseFrame.id == ResponseId::FAILED) {
|
if (responseFrame.id == ResponseId::FAILED) {
|
||||||
// Failed response returned!
|
// Failed response returned!
|
||||||
throw DeviceFailure("Failed to end session with EDBG device!");
|
throw DeviceFailure{"Failed to end session with EDBG device!"};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->sessionStarted = false;
|
this->sessionStarted = false;
|
||||||
|
|||||||
@@ -50,22 +50,7 @@ namespace DebugToolDrivers::Microchip
|
|||||||
*/
|
*/
|
||||||
void close() override;
|
void close() override;
|
||||||
|
|
||||||
TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
[[nodiscard]] bool isInitialised() const override;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the device serial number via the "Discovery" EDBG sub-protocol.
|
* Retrieves the device serial number via the "Discovery" EDBG sub-protocol.
|
||||||
@@ -81,6 +66,18 @@ namespace DebugToolDrivers::Microchip
|
|||||||
*/
|
*/
|
||||||
std::string getFirmwareVersionString() override;
|
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.
|
* Starts a session with the EDBG device using the "Housekeeping" EDBG sub-protocol.
|
||||||
*/
|
*/
|
||||||
@@ -92,6 +89,8 @@ namespace DebugToolDrivers::Microchip
|
|||||||
void endSession();
|
void endSession();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool initialised = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The USB interface number of the CMSIS-DAP HID interface.
|
* 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
|
* ISP cannot be used for debugging operations. The EdbgAvrIspInterface class does *not* implement
|
||||||
* the Avr8DebugInterface.
|
* the Avr8DebugInterface.
|
||||||
*
|
*
|
||||||
* Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWire
|
* 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
|
* 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.
|
* second connection attempt via the debugWIRE interface.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<Microchip::Protocols::Edbg::Avr::EdbgAvrIspInterface> edbgAvrIspInterface = nullptr;
|
std::unique_ptr<Microchip::Protocols::Edbg::Avr::EdbgAvrIspInterface> edbgAvrIspInterface = nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ namespace DebugToolDrivers::Microchip
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!nonEdbgDevices.empty()) {
|
if (!nonEdbgDevices.empty()) {
|
||||||
throw DeviceNotFound(
|
throw DeviceNotFound{
|
||||||
"The connected MPLAB PICkit 4 device is not in \"AVR mode\". Please follow the instructions at "
|
"The connected MPLAB PICkit 4 device is not in \"AVR mode\". Please follow the instructions at "
|
||||||
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
|
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw exception;
|
throw exception;
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ namespace DebugToolDrivers::Microchip
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!nonEdbgDevices.empty()) {
|
if (!nonEdbgDevices.empty()) {
|
||||||
throw DeviceNotFound(
|
throw DeviceNotFound{
|
||||||
"The connected MPLAB Snap device is not in \"AVR mode\". Please follow the instructions at "
|
"The connected MPLAB Snap device is not in \"AVR mode\". Please follow the instructions at "
|
||||||
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
|
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw exception;
|
throw exception;
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
{
|
{
|
||||||
struct Avr8EdbgParameter
|
struct Avr8EdbgParameter
|
||||||
{
|
{
|
||||||
unsigned char context = 0x00;
|
unsigned char context;
|
||||||
unsigned char id = 0x00;
|
unsigned char id;
|
||||||
|
|
||||||
constexpr Avr8EdbgParameter() = default;
|
|
||||||
constexpr Avr8EdbgParameter(unsigned char context, unsigned char id)
|
constexpr Avr8EdbgParameter(unsigned char context, unsigned char id)
|
||||||
: context(context)
|
: context(context)
|
||||||
, id(id)
|
, id(id)
|
||||||
@@ -20,70 +19,70 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
|
|
||||||
struct Avr8EdbgParameters
|
struct Avr8EdbgParameters
|
||||||
{
|
{
|
||||||
static constexpr Avr8EdbgParameter CONFIG_VARIANT {0x00, 0x00};
|
static constexpr Avr8EdbgParameter CONFIG_VARIANT = {0x00, 0x00};
|
||||||
static constexpr Avr8EdbgParameter CONFIG_FUNCTION {0x00, 0x01};
|
static constexpr Avr8EdbgParameter CONFIG_FUNCTION = {0x00, 0x01};
|
||||||
static constexpr Avr8EdbgParameter PHYSICAL_INTERFACE {0x01, 0x00};
|
static constexpr Avr8EdbgParameter PHYSICAL_INTERFACE = {0x01, 0x00};
|
||||||
static constexpr Avr8EdbgParameter DW_CLOCK_DIVISION_FACTOR {0x01, 0x10};
|
static constexpr Avr8EdbgParameter DW_CLOCK_DIVISION_FACTOR = {0x01, 0x10};
|
||||||
static constexpr Avr8EdbgParameter PDI_CLOCK_SPEED {0x01, 0x31};
|
static constexpr Avr8EdbgParameter PDI_CLOCK_SPEED = {0x01, 0x31};
|
||||||
static constexpr Avr8EdbgParameter MEGA_DEBUG_CLOCK {0x01, 0x21};
|
static constexpr Avr8EdbgParameter MEGA_DEBUG_CLOCK = {0x01, 0x21};
|
||||||
static constexpr Avr8EdbgParameter JTAG_DAISY_CHAIN_SETTINGS {0x01, 0x01};
|
static constexpr Avr8EdbgParameter JTAG_DAISY_CHAIN_SETTINGS = {0x01, 0x01};
|
||||||
|
|
||||||
// debugWire and JTAG parameters
|
// debugWIRE and JTAG parameters
|
||||||
static constexpr Avr8EdbgParameter DEVICE_BOOT_START_ADDR {0x02, 0x0A};
|
static constexpr Avr8EdbgParameter DEVICE_BOOT_START_ADDR = {0x02, 0x0A};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_BASE {0x02, 0x06};
|
static constexpr Avr8EdbgParameter DEVICE_FLASH_BASE = {0x02, 0x06};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_SRAM_START {0x02, 0x0E};
|
static constexpr Avr8EdbgParameter DEVICE_SRAM_START = {0x02, 0x0E};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_EEPROM_SIZE {0x02, 0x10};
|
static constexpr Avr8EdbgParameter DEVICE_EEPROM_SIZE = {0x02, 0x10};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_EEPROM_PAGE_SIZE {0x02, 0x12};
|
static constexpr Avr8EdbgParameter DEVICE_EEPROM_PAGE_SIZE = {0x02, 0x12};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_PAGE_SIZE {0x02, 0x00};
|
static constexpr Avr8EdbgParameter DEVICE_FLASH_PAGE_SIZE = {0x02, 0x00};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_SIZE {0x02, 0x02};
|
static constexpr Avr8EdbgParameter DEVICE_FLASH_SIZE = {0x02, 0x02};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_OCD_REVISION {0x02, 0x13};
|
static constexpr Avr8EdbgParameter DEVICE_OCD_REVISION = {0x02, 0x13};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_OCD_DATA_REGISTER {0x02, 0x18};
|
static constexpr Avr8EdbgParameter DEVICE_OCD_DATA_REGISTER = {0x02, 0x18};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_SPMCR_REGISTER {0x02, 0x1D};
|
static constexpr Avr8EdbgParameter DEVICE_SPMCR_REGISTER = {0x02, 0x1D};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_OSCCAL_ADDR {0x02, 0x1E};
|
static constexpr Avr8EdbgParameter DEVICE_OSCCAL_ADDR = {0x02, 0x1E};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_EEARH_ADDR {0x02, 0x19};
|
static constexpr Avr8EdbgParameter DEVICE_EEARH_ADDR = {0x02, 0x19};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_EEARL_ADDR {0x02, 0x1A};
|
static constexpr Avr8EdbgParameter DEVICE_EEARL_ADDR = {0x02, 0x1A};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_EECR_ADDR {0x02, 0x1B};
|
static constexpr Avr8EdbgParameter DEVICE_EECR_ADDR = {0x02, 0x1B};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_EEDR_ADDR {0x02, 0x1C};
|
static constexpr Avr8EdbgParameter DEVICE_EEDR_ADDR = {0x02, 0x1C};
|
||||||
|
|
||||||
// PDI/XMega device parameters
|
// PDI/XMega device parameters
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPL_BASE_ADDR {0x02, 0x00};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPL_BASE_ADDR = {0x02, 0x00};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BASE_ADDR {0x02, 0x04};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BASE_ADDR = {0x02, 0x04};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_BASE_ADDR {0x02, 0x08};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_BASE_ADDR = {0x02, 0x08};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FUSE_BASE_ADDR {0x02, 0x0C};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FUSE_BASE_ADDR = {0x02, 0x0C};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_LOCKBIT_BASE_ADDR {0x02, 0x10};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_LOCKBIT_BASE_ADDR = {0x02, 0x10};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_USER_SIGN_BASE_ADDR {0x02, 0x14};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_USER_SIGN_BASE_ADDR = {0x02, 0x14};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_PROD_SIGN_BASE_ADDR {0x02, 0x18};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_PROD_SIGN_BASE_ADDR = {0x02, 0x18};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_DATA_BASE_ADDR {0x02, 0x1C};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_DATA_BASE_ADDR = {0x02, 0x1C};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPLICATION_BYTES {0x02, 0x20};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPLICATION_BYTES = {0x02, 0x20};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BYTES {0x02, 0x24};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BYTES = {0x02, 0x24};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_NVM_BASE {0x02, 0x2B};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_NVM_BASE = {0x02, 0x2B};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_SIGNATURE_OFFSET {0x02, 0x2D};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_SIGNATURE_OFFSET = {0x02, 0x2D};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FLASH_PAGE_BYTES {0x02, 0x26};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FLASH_PAGE_BYTES = {0x02, 0x26};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_SIZE {0x02, 0x28};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_SIZE = {0x02, 0x28};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_PAGE_SIZE {0x02, 0x2A};
|
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_PAGE_SIZE = {0x02, 0x2A};
|
||||||
|
|
||||||
// UPDI device parameters
|
// UPDI device parameters
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR {0x02, 0x00};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR = {0x02, 0x00};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE {0x02, 0x02};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE = {0x02, 0x02};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_PAGE_SIZE {0x02, 0x03};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_PAGE_SIZE = {0x02, 0x03};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_NVMCTRL_ADDR {0x02, 0x04};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_NVMCTRL_ADDR = {0x02, 0x04};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_OCD_ADDR {0x02, 0x06};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_OCD_ADDR = {0x02, 0x06};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_SIZE {0x02, 0x12};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_SIZE = {0x02, 0x12};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_SIZE {0x02, 0x16};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_SIZE = {0x02, 0x16};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_SIZE {0x02, 0x18};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_SIZE = {0x02, 0x18};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_SIZE {0x02, 0x1A};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_SIZE = {0x02, 0x1A};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_BASE_ADDR {0x02, 0x20};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_BASE_ADDR = {0x02, 0x20};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_BASE_ADDR {0x02, 0x22};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_BASE_ADDR = {0x02, 0x22};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_SIG_BASE_ADDR {0x02, 0x24};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_SIG_BASE_ADDR = {0x02, 0x24};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_BASE_ADDR {0x02, 0x26};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_BASE_ADDR = {0x02, 0x26};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_LOCK_BASE_ADDR {0x02, 0x28};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_LOCK_BASE_ADDR = {0x02, 0x28};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_DEVICE_ID {0x02, 0x2A};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_DEVICE_ID = {0x02, 0x2A};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB {0x02, 0x2C};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB = {0x02, 0x2C};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE_MSB {0x02, 0x2D};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE_MSB = {0x02, 0x2D};
|
||||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE {0x02, 0x2E};
|
static constexpr Avr8EdbgParameter DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE = {0x02, 0x2E};
|
||||||
|
|
||||||
static constexpr Avr8EdbgParameter RUN_TIMERS_WHILST_STOPPED {0x03, 0x00};
|
static constexpr Avr8EdbgParameter RUN_TIMERS_WHILST_STOPPED = {0x03, 0x00};
|
||||||
static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI {0x03, 0x06};
|
static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI = {0x03, 0x06};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Avr8ConfigVariant: unsigned char
|
enum class Avr8ConfigVariant: unsigned char
|
||||||
@@ -103,16 +102,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
DEBUGGING = 0x02,
|
DEBUGGING = 0x02,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline auto getPhysicalInterfaceToAvr8IdMapping() {
|
enum class Avr8PhysicalInterface: unsigned char
|
||||||
using Targets::TargetPhysicalInterface;
|
{
|
||||||
|
NONE = 0x00,
|
||||||
return std::map<TargetPhysicalInterface, unsigned char>({
|
JTAG = 0x04,
|
||||||
{TargetPhysicalInterface::DEBUG_WIRE, 0x05},
|
DEBUG_WIRE = 0x05,
|
||||||
{TargetPhysicalInterface::PDI, 0x06},
|
PDI = 0x06,
|
||||||
{TargetPhysicalInterface::JTAG, 0x04},
|
PDI_1W = 0x08,
|
||||||
{TargetPhysicalInterface::UPDI, 0x08},
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Avr8MemoryType: unsigned char
|
enum class Avr8MemoryType: unsigned char
|
||||||
{
|
{
|
||||||
@@ -148,10 +145,10 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
/**
|
/**
|
||||||
* The FLASH_PAGE memory type can be used to read and write full flash pages on the target.
|
* The FLASH_PAGE memory type can be used to read and write full flash pages on the target.
|
||||||
*
|
*
|
||||||
* Only available with the JTAG and debugWire config variants.
|
* Only available with the JTAG and debugWIRE config variants.
|
||||||
*
|
*
|
||||||
* This memory type is not available with the JTAG config variant in debugging mode. Programming mode will need
|
* This memory type is not available with the JTAG config variant in debugging mode. Programming mode will need
|
||||||
* to be enabled before it can be used with JTAG targets. With the debugWire variant, this memory type *can* be
|
* to be enabled before it can be used with JTAG targets. With the debugWIRE variant, this memory type *can* be
|
||||||
* used whilst in debugging mode.
|
* used whilst in debugging mode.
|
||||||
*/
|
*/
|
||||||
FLASH_PAGE = 0xB0,
|
FLASH_PAGE = 0xB0,
|
||||||
@@ -171,7 +168,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
/**
|
/**
|
||||||
* The SPM memory type can be used to read memory from the target whilst in debugging mode.
|
* The SPM memory type can be used to read memory from the target whilst in debugging mode.
|
||||||
*
|
*
|
||||||
* Only available with JTAG and debugWire config variants.
|
* Only available with JTAG and debugWIRE config variants.
|
||||||
*/
|
*/
|
||||||
SPM = 0xA0,
|
SPM = 0xA0,
|
||||||
|
|
||||||
@@ -186,7 +183,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
/**
|
/**
|
||||||
* The FUSES memory type can be used to read and write AVR fuses in programming mode.
|
* The FUSES memory type can be used to read and write AVR fuses in programming mode.
|
||||||
*
|
*
|
||||||
* Not available for the debugWire config variant.
|
* Not available for the debugWIRE config variant.
|
||||||
*/
|
*/
|
||||||
FUSES = 0xB2,
|
FUSES = 0xB2,
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user