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:
107
src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.cpp
Normal file
107
src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "EepromFill.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <algorithm>
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
using Services::TargetControllerService;
|
||||
|
||||
using ResponsePackets::ResponsePacket;
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
|
||||
using ::Exceptions::Exception;
|
||||
using Exceptions::InvalidCommandOption;
|
||||
|
||||
EepromFill::EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||
: Monitor(std::move(monitorPacket))
|
||||
, eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor)
|
||||
, eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor)
|
||||
{
|
||||
const auto fillValueOptionIt = this->commandOptions.find("value");
|
||||
if (fillValueOptionIt != this->commandOptions.end() && fillValueOptionIt->second.has_value()) {
|
||||
this->fillValue = Services::StringService::dataFromHex(*(fillValueOptionIt->second));
|
||||
}
|
||||
}
|
||||
|
||||
void EepromFill::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling EepromFill packet");
|
||||
|
||||
try {
|
||||
const auto eepromSize = this->eepromMemorySegmentDescriptor.size();
|
||||
const auto fillValueSize = this->fillValue.size();
|
||||
|
||||
if (fillValueSize == 0) {
|
||||
throw InvalidCommandOption{"Fill value required"};
|
||||
}
|
||||
|
||||
if (fillValueSize > eepromSize) {
|
||||
throw InvalidCommandOption{
|
||||
"Fill value size (" + std::to_string(fillValueSize) + " bytes) exceeds EEPROM size ("
|
||||
+ std::to_string(eepromSize) + " bytes)"
|
||||
};
|
||||
}
|
||||
|
||||
if ((eepromSize % fillValueSize) != 0) {
|
||||
Logger::warning(
|
||||
"The fill value size (" + std::to_string(fillValueSize) + " bytes) is not a multiple of the EEPROM "
|
||||
"size (" + std::to_string(eepromSize) + " bytes) - the fill value will be truncated"
|
||||
);
|
||||
}
|
||||
|
||||
Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM");
|
||||
|
||||
auto data = Targets::TargetMemoryBuffer{};
|
||||
data.reserve(eepromSize);
|
||||
|
||||
// Repeat this->fillValue until we've filled `data`
|
||||
while (data.size() < eepromSize) {
|
||||
data.insert(
|
||||
data.end(),
|
||||
this->fillValue.begin(),
|
||||
this->fillValue.begin() + static_cast<std::int32_t>(
|
||||
std::min(fillValueSize, (eepromSize - data.size()))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const auto hexValues = Services::StringService::toHex(data);
|
||||
Logger::debug("Filling EEPROM with values: " + hexValues);
|
||||
|
||||
targetControllerService.writeMemory(
|
||||
this->eepromAddressSpaceDescriptor,
|
||||
this->eepromMemorySegmentDescriptor,
|
||||
this->eepromMemorySegmentDescriptor.addressRange.startAddress,
|
||||
std::move(data)
|
||||
);
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||
"Filled " + std::to_string(eepromSize) + " bytes of EEPROM, with values: " + hexValues + "\n"
|
||||
)});
|
||||
|
||||
} catch (const InvalidCommandOption& exception) {
|
||||
Logger::error(exception.getMessage());
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket{Services::StringService::toHex(exception.getMessage() + "\n")}
|
||||
);
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to fill EEPROM - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp
Normal file
37
src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
/**
|
||||
* The EepromFill class implements a structure for the "monitor eeprom fill" GDB command.
|
||||
*
|
||||
* This command fills the target's EEPROM with the given value.
|
||||
*/
|
||||
class EepromFill: public Gdb::CommandPackets::Monitor
|
||||
{
|
||||
public:
|
||||
const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor;
|
||||
|
||||
explicit EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
private:
|
||||
Targets::TargetMemoryBuffer fillValue;
|
||||
};
|
||||
}
|
||||
@@ -15,31 +15,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
using namespace Exceptions;
|
||||
|
||||
FlashDone::FlashDone(const RawPacket& rawPacket)
|
||||
FlashDone::FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
, programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor)
|
||||
, programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor)
|
||||
{}
|
||||
|
||||
void FlashDone::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void FlashDone::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling FlashDone packet");
|
||||
|
||||
try {
|
||||
if (debugSession.programmingSession.has_value()) {
|
||||
const auto& programmingSession = debugSession.programmingSession.value();
|
||||
Logger::info(
|
||||
"Flushing " + std::to_string(programmingSession.buffer.size()) + " bytes to target's program memory"
|
||||
);
|
||||
|
||||
targetControllerService.enableProgrammingMode();
|
||||
|
||||
targetControllerService.writeMemory(
|
||||
Targets::TargetMemoryType::FLASH,
|
||||
programmingSession.startAddress,
|
||||
std::move(programmingSession.buffer)
|
||||
);
|
||||
|
||||
debugSession.programmingSession.reset();
|
||||
if (!debugSession.programmingSession.has_value()) {
|
||||
throw Exception{"No active programming session"};
|
||||
}
|
||||
|
||||
Logger::info(
|
||||
"Flushing " + std::to_string(debugSession.programmingSession->buffer.size())
|
||||
+ " bytes to target's program memory"
|
||||
);
|
||||
|
||||
targetControllerService.enableProgrammingMode();
|
||||
|
||||
targetControllerService.writeMemory(
|
||||
this->programMemoryAddressSpaceDescriptor,
|
||||
this->programMemorySegmentDescriptor,
|
||||
debugSession.programmingSession->startAddress,
|
||||
std::move(debugSession.programmingSession->buffer)
|
||||
);
|
||||
|
||||
debugSession.programmingSession.reset();
|
||||
|
||||
Logger::warning("Program memory updated");
|
||||
targetControllerService.disableProgrammingMode();
|
||||
|
||||
@@ -47,7 +57,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
targetControllerService.resetTarget();
|
||||
Logger::info("Target reset complete");
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to handle FlashDone packet - " + exception.getMessage());
|
||||
@@ -60,7 +70,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
Logger::error("Failed to disable programming mode - " + exception.getMessage());
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
@@ -16,10 +17,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
class FlashDone: public Gdb::CommandPackets::CommandPacket
|
||||
{
|
||||
public:
|
||||
explicit FlashDone(const RawPacket& rawPacket);
|
||||
const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||
|
||||
explicit FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
@@ -15,40 +16,40 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
using namespace Exceptions;
|
||||
|
||||
FlashErase::FlashErase(const RawPacket& rawPacket)
|
||||
FlashErase::FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
, programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor)
|
||||
, programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor)
|
||||
{
|
||||
const auto packetString = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 12),
|
||||
static_cast<int>(this->data.size() - 12)
|
||||
);
|
||||
using Services::StringService;
|
||||
|
||||
if (rawPacket.size() < 8) {
|
||||
throw Exception{"Invalid packet length"};
|
||||
}
|
||||
|
||||
/*
|
||||
* The flash erase ('vFlashErase') packet consists of two segments, an address and a length, separated by a
|
||||
* comma.
|
||||
*
|
||||
* Example: $vFlashErase:00000000,00004f00#f4
|
||||
*/
|
||||
const auto packetSegments = packetString.split(",");
|
||||
if (packetSegments.size() != 2) {
|
||||
throw Exception(
|
||||
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
|
||||
);
|
||||
const auto command = std::string{this->data.begin() + 12, this->data.end()};
|
||||
|
||||
const auto delimiterPos = command.find_first_of(',');
|
||||
if (delimiterPos == std::string::npos) {
|
||||
throw Exception{"Invalid packet"};
|
||||
}
|
||||
|
||||
bool conversionStatus = false;
|
||||
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse start address from flash erase packet data");
|
||||
}
|
||||
|
||||
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse length from flash erase packet data");
|
||||
}
|
||||
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
||||
this->bytes = StringService::toUint32(command.substr(delimiterPos + 1), 16);
|
||||
}
|
||||
|
||||
void FlashErase::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void FlashErase::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling FlashErase packet");
|
||||
|
||||
try {
|
||||
@@ -57,9 +58,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
Logger::warning("Erasing program memory, in preparation for programming");
|
||||
|
||||
// We don't erase a specific address range - we just erase the entire program memory.
|
||||
targetControllerService.eraseMemory(debugSession.gdbTargetDescriptor.targetDescriptor.programMemoryType);
|
||||
targetControllerService.eraseMemory(
|
||||
this->programMemoryAddressSpaceDescriptor,
|
||||
this->programMemorySegmentDescriptor
|
||||
);
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to erase flash memory - " + exception.getMessage());
|
||||
@@ -72,7 +76,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
Logger::error("Failed to disable programming mode - " + exception.getMessage());
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
@@ -19,11 +20,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
public:
|
||||
std::uint32_t startAddress = 0;
|
||||
std::uint32_t bytes = 0;
|
||||
const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||
|
||||
explicit FlashErase(const RawPacket& rawPacket);
|
||||
explicit FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#include "FlashWrite.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
@@ -20,44 +19,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
FlashWrite::FlashWrite(const RawPacket& rawPacket)
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
using Services::StringService;
|
||||
|
||||
if (this->data.size() < 15) {
|
||||
throw Exception("Invalid packet length");
|
||||
throw Exception{"Invalid packet length"};
|
||||
}
|
||||
|
||||
/*
|
||||
* The flash write ('vFlashWrite') packet consists of two segments, an address and a buffer.
|
||||
*
|
||||
* Seperated by a colon.
|
||||
* The flash write ('vFlashWrite') packet consists of two segments: an address and a buffer, seperated by a
|
||||
* colon.
|
||||
*/
|
||||
auto colonIt = std::find(this->data.begin() + 12, this->data.end(), ':');
|
||||
const auto delimiterIt = std::find(this->data.begin() + 12, this->data.end(), ':');
|
||||
|
||||
if (colonIt == this->data.end()) {
|
||||
throw Exception("Failed to find colon delimiter in write flash packet.");
|
||||
if (delimiterIt == this->data.end()) {
|
||||
throw Exception{"Failed to find colon delimiter in write flash packet."};
|
||||
}
|
||||
|
||||
bool conversionStatus = false;
|
||||
this->startAddress = QByteArray(
|
||||
reinterpret_cast<const char*>(this->data.data() + 12),
|
||||
std::distance(this->data.begin(), colonIt) - 12
|
||||
).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse start address from flash write packet data");
|
||||
}
|
||||
|
||||
this->buffer = Targets::TargetMemoryBuffer(colonIt + 1, this->data.end());
|
||||
this->startAddress = StringService::toUint32(std::string{this->data.begin() + 12, delimiterIt}, 16);
|
||||
this->buffer = Targets::TargetMemoryBuffer{delimiterIt + 1, this->data.end()};
|
||||
}
|
||||
|
||||
void FlashWrite::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void FlashWrite::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling FlashWrite packet");
|
||||
|
||||
try {
|
||||
if (this->buffer.empty()) {
|
||||
throw Exception("Received empty buffer from GDB");
|
||||
throw Exception{"Received empty buffer from GDB"};
|
||||
}
|
||||
|
||||
if (!debugSession.programmingSession.has_value()) {
|
||||
debugSession.programmingSession = ProgrammingSession(this->startAddress, this->buffer);
|
||||
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
|
||||
|
||||
} else {
|
||||
auto& programmingSession = debugSession.programmingSession.value();
|
||||
@@ -65,7 +61,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
const auto expectedStartAddress = (currentEndAddress + 1);
|
||||
|
||||
if (this->startAddress < expectedStartAddress) {
|
||||
throw Exception("Invalid start address from GDB - the buffer would overlap a previous buffer");
|
||||
throw Exception{"Invalid start address from GDB - the buffer would overlap a previous buffer"};
|
||||
}
|
||||
|
||||
if (this->startAddress > expectedStartAddress) {
|
||||
@@ -84,7 +80,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
);
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to handle FlashWrite packet - " + exception.getMessage());
|
||||
@@ -97,7 +93,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
Logger::error("Failed to disable programming mode - " + exception.getMessage());
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "ReadMemory.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||
|
||||
@@ -18,141 +20,139 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
using Exceptions::Exception;
|
||||
|
||||
ReadMemory::ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
if (this->data.size() < 4) {
|
||||
throw Exception("Invalid packet length");
|
||||
}
|
||||
: ReadMemory(rawPacket, gdbTargetDescriptor, ReadMemory::extractPacketData(rawPacket))
|
||||
{}
|
||||
|
||||
const auto packetString = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 1),
|
||||
static_cast<int>(this->data.size() - 1)
|
||||
);
|
||||
|
||||
/*
|
||||
* The read memory ('m') packet consists of two segments, an address and a number of bytes to read.
|
||||
* These are separated by a comma character.
|
||||
*/
|
||||
const auto packetSegments = packetString.split(",");
|
||||
|
||||
if (packetSegments.size() != 2) {
|
||||
throw Exception(
|
||||
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
|
||||
);
|
||||
}
|
||||
|
||||
bool conversionStatus = false;
|
||||
const auto gdbStartAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse start address from read memory packet data");
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the memory type from the memory address (see Gdb::TargetDescriptor::memoryOffsetsByType for more on
|
||||
* this).
|
||||
*/
|
||||
this->memoryType = gdbTargetDescriptor.getMemoryTypeFromGdbAddress(gdbStartAddress);
|
||||
this->startAddress = gdbStartAddress & ~(gdbTargetDescriptor.getMemoryOffset(this->memoryType));
|
||||
|
||||
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse read length from read memory packet data");
|
||||
}
|
||||
}
|
||||
|
||||
void ReadMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void ReadMemory::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling ReadMemory packet");
|
||||
|
||||
try {
|
||||
const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType;
|
||||
const auto memoryDescriptorIt = memoryDescriptorsByType.find(this->memoryType);
|
||||
|
||||
if (memoryDescriptorIt == memoryDescriptorsByType.end()) {
|
||||
throw Exception("Target does not support the requested memory type.");
|
||||
}
|
||||
|
||||
if (this->bytes == 0) {
|
||||
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>()));
|
||||
debugSession.connection.writePacket(ResponsePacket{Targets::TargetMemoryBuffer{}});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& memoryDescriptor = memoryDescriptorIt->second;
|
||||
const auto addressRange = Targets::TargetMemoryAddressRange{
|
||||
this->startAddress,
|
||||
this->startAddress + this->bytes - 1
|
||||
};
|
||||
|
||||
if (this->memoryType == Targets::TargetMemoryType::EEPROM) {
|
||||
// GDB sends EEPROM addresses in relative form - we convert them to absolute form, here.
|
||||
this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress;
|
||||
const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
|
||||
addressRange
|
||||
);
|
||||
|
||||
/*
|
||||
* First pass to ensure that we can read all of the memory before attempting to do so. And to ensure that
|
||||
* the requested address range completely resides within known memory segments.
|
||||
*/
|
||||
auto accessibleBytes = Targets::TargetMemorySize{0};
|
||||
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
|
||||
if (!memorySegmentDescriptor->debugModeAccess.readable) {
|
||||
throw Exception{
|
||||
"Attempted to access restricted memory segment (" + memorySegmentDescriptor->key
|
||||
+ ") - segment not readable in debug mode"
|
||||
};
|
||||
}
|
||||
|
||||
accessibleBytes += memorySegmentDescriptor->addressRange.intersectingSize(addressRange);
|
||||
}
|
||||
|
||||
/*
|
||||
* In AVR targets, RAM is mapped to many registers and peripherals - we don't want to block GDB from
|
||||
* accessing them.
|
||||
* GDB will sometimes request an excess of up to two bytes outside the memory segment address range, even
|
||||
* though we provide it with a memory map. I don't know why it does this, but I do know that we must
|
||||
* tolerate it, otherwise GDB will moan.
|
||||
*/
|
||||
const auto permittedStartAddress = (this->memoryType == Targets::TargetMemoryType::RAM)
|
||||
? 0x00
|
||||
: memoryDescriptor.addressRange.startAddress;
|
||||
|
||||
const auto permittedEndAddress = memoryDescriptor.addressRange.endAddress + 2;
|
||||
|
||||
if (
|
||||
this->startAddress < permittedStartAddress
|
||||
|| (this->startAddress + (this->bytes - 1)) > permittedEndAddress
|
||||
) {
|
||||
if (accessibleBytes < this->bytes && (this->bytes - accessibleBytes) > 2) {
|
||||
/*
|
||||
* GDB can be configured to generate backtraces past the main function and the internal entry point
|
||||
* of the application. Although this isn't very useful to most devs, CLion now seems to enable it by
|
||||
* default. Somewhere between CLion 2021.1 and 2022.1, it began issuing the "-gdb-set backtrace past-entry on"
|
||||
* command to GDB, at the beginning of each debug session.
|
||||
* GDB has requested memory that, at least partially, does not reside in any known memory segment.
|
||||
*
|
||||
* This means that GDB will attempt to walk down the stack to identify every frame. The problem is that
|
||||
* GDB doesn't really know where the stack begins, so it ends up in a loop, continually issuing read
|
||||
* memory commands. This has exposed an issue on our end - we need to validate the requested memory
|
||||
* address range and reject any request for a range that's not within the target's memory. We do this
|
||||
* here.
|
||||
* This could be a result of GDB being configured to generate backtraces past the main function and
|
||||
* the internal entry point of the application. This means that GDB will attempt to walk down the stack
|
||||
* to identify every frame. The problem is that GDB doesn't really know where the stack begins, so it
|
||||
* probes the target by continuously issuing read memory commands until the server responds with an
|
||||
* error.
|
||||
*
|
||||
* CLion seems to enable this by default. Somewhere between CLion 2021.1 and 2022.1, it began issuing
|
||||
* the "-gdb-set backtrace past-entry on" command to GDB, at the beginning of each debug session.
|
||||
*
|
||||
* We don't throw an exception here, because this isn't really an error and so it's best not to report
|
||||
* it as such. I don't think it's an error because it's expected behaviour, even though we respond to
|
||||
* GDB with an error response.
|
||||
*/
|
||||
Logger::debug(
|
||||
"GDB requested access to memory which is outside the target's memory range - returning error "
|
||||
"response"
|
||||
"GDB requested access to memory which does not reside within any memory segment - returning error "
|
||||
"response"
|
||||
);
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* GDB may request more bytes than what's available (even though we give it a memory map?!) - ensure that
|
||||
* we don't try to read any more than what's available.
|
||||
*
|
||||
* We fill the out-of-bounds bytes with 0x00, below.
|
||||
*/
|
||||
const auto bytesToRead = (this->startAddress <= memoryDescriptor.addressRange.endAddress)
|
||||
? std::min(this->bytes, (memoryDescriptor.addressRange.endAddress - this->startAddress) + 1)
|
||||
: 0;
|
||||
auto buffer = Targets::TargetMemoryBuffer(this->bytes, 0x00);
|
||||
|
||||
auto memoryBuffer = Targets::TargetMemoryBuffer();
|
||||
{
|
||||
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||
|
||||
if (bytesToRead > 0) {
|
||||
memoryBuffer = targetControllerService.readMemory(
|
||||
this->memoryType,
|
||||
this->startAddress,
|
||||
bytesToRead
|
||||
);
|
||||
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
|
||||
const auto segmentStartAddress = std::max(
|
||||
this->startAddress,
|
||||
memorySegmentDescriptor->addressRange.startAddress
|
||||
);
|
||||
|
||||
const auto segmentBuffer = targetControllerService.readMemory(
|
||||
this->addressSpaceDescriptor,
|
||||
*memorySegmentDescriptor,
|
||||
segmentStartAddress,
|
||||
memorySegmentDescriptor->addressRange.intersectingSize(addressRange)
|
||||
);
|
||||
|
||||
const auto bufferOffsetIt = buffer.begin() + (segmentStartAddress - this->startAddress);
|
||||
assert(segmentBuffer.size() <= std::distance(bufferOffsetIt, buffer.end()));
|
||||
|
||||
std::copy(segmentBuffer.begin(), segmentBuffer.end(), bufferOffsetIt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bytesToRead < this->bytes) {
|
||||
// GDB requested some out-of-bounds memory - fill the inaccessible bytes with 0x00
|
||||
memoryBuffer.insert(memoryBuffer.end(), (this->bytes - bytesToRead), 0x00);
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(memoryBuffer)));
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to read memory from target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
|
||||
ReadMemory::PacketData ReadMemory::extractPacketData(const RawPacket& rawPacket) {
|
||||
using Services::StringService;
|
||||
|
||||
if (rawPacket.size() < 8) {
|
||||
throw Exception{"Invalid packet length"};
|
||||
}
|
||||
|
||||
const auto command = std::string{rawPacket.begin() + 2, rawPacket.end() - 3};
|
||||
|
||||
const auto delimiterPos = command.find_first_of(',');
|
||||
if (delimiterPos == std::string::npos) {
|
||||
throw Exception{"Invalid packet"};
|
||||
}
|
||||
|
||||
return {
|
||||
StringService::toUint32(command.substr(0, delimiterPos), 16),
|
||||
StringService::toUint32(command.substr(delimiterPos + 1), 16)
|
||||
};
|
||||
}
|
||||
|
||||
ReadMemory::ReadMemory(
|
||||
const RawPacket& rawPacket,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
ReadMemory::PacketData&& packetData
|
||||
)
|
||||
: CommandPacket(rawPacket)
|
||||
, addressSpaceDescriptor(gdbTargetDescriptor.addressSpaceDescriptorFromGdbAddress(packetData.gdbStartAddress))
|
||||
, startAddress(gdbTargetDescriptor.translateGdbAddress(packetData.gdbStartAddress))
|
||||
, bytes(packetData.bytes)
|
||||
{}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
@@ -17,26 +18,32 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
class ReadMemory: public Gdb::CommandPackets::CommandPacket
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Start address of the memory operation.
|
||||
*/
|
||||
Targets::TargetMemoryAddress startAddress = 0;
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||
|
||||
/**
|
||||
* The type of memory to read from.
|
||||
*/
|
||||
Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH;
|
||||
Targets::TargetMemoryAddress startAddress;
|
||||
Targets::TargetMemorySize bytes;
|
||||
|
||||
/**
|
||||
* Number of bytes to read.
|
||||
*/
|
||||
Targets::TargetMemorySize bytes = 0;
|
||||
|
||||
explicit ReadMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
||||
ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
private:
|
||||
struct PacketData
|
||||
{
|
||||
GdbMemoryAddress gdbStartAddress;
|
||||
std::uint32_t bytes;
|
||||
};
|
||||
|
||||
static PacketData extractPacketData(const RawPacket& rawPacket);
|
||||
ReadMemory(
|
||||
const RawPacket& rawPacket,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
PacketData&& packetData
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
@@ -12,111 +13,74 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
using Exceptions::Exception;
|
||||
|
||||
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket)
|
||||
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
, eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor)
|
||||
, programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor)
|
||||
, eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor)
|
||||
{
|
||||
if (this->data.size() < 26) {
|
||||
throw Exception("Invalid packet length");
|
||||
}
|
||||
using Services::StringService;
|
||||
|
||||
auto packetString = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 23), // +23 to exclude the "qXfer:memory-map:read::"
|
||||
static_cast<int>(this->data.size() - 23)
|
||||
);
|
||||
if (this->data.size() < 26) {
|
||||
throw Exception{"Invalid packet length"};
|
||||
}
|
||||
|
||||
/*
|
||||
* The read memory map ('qXfer:memory-map:read::...') packet consists of two segments, an offset and a length.
|
||||
* These are separated by a comma character.
|
||||
*/
|
||||
auto packetSegments = packetString.split(",");
|
||||
const auto command = std::string{this->data.begin() + 23, this->data.end()};
|
||||
|
||||
if (packetSegments.size() != 2) {
|
||||
throw Exception(
|
||||
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
|
||||
);
|
||||
const auto delimiterPos = command.find_first_of(',');
|
||||
if (delimiterPos == std::string::npos) {
|
||||
throw Exception{"Invalid packet"};
|
||||
}
|
||||
|
||||
bool conversionStatus = false;
|
||||
this->offset = packetSegments.at(0).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse offset from read memory map packet data");
|
||||
}
|
||||
|
||||
this->length = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse read length from read memory map packet data");
|
||||
}
|
||||
this->offset = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
||||
this->length = StringService::toUint32(command.substr(delimiterPos + 1), 16);
|
||||
}
|
||||
|
||||
void ReadMemoryMap::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void ReadMemoryMap::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling ReadMemoryMap packet");
|
||||
|
||||
using Targets::TargetMemoryType;
|
||||
const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType;
|
||||
|
||||
const auto& ramDescriptor = memoryDescriptorsByType.at(TargetMemoryType::RAM);
|
||||
const auto& flashDescriptor = memoryDescriptorsByType.at(TargetMemoryType::FLASH);
|
||||
|
||||
const auto eepromDescriptorIt = memoryDescriptorsByType.find(TargetMemoryType::EEPROM);
|
||||
const auto eepromDescriptor = eepromDescriptorIt != memoryDescriptorsByType.end()
|
||||
? std::optional(eepromDescriptorIt->second)
|
||||
: std::nullopt;
|
||||
|
||||
const auto ramGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset(
|
||||
Targets::TargetMemoryType::RAM
|
||||
);
|
||||
|
||||
const auto eepromGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset(
|
||||
Targets::TargetMemoryType::EEPROM
|
||||
);
|
||||
|
||||
const auto flashGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset(
|
||||
Targets::TargetMemoryType::FLASH
|
||||
);
|
||||
|
||||
/*
|
||||
* We include register and EEPROM memory in our RAM section. This allows GDB to access registers and EEPROM
|
||||
* data via memory read/write packets.
|
||||
*
|
||||
* Like SRAM, GDB applies an offset to EEPROM addresses. We account for that offset in our ramSectionSize.
|
||||
*
|
||||
* The SRAM and EEPROM offsets allow for a maximum of 65KB of SRAM. But that must also accommodate the
|
||||
* register addresses, which can vary in size.
|
||||
*
|
||||
* As of writing this (Dec 2022), there are no 8-bit AVR targets on sale today, with 65KB+ of SRAM.
|
||||
*/
|
||||
const auto eepromEndAddress = eepromGdbOffset + (eepromDescriptor.has_value() ? eepromDescriptor->size() : 0);
|
||||
const auto ramSectionSize = eepromEndAddress - ramGdbOffset;
|
||||
|
||||
const auto flashSize = flashDescriptor.size();
|
||||
const auto flashPageSize = flashDescriptor.pageSize.value();
|
||||
const auto ramSectionEndAddress = gdbTargetDescriptor.translateTargetMemoryAddress(
|
||||
this->eepromMemorySegmentDescriptor.addressRange.endAddress,
|
||||
this->eepromAddressSpaceDescriptor,
|
||||
this->eepromMemorySegmentDescriptor
|
||||
);
|
||||
const auto ramSectionStartAddress = TargetDescriptor::SRAM_ADDRESS_MASK;
|
||||
const auto ramSectionSize = ramSectionEndAddress - ramSectionStartAddress + 1;
|
||||
|
||||
const auto memoryMap =
|
||||
std::string("<memory-map>")
|
||||
+ "<memory type=\"ram\" start=\"" + std::to_string(ramGdbOffset) + "\" length=\"" + std::to_string(ramSectionSize) + "\"/>"
|
||||
+ "<memory type=\"flash\" start=\"" + std::to_string(flashGdbOffset) + "\" length=\"" + std::to_string(flashSize) + "\">"
|
||||
+ "<property name=\"blocksize\">" + std::to_string(flashPageSize) + "</property>"
|
||||
std::string{"<memory-map>"}
|
||||
+ "<memory type=\"ram\" start=\"" + std::to_string(ramSectionStartAddress) + "\" length=\"" + std::to_string(ramSectionSize) + "\"/>"
|
||||
+ "<memory type=\"flash\" start=\"0\" length=\"" + std::to_string(this->programMemorySegmentDescriptor.size()) + "\">"
|
||||
+ "<property name=\"blocksize\">" + std::to_string(this->programMemorySegmentDescriptor.pageSize.value()) + "</property>"
|
||||
+ "</memory>"
|
||||
+ "</memory-map>";
|
||||
|
||||
auto responseData = std::vector<unsigned char>{'l'};
|
||||
|
||||
if (this->offset < memoryMap.size() && this->length > 0) {
|
||||
auto memoryMapData = std::vector<unsigned char>(
|
||||
responseData.insert(
|
||||
responseData.end(),
|
||||
memoryMap.begin() + this->offset,
|
||||
memoryMap.begin() + std::min(
|
||||
static_cast<long>(this->offset + this->length),
|
||||
static_cast<long>(memoryMap.size())
|
||||
memoryMap.begin() + this->offset + std::min(
|
||||
static_cast<long>(this->length),
|
||||
static_cast<long>(memoryMap.size() - this->offset)
|
||||
)
|
||||
);
|
||||
|
||||
auto responseData = std::vector<unsigned char>({'l'});
|
||||
std::move(memoryMapData.begin(), memoryMapData.end(), std::back_inserter(responseData));
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(responseData));
|
||||
return;
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>({'l'})));
|
||||
debugSession.connection.writePacket(ResponsePacket{responseData});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
@@ -13,6 +17,10 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
class ReadMemoryMap: public Gdb::CommandPackets::CommandPacket
|
||||
{
|
||||
public:
|
||||
const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor;
|
||||
|
||||
/**
|
||||
* The offset of the memory map, from which to read.
|
||||
*/
|
||||
@@ -23,10 +31,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
*/
|
||||
std::uint32_t length = 0;
|
||||
|
||||
explicit ReadMemoryMap(const RawPacket& rawPacket);
|
||||
explicit ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "ReadRegister.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
@@ -24,16 +24,23 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
ReadRegister::ReadRegister(const RawPacket& rawPacket)
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
using Services::StringService;
|
||||
|
||||
if (this->data.size() < 2) {
|
||||
throw Exception("Invalid packet length");
|
||||
throw Exception{"Invalid packet length"};
|
||||
}
|
||||
|
||||
this->registerId = static_cast<GdbRegisterId>(
|
||||
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
|
||||
StringService::toUint32(std::string{this->data.begin() + 1, this->data.end()}, 16)
|
||||
);
|
||||
}
|
||||
|
||||
void ReadRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void ReadRegister::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling ReadRegister packet");
|
||||
|
||||
try {
|
||||
@@ -47,49 +54,60 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
const auto programCounter = targetControllerService.getProgramCounter();
|
||||
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket(Services::StringService::toHex(Targets::TargetMemoryBuffer({
|
||||
ResponsePacket{Services::StringService::toHex(Targets::TargetMemoryBuffer{
|
||||
static_cast<unsigned char>(programCounter),
|
||||
static_cast<unsigned char>(programCounter >> 8),
|
||||
static_cast<unsigned char>(programCounter >> 16),
|
||||
static_cast<unsigned char>(programCounter >> 24),
|
||||
})))
|
||||
})}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
|
||||
const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(this->registerId);
|
||||
if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) {
|
||||
/*
|
||||
* GDB has requested the program counter. We can't access this in the same way as we do with other
|
||||
* registers.
|
||||
*/
|
||||
const auto stackPointer = targetControllerService.getStackPointer();
|
||||
|
||||
const auto targetRegisterDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket{Services::StringService::toHex(Targets::TargetMemoryBuffer{
|
||||
static_cast<unsigned char>(stackPointer),
|
||||
static_cast<unsigned char>(stackPointer >> 8),
|
||||
})}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId);
|
||||
const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find(
|
||||
this->registerId
|
||||
);
|
||||
|
||||
if (!targetRegisterDescriptorId.has_value()) {
|
||||
throw Exception("GDB requested an invalid/unknown register");
|
||||
if (
|
||||
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|
||||
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
|
||||
) {
|
||||
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
|
||||
}
|
||||
|
||||
auto registerValue = targetControllerService.readRegisters({*targetRegisterDescriptorId}).front().value;
|
||||
|
||||
// GDB expects register values to be in LSB.
|
||||
std::reverse(registerValue.begin(), registerValue.end());
|
||||
auto registerValue = targetControllerService.readRegister(*(targetRegisterDescriptorIt->second));
|
||||
std::reverse(registerValue.begin(), registerValue.end()); // MSB to LSB
|
||||
|
||||
const auto& gdbRegisterDescriptor = gdbRegisterDescriptorIt->second;
|
||||
if (registerValue.size() < gdbRegisterDescriptor.size) {
|
||||
/*
|
||||
* The register on the target is smaller than the size expected by GDB.
|
||||
*
|
||||
* Insert the rest of the bytes.
|
||||
*/
|
||||
// The register on the target is smaller than the size expected by GDB.
|
||||
registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00);
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket(Services::StringService::toHex(registerValue))
|
||||
);
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(registerValue)});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to read general registers - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#include "ReadRegisters.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
@@ -16,93 +17,78 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
using Services::TargetControllerService;
|
||||
|
||||
using Targets::TargetRegister;
|
||||
using Targets::TargetRegisterDescriptorIds;
|
||||
|
||||
using ResponsePackets::ResponsePacket;
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
|
||||
using Exceptions::Exception;
|
||||
|
||||
ReadRegisters::ReadRegisters(const RawPacket& rawPacket)
|
||||
ReadRegisters::ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
, gpRegistersMemorySegmentDescriptor(gdbTargetDescriptor.gpRegistersMemorySegmentDescriptor)
|
||||
{}
|
||||
|
||||
void ReadRegisters::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void ReadRegisters::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling ReadRegisters packet");
|
||||
|
||||
try {
|
||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor;
|
||||
auto descriptorIds = TargetRegisterDescriptorIds();
|
||||
auto buffer = Targets::TargetMemoryBuffer(39, 0x00);
|
||||
|
||||
// Read all target registers mapped to a GDB register
|
||||
for (const auto& [gdbRegisterId, gdbRegisterDescriptor] : targetDescriptor.gdbRegisterDescriptorsById) {
|
||||
const auto registerDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
|
||||
gdbRegisterId
|
||||
);
|
||||
|
||||
if (registerDescriptorId.has_value()) {
|
||||
descriptorIds.insert(*registerDescriptorId);
|
||||
auto gpRegDescriptors = Targets::TargetRegisterDescriptors{};
|
||||
std::transform(
|
||||
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.begin(),
|
||||
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end(),
|
||||
std::back_inserter(gpRegDescriptors),
|
||||
[] (const auto& pair) {
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
Targets::TargetRegisters registerSet;
|
||||
Targets::TargetMemoryAddress programCounter;
|
||||
);
|
||||
|
||||
{
|
||||
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||
|
||||
registerSet = targetControllerService.readRegisters(descriptorIds);
|
||||
programCounter = targetControllerService.getProgramCounter();
|
||||
}
|
||||
for (auto& [regDesc, regVal] : targetControllerService.readRegisters(gpRegDescriptors)) {
|
||||
if (regDesc.type != Targets::TargetRegisterType::GENERAL_PURPOSE_REGISTER) {
|
||||
// Status register (SREG)
|
||||
assert(regVal.size() == 1);
|
||||
buffer[32] = regVal[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort each register by their respective GDB register ID - this will leave us with a collection of
|
||||
* registers in the order expected by the GDB client.
|
||||
*/
|
||||
std::sort(
|
||||
registerSet.begin(),
|
||||
registerSet.end(),
|
||||
[this, &targetDescriptor] (const TargetRegister& regA, const TargetRegister& regB) {
|
||||
return targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regA.descriptorId).value() <
|
||||
targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regB.descriptorId).value();
|
||||
}
|
||||
);
|
||||
const auto bufferOffset = regDesc.startAddress
|
||||
- this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress;
|
||||
|
||||
/*
|
||||
* Reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure that
|
||||
* each register value size matches the size in the associated GDB register descriptor and implode the
|
||||
* values.
|
||||
*/
|
||||
auto registers = std::vector<unsigned char>();
|
||||
for (auto& reg : registerSet) {
|
||||
std::reverse(reg.value.begin(), reg.value.end());
|
||||
assert((buffer.size() - bufferOffset) >= regVal.size());
|
||||
|
||||
const auto gdbRegisterId = targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(
|
||||
reg.descriptorId
|
||||
).value();
|
||||
const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(gdbRegisterId);
|
||||
|
||||
if (reg.value.size() < gdbRegisterDescriptor.size) {
|
||||
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
|
||||
/*
|
||||
* GDB expects register values in LSB form, which is why we use reverse iterators below.
|
||||
*
|
||||
* This isn't really necessary though, as all of the registers that are handled here are
|
||||
* single-byte registers.
|
||||
*/
|
||||
std::copy(regVal.rbegin(), regVal.rend(), buffer.begin() + bufferOffset);
|
||||
}
|
||||
|
||||
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
|
||||
const auto spValue = targetControllerService.getStackPointer();
|
||||
buffer[33] = static_cast<unsigned char>(spValue);
|
||||
buffer[34] = static_cast<unsigned char>(spValue >> 8);
|
||||
|
||||
const auto pcValue = targetControllerService.getProgramCounter();
|
||||
buffer[35] = static_cast<unsigned char>(pcValue);
|
||||
buffer[36] = static_cast<unsigned char>(pcValue >> 8);
|
||||
buffer[37] = static_cast<unsigned char>(pcValue >> 16);
|
||||
buffer[38] = static_cast<unsigned char>(pcValue >> 24);
|
||||
}
|
||||
|
||||
// Finally, include the program counter (which GDB expects to reside at the end)
|
||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter));
|
||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 8));
|
||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 16));
|
||||
registers.insert(registers.end(), static_cast<unsigned char>(programCounter >> 24));
|
||||
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket(Services::StringService::toHex(registers))
|
||||
);
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to read registers - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
@@ -15,10 +17,14 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
class ReadRegisters: public Gdb::CommandPackets::CommandPacket
|
||||
{
|
||||
public:
|
||||
explicit ReadRegisters(const RawPacket& rawPacket);
|
||||
const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor;
|
||||
|
||||
explicit ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
: CommandPacket(rawPacket)
|
||||
{}
|
||||
|
||||
void VContContinueExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void VContContinueExecution::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling VContContinueExecution packet");
|
||||
|
||||
try {
|
||||
targetControllerService.continueTargetExecution(std::nullopt, std::nullopt);
|
||||
targetControllerService.resumeTargetExecution();
|
||||
debugSession.waitingForBreak = true;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to continue execution on target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp"
|
||||
#include "src/Services/Avr8InstructionService.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Services/PathService.hpp"
|
||||
@@ -13,43 +14,42 @@
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
using Services::TargetControllerService;
|
||||
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
|
||||
using ::Exceptions::Exception;
|
||||
|
||||
VContRangeStep::VContRangeStep(const RawPacket& rawPacket)
|
||||
VContRangeStep::VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
, programAddressSpaceDescriptor(gdbTargetDescriptor.programAddressSpaceDescriptor)
|
||||
, programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor)
|
||||
{
|
||||
using Services::StringService;
|
||||
|
||||
if (this->data.size() < 10) {
|
||||
throw Exception("Unexpected VContRangeStep packet size");
|
||||
throw Exception{"Unexpected VContRangeStep packet size"};
|
||||
}
|
||||
|
||||
const auto commandData = std::string(this->data.begin() + 7, this->data.end());
|
||||
const auto command = std::string{this->data.begin() + 7, this->data.end()};
|
||||
|
||||
const auto delimiterPosition = commandData.find(',');
|
||||
const auto threadIdDelimiterPosition = commandData.find(':');
|
||||
|
||||
if (delimiterPosition == std::string::npos || delimiterPosition >= (commandData.size() - 1)) {
|
||||
throw Exception("Invalid VContRangeStep packet");
|
||||
const auto delimiterPos = command.find(',');
|
||||
const auto threadIdDelimiterPos = command.find(':');
|
||||
if (delimiterPos == std::string::npos || delimiterPos >= (command.size() - 1)) {
|
||||
throw Exception{"Invalid VContRangeStep packet"};
|
||||
}
|
||||
|
||||
const auto& delimiterIt = commandData.begin() + static_cast<decltype(commandData)::difference_type>(
|
||||
delimiterPosition
|
||||
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
||||
this->endAddress = StringService::toUint32(
|
||||
command.substr(delimiterPos + 1, threadIdDelimiterPos - (delimiterPos + 1)),
|
||||
16
|
||||
);
|
||||
const auto startAddressHex = std::string(commandData.begin(), delimiterIt);
|
||||
const auto endAddressHex = std::string(
|
||||
delimiterIt + 1,
|
||||
threadIdDelimiterPosition != std::string::npos
|
||||
? commandData.begin() + static_cast<decltype(commandData)::difference_type>(threadIdDelimiterPosition)
|
||||
: commandData.end()
|
||||
);
|
||||
|
||||
this->startAddress = static_cast<Targets::TargetMemoryAddress>(std::stoi(startAddressHex, nullptr, 16));
|
||||
this->endAddress = static_cast<Targets::TargetMemoryAddress>(std::stoi(endAddressHex, nullptr, 16));
|
||||
}
|
||||
|
||||
void VContRangeStep::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void VContRangeStep::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
using Targets::Microchip::Avr8::OpcodeDecoder::Decoder;
|
||||
using Services::Avr8InstructionService;
|
||||
using Services::StringService;
|
||||
|
||||
@@ -59,19 +59,17 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
Logger::debug("Requested stepping range end address (exclusive): 0x" + StringService::toHex(this->endAddress));
|
||||
|
||||
try {
|
||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
|
||||
const auto& programMemoryAddressRange = targetDescriptor.memoryDescriptorsByType.at(
|
||||
targetDescriptor.programMemoryType
|
||||
).addressRange;
|
||||
const auto stepAddressRange = Targets::TargetMemoryAddressRange{this->startAddress, this->endAddress};
|
||||
const auto stepByteSize = stepAddressRange.size() - 1; // -1 because the end address is exclusive
|
||||
const auto& programMemoryAddressRange = this->programMemorySegmentDescriptor.addressRange;
|
||||
|
||||
if (
|
||||
this->startAddress > this->endAddress
|
||||
|| (this->startAddress % 2) != 0
|
||||
|| (this->endAddress % 2) != 0
|
||||
|| this->startAddress < programMemoryAddressRange.startAddress
|
||||
|| this->endAddress > programMemoryAddressRange.endAddress
|
||||
stepAddressRange.startAddress > stepAddressRange.endAddress
|
||||
|| (stepAddressRange.startAddress % 2) != 0
|
||||
|| (stepAddressRange.endAddress % 2) != 0
|
||||
|| !programMemoryAddressRange.contains(stepAddressRange)
|
||||
) {
|
||||
throw Exception("Invalid address range in VContRangeStep");
|
||||
throw Exception{"Invalid address range in VContRangeStep"};
|
||||
}
|
||||
|
||||
if (debugSession.activeRangeSteppingSession.has_value()) {
|
||||
@@ -81,26 +79,30 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
debugSession.terminateRangeSteppingSession(targetControllerService);
|
||||
}
|
||||
|
||||
if (this->startAddress == this->endAddress || (this->endAddress - this->startAddress) <= 2) {
|
||||
if (stepByteSize <= 2) {
|
||||
// Single step requested. No need for a range step here.
|
||||
targetControllerService.stepTargetExecution(std::nullopt);
|
||||
targetControllerService.stepTargetExecution();
|
||||
debugSession.waitingForBreak = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto addressRange = Targets::TargetMemoryAddressRange(this->startAddress, this->endAddress);
|
||||
auto rangeSteppingSession = RangeSteppingSession(addressRange, {});
|
||||
auto rangeSteppingSession = RangeSteppingSession{stepAddressRange, {}};
|
||||
|
||||
const auto instructionsByAddress = Avr8InstructionService::fetchInstructions(
|
||||
addressRange,
|
||||
targetDescriptor,
|
||||
targetControllerService
|
||||
const auto instructionsByAddress = Decoder::decode(
|
||||
stepAddressRange.startAddress,
|
||||
targetControllerService.readMemory(
|
||||
this->programAddressSpaceDescriptor,
|
||||
this->programMemorySegmentDescriptor,
|
||||
stepAddressRange.startAddress,
|
||||
stepByteSize
|
||||
)
|
||||
);
|
||||
|
||||
Logger::debug(
|
||||
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instructions within stepping range "
|
||||
"(byte addresses) 0x" + StringService::toHex(addressRange.startAddress) + " -> 0x"
|
||||
+ StringService::toHex(addressRange.endAddress) + ", in preparation for new range stepping session"
|
||||
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instruction(s) within stepping range "
|
||||
"(byte addresses) 0x" + StringService::toHex(stepAddressRange.startAddress) + " -> 0x"
|
||||
+ StringService::toHex(stepAddressRange.endAddress) + ", in preparation for new range stepping "
|
||||
"session"
|
||||
);
|
||||
|
||||
for (const auto& [instructionAddress, instruction] : instructionsByAddress) {
|
||||
@@ -144,10 +146,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
*destinationAddress < programMemoryAddressRange.startAddress
|
||||
|| *destinationAddress > programMemoryAddressRange.endAddress
|
||||
) {
|
||||
if (!programMemoryAddressRange.contains(*destinationAddress)) {
|
||||
/*
|
||||
* This instruction may jump to an invalid address. Someone screwed up here - could be
|
||||
* something wrong in Bloom (opcode decoding bug, incorrect program memory address range in
|
||||
@@ -158,7 +157,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
*/
|
||||
Logger::debug(
|
||||
"Intercepting CCPF instruction (\"" + instruction->name + "\") with invalid destination "
|
||||
"byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x"
|
||||
"byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x"
|
||||
+ StringService::toHex(instructionAddress)
|
||||
);
|
||||
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
||||
@@ -166,8 +165,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
}
|
||||
|
||||
if (
|
||||
*destinationAddress < addressRange.startAddress
|
||||
|| *destinationAddress >= addressRange.endAddress
|
||||
*destinationAddress < stepAddressRange.startAddress
|
||||
|| *destinationAddress >= stepAddressRange.endAddress
|
||||
) {
|
||||
/*
|
||||
* This instruction may jump to an address outside the requested stepping range.
|
||||
@@ -185,6 +184,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, ensure that we intercept the first instruction outside the range (which is the end address
|
||||
* of the range, because it's exclusive).
|
||||
*/
|
||||
rangeSteppingSession.interceptedAddresses.insert(stepAddressRange.endAddress);
|
||||
|
||||
debugSession.startRangeSteppingSession(std::move(rangeSteppingSession), targetControllerService);
|
||||
|
||||
/*
|
||||
@@ -195,12 +200,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
* we should continue. See that member function for more.
|
||||
*/
|
||||
debugSession.activeRangeSteppingSession->singleStepping = true;
|
||||
targetControllerService.stepTargetExecution(std::nullopt);
|
||||
targetControllerService.stepTargetExecution();
|
||||
debugSession.waitingForBreak = true;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to start new range stepping session - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
@@ -16,13 +19,18 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
class VContRangeStep: public Gdb::CommandPackets::CommandPacket
|
||||
{
|
||||
public:
|
||||
const Targets::TargetAddressSpaceDescriptor& programAddressSpaceDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||
|
||||
Targets::TargetMemoryAddress startAddress;
|
||||
Targets::TargetMemoryAddress endAddress;
|
||||
|
||||
explicit VContRangeStep(const RawPacket& rawPacket);
|
||||
explicit VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
: CommandPacket(rawPacket)
|
||||
{}
|
||||
|
||||
void VContStepExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void VContStepExecution::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling VContStepExecution packet");
|
||||
|
||||
try {
|
||||
targetControllerService.stepTargetExecution(std::nullopt);
|
||||
targetControllerService.stepTargetExecution();
|
||||
debugSession.waitingForBreak = true;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to step execution on target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -12,15 +12,20 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
: CommandPacket(rawPacket)
|
||||
{}
|
||||
|
||||
void VContSupportedActionsQuery::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void VContSupportedActionsQuery::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling VContSupportedActionsQuery packet");
|
||||
|
||||
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
|
||||
debugSession.connection.writePacket(ResponsePackets::ResponsePacket(
|
||||
debugSession.connection.writePacket(ResponsePackets::ResponsePacket{
|
||||
debugSession.serverConfig.rangeStepping
|
||||
? "vCont;c;C;s;S;r"
|
||||
: "vCont;c;C;s;S"
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
@@ -16,124 +17,116 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
using namespace Exceptions;
|
||||
|
||||
WriteMemory::WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor)
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
if (this->data.size() < 4) {
|
||||
throw Exception("Invalid packet length");
|
||||
}
|
||||
: WriteMemory(rawPacket, gdbTargetDescriptor, WriteMemory::extractPacketData(rawPacket))
|
||||
{}
|
||||
|
||||
const auto packetString = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 1),
|
||||
static_cast<int>(this->data.size() - 1)
|
||||
);
|
||||
|
||||
/*
|
||||
* The write memory ('M') packet consists of three segments, an address, a length and a buffer.
|
||||
* The address and length are separated by a comma character, and the buffer proceeds a colon character.
|
||||
*/
|
||||
const auto packetSegments = packetString.split(",");
|
||||
if (packetSegments.size() != 2) {
|
||||
throw Exception(
|
||||
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
|
||||
);
|
||||
}
|
||||
|
||||
bool conversionStatus = false;
|
||||
const auto gdbStartAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse start address from write memory packet data");
|
||||
}
|
||||
|
||||
this->memoryType = gdbTargetDescriptor.getMemoryTypeFromGdbAddress(gdbStartAddress);
|
||||
this->startAddress = gdbStartAddress & ~(gdbTargetDescriptor.getMemoryOffset(this->memoryType));
|
||||
|
||||
const auto lengthAndBufferSegments = packetSegments.at(1).split(":");
|
||||
if (lengthAndBufferSegments.size() != 2) {
|
||||
throw Exception(
|
||||
"Unexpected number of segments in packet data: "
|
||||
+ std::to_string(lengthAndBufferSegments.size())
|
||||
);
|
||||
}
|
||||
|
||||
const auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16);
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to parse write length from write memory packet data");
|
||||
}
|
||||
|
||||
this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString());
|
||||
|
||||
if (this->buffer.size() != bufferSize) {
|
||||
throw Exception("Buffer size does not match length value given in write memory packet");
|
||||
}
|
||||
}
|
||||
|
||||
void WriteMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void WriteMemory::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling WriteMemory packet");
|
||||
|
||||
try {
|
||||
const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType;
|
||||
const auto memoryDescriptorIt = memoryDescriptorsByType.find(this->memoryType);
|
||||
|
||||
if (memoryDescriptorIt == memoryDescriptorsByType.end()) {
|
||||
throw Exception("Target does not support the requested memory type.");
|
||||
}
|
||||
|
||||
if (this->memoryType == Targets::TargetMemoryType::FLASH) {
|
||||
/*
|
||||
* This shouldn't happen - GDB should send the FlashWrite (vFlashWrite) packet to write to the target's
|
||||
* program memory.
|
||||
*
|
||||
* A number of actions have to be taken before we can write to the target's program memory - this is
|
||||
* all covered in the FlashWrite and FlashDone command classes. I don't want to cover it again in here,
|
||||
* so just respond with an error and request that this issue be reported.
|
||||
*/
|
||||
throw Exception(
|
||||
"GDB attempted to write to program memory via an \"M\" packet - this is not supported. Please "
|
||||
"report this issue to Bloom developers with the full debug log."
|
||||
);
|
||||
}
|
||||
|
||||
if (this->buffer.size() == 0) {
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& memoryDescriptor = memoryDescriptorIt->second;
|
||||
|
||||
if (this->memoryType == Targets::TargetMemoryType::EEPROM) {
|
||||
// GDB sends EEPROM addresses in relative form - we convert them to absolute form, here.
|
||||
this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress;
|
||||
}
|
||||
|
||||
/*
|
||||
* In AVR targets, RAM is mapped to many registers and peripherals - we don't want to block GDB from
|
||||
* accessing them.
|
||||
*/
|
||||
const auto memoryStartAddress = (this->memoryType == Targets::TargetMemoryType::RAM)
|
||||
? 0x00
|
||||
: memoryDescriptor.addressRange.startAddress;
|
||||
|
||||
if (
|
||||
this->startAddress < memoryStartAddress
|
||||
|| (this->startAddress + (this->buffer.size() - 1)) > memoryDescriptor.addressRange.endAddress
|
||||
) {
|
||||
throw Exception(
|
||||
"GDB requested access to memory which is outside the target's memory range"
|
||||
);
|
||||
}
|
||||
|
||||
targetControllerService.writeMemory(
|
||||
this->memoryType,
|
||||
const auto addressRange = Targets::TargetMemoryAddressRange{
|
||||
this->startAddress,
|
||||
this->buffer
|
||||
this->startAddress + static_cast<Targets::TargetMemorySize>(this->buffer.size()) - 1
|
||||
};
|
||||
|
||||
const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
|
||||
addressRange
|
||||
);
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
auto accessibleBytes = Targets::TargetMemorySize{0};
|
||||
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
|
||||
if (!memorySegmentDescriptor->debugModeAccess.writeable) {
|
||||
throw Exception{
|
||||
"Attempted to access restricted memory segment (" + memorySegmentDescriptor->key
|
||||
+ ") - segment not writeable in debug mode"
|
||||
};
|
||||
}
|
||||
|
||||
accessibleBytes += memorySegmentDescriptor->addressRange.intersectingSize(addressRange);
|
||||
}
|
||||
|
||||
if (accessibleBytes < this->bytes) {
|
||||
throw Exception{"GDB requested access to memory which does not reside within any memory segment"};
|
||||
}
|
||||
|
||||
{
|
||||
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||
|
||||
for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) {
|
||||
const auto segmentStartAddress = std::max(
|
||||
this->startAddress,
|
||||
memorySegmentDescriptor->addressRange.startAddress
|
||||
);
|
||||
|
||||
const auto bufferOffsetIt = buffer.begin() + (segmentStartAddress - this->startAddress);
|
||||
targetControllerService.writeMemory(
|
||||
this->addressSpaceDescriptor,
|
||||
*memorySegmentDescriptor,
|
||||
segmentStartAddress,
|
||||
Targets::TargetMemoryBuffer{
|
||||
bufferOffsetIt,
|
||||
bufferOffsetIt + memorySegmentDescriptor->addressRange.intersectingSize(addressRange)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to write memory to target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
|
||||
WriteMemory::PacketData WriteMemory::extractPacketData(const RawPacket& rawPacket) {
|
||||
using Services::StringService;
|
||||
|
||||
if (rawPacket.size() < 8) {
|
||||
throw Exception{"Invalid packet length"};
|
||||
}
|
||||
|
||||
const auto command = std::string{rawPacket.begin() + 2, rawPacket.end() - 3};
|
||||
|
||||
const auto commaDelimiterPos = command.find_first_of(',');
|
||||
const auto colonDelimiterPos = command.find_first_of(':');
|
||||
if (commaDelimiterPos == std::string::npos || colonDelimiterPos == std::string::npos) {
|
||||
throw Exception{"Invalid packet"};
|
||||
}
|
||||
|
||||
return {
|
||||
StringService::toUint32(command.substr(0, commaDelimiterPos), 16),
|
||||
StringService::toUint32(
|
||||
command.substr(commaDelimiterPos + 1, colonDelimiterPos - (commaDelimiterPos + 1)),
|
||||
16
|
||||
),
|
||||
StringService::dataFromHex(command.substr(colonDelimiterPos + 1))
|
||||
};
|
||||
}
|
||||
|
||||
WriteMemory::WriteMemory(
|
||||
const RawPacket& rawPacket,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
PacketData&& packetData
|
||||
)
|
||||
: CommandPacket(rawPacket)
|
||||
, addressSpaceDescriptor(gdbTargetDescriptor.addressSpaceDescriptorFromGdbAddress(packetData.gdbStartAddress))
|
||||
, startAddress(gdbTargetDescriptor.translateGdbAddress(packetData.gdbStartAddress))
|
||||
, bytes(packetData.bytes)
|
||||
, buffer(std::move(packetData.buffer))
|
||||
{
|
||||
if (this->buffer.size() != this->bytes) {
|
||||
throw Exception{"Buffer size does not match length value given in write memory packet"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
@@ -17,26 +18,29 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
class WriteMemory: public Gdb::CommandPackets::CommandPacket
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Start address of the memory operation.
|
||||
*/
|
||||
Targets::TargetMemoryAddress startAddress = 0;
|
||||
|
||||
/**
|
||||
* The type of memory to read from.
|
||||
*/
|
||||
Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH;
|
||||
|
||||
/**
|
||||
* Data to write.
|
||||
*/
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||
Targets::TargetMemoryAddress startAddress;
|
||||
Targets::TargetMemorySize bytes;
|
||||
Targets::TargetMemoryBuffer buffer;
|
||||
|
||||
explicit WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
||||
explicit WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor);
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
private:
|
||||
struct PacketData
|
||||
{
|
||||
std::uint32_t gdbStartAddress;
|
||||
std::uint32_t bytes;
|
||||
Targets::TargetMemoryBuffer buffer;
|
||||
};
|
||||
|
||||
static PacketData extractPacketData(const RawPacket& rawPacket);
|
||||
WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor, PacketData&& packetData);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
@@ -13,9 +11,6 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
{
|
||||
using Services::TargetControllerService;
|
||||
|
||||
using Targets::TargetRegister;
|
||||
using Targets::TargetRegisterDescriptors;
|
||||
|
||||
using ResponsePackets::ResponsePacket;
|
||||
using ResponsePackets::OkResponsePacket;
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
@@ -25,87 +20,88 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
WriteRegister::WriteRegister(const RawPacket& rawPacket)
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
using Services::StringService;
|
||||
|
||||
if (this->data.size() < 4) {
|
||||
throw Exception{"Invalid WriteRegister command packet - insufficient data in packet."};
|
||||
}
|
||||
|
||||
// The P packet updates a single register
|
||||
auto packet = std::string(this->data.begin(), this->data.end());
|
||||
auto command = std::string{this->data.begin() + 1, this->data.end()};
|
||||
|
||||
if (packet.size() < 4) {
|
||||
throw Exception("Invalid WriteRegister command packet - insufficient data in packet.");
|
||||
const auto delimiterPos = command.find_first_of('=');
|
||||
if (delimiterPos == std::string::npos) {
|
||||
throw Exception{"Invalid packet"};
|
||||
}
|
||||
|
||||
if (packet.find('=') == std::string::npos) {
|
||||
throw Exception("Invalid WriteRegister command packet - unexpected format");
|
||||
}
|
||||
|
||||
const auto packetSegments = QString::fromStdString(packet).split("=");
|
||||
this->registerId = static_cast<GdbRegisterId>(packetSegments.front().mid(1).toUInt(nullptr, 16));
|
||||
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
|
||||
this->registerId = static_cast<GdbRegisterId>(StringService::toUint32(command.substr(0, delimiterPos), 16));
|
||||
this->registerValue = Services::StringService::dataFromHex(command.substr(delimiterPos + 1));
|
||||
|
||||
if (this->registerValue.empty()) {
|
||||
throw Exception("Invalid WriteRegister command packet - missing register value");
|
||||
throw Exception{"Invalid WriteRegister command packet - missing register value"};
|
||||
}
|
||||
|
||||
// LSB to MSB
|
||||
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
||||
}
|
||||
|
||||
void WriteRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void WriteRegister::handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling WriteRegister packet");
|
||||
|
||||
try {
|
||||
if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID) {
|
||||
if (this->registerValue.size() != 4) {
|
||||
throw Exception{"Invalid PC value register size"};
|
||||
}
|
||||
|
||||
targetControllerService.setProgramCounter(
|
||||
static_cast<Targets::TargetMemoryAddress>(
|
||||
(this->registerValue.size() >= 1 ? this->registerValue[0] : 0x00) << 24
|
||||
| (this->registerValue.size() >= 2 ? this->registerValue[1] : 0x00) << 16
|
||||
| (this->registerValue.size() >= 3 ? this->registerValue[2] : 0x00) << 8
|
||||
| (this->registerValue.size() >= 4 ? this->registerValue[3] : 0x00)
|
||||
this->registerValue[0] << 24
|
||||
| this->registerValue[1] << 16
|
||||
| this->registerValue[2] << 8
|
||||
| this->registerValue[3]
|
||||
)
|
||||
);
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& gdbTargetDescriptor = debugSession.gdbTargetDescriptor;
|
||||
const auto descriptorId = gdbTargetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId(
|
||||
if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) {
|
||||
if (this->registerValue.size() != 2) {
|
||||
throw Exception{"Invalid SP register value size"};
|
||||
}
|
||||
|
||||
targetControllerService.setStackPointer(
|
||||
static_cast<Targets::TargetStackPointer>(this->registerValue[0] << 8 | this->registerValue[1])
|
||||
);
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId);
|
||||
const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find(
|
||||
this->registerId
|
||||
);
|
||||
|
||||
if (!descriptorId.has_value()) {
|
||||
throw Exception("Invalid/unknown register");
|
||||
if (
|
||||
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|
||||
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
|
||||
) {
|
||||
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
|
||||
}
|
||||
|
||||
const auto& descriptor = gdbTargetDescriptor.targetDescriptor.registerDescriptorsById.at(*descriptorId);
|
||||
|
||||
if (this->registerValue.size() > descriptor.size) {
|
||||
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
|
||||
for (auto i = this->registerValue.size() - 1; i >= descriptor.size; --i) {
|
||||
if (this->registerValue.at(i) != 0x00) {
|
||||
// If we reach a non-zero byte, we cannot trim anymore without changing the data
|
||||
break;
|
||||
}
|
||||
|
||||
this->registerValue.erase(this->registerValue.begin() + i);
|
||||
}
|
||||
|
||||
if (this->registerValue.size() > descriptor.size) {
|
||||
const auto& gdbRegisterDescriptor = gdbTargetDescriptor.gdbRegisterDescriptorsById.at(
|
||||
this->registerId
|
||||
);
|
||||
throw Exception(
|
||||
"Cannot set value for " + gdbRegisterDescriptor.name + " - value size exceeds register size."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
targetControllerService.writeRegisters({
|
||||
TargetRegister(descriptor.id, this->registerValue)
|
||||
});
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
targetControllerService.writeRegister(*(targetRegisterDescriptorIt->second), this->registerValue);
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to write registers - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
|
||||
#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
@@ -21,6 +22,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
Gdb::DebugSession& debugSession,
|
||||
const Gdb::TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user