Initial commit

This commit is contained in:
Nav
2021-04-04 21:04:12 +01:00
commit a29c5e1fec
549 changed files with 441216 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
#include "CommandPacket.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include <vector>
#include <memory>
#include "src/DebugServers/GdbRsp/Packet.hpp"
namespace Bloom::DebugServers::Gdb {
class GdbRspDebugServer;
}
namespace Bloom::DebugServers::Gdb::CommandPackets
{
/**
* GDB RSP command packets are sent to the server, from the GDB client. These packets carry instructions that the
* server is expected to carry out. Upon completion, the server is expected to respond to the client with
* a ResponsePacket.
*
* For some command packets, we define a specific data structure by extending this CommandPacket class. These
* classes extend the data structure to include fields for data which may be specific to the command.
* They also implement additional methods that allow us to easily access the additional data. An example
* of this would be the SupportedFeaturesQuery class. It extends the CommandPacket class and provides access
* to additional data fields that are specific to the command (in this case, a set of GDB features reported to be
* supported by the GDB client).
*
* Typically, command packets that require specific data structures are handled in a dedicated handler method
* in the GdbRspDebugServer. This is done by double dispatching the packet object to the appropriate handler.
* See CommandPacket::dispatchToHandler(), GdbRspDebugServer::serve() and the overloads
* for GdbRspDebugServer::handleGdbPacket() for more on this.
*
* Some command packets are so simple they do not require a dedicated data structure. An example of this is
* the halt reason packet, which contains nothing more than an ? character in the packet body. These packets are
* typically handled in the generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) method.
*
* See the Packet class for information on how the raw packets are formatted.
*/
class CommandPacket: public Packet
{
public:
CommandPacket(const std::vector<unsigned char>& rawPacket) : Packet(rawPacket) {}
/**
* Double dispatches the packet to the appropriate overload of handleGdbPacket(), within the passed instance of
* GdbRspDebugServer. If there is no overload defined for a specific CommandPacket-inherited type, the
* generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) is used.
*
* @param gdbRspDebugServer
*/
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer);
};
}

View File

@@ -0,0 +1,129 @@
#include <vector>
#include <memory>
#include <map>
#include "CommandPacketFactory.hpp"
using namespace Bloom::DebugServers::Gdb;
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
// This is an interrupt request - create a fake packet for it
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
}
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
if (rawPacketString.size() >= 2) {
/*
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
* looking for a command identifier string.
*/
if (rawPacketString.find("qSupported") == 1) {
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
return std::make_unique<CommandPackets::ReadGeneralRegisters>(rawPacket);
} else if (rawPacketString[1] == 'G' || rawPacketString[1] == 'P') {
return std::make_unique<CommandPackets::WriteGeneralRegisters>(rawPacket);
} else if (rawPacketString[1] == 'c') {
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
} else if (rawPacketString[1] == 's') {
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
} else if (rawPacketString[1] == 'm') {
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
} else if (rawPacketString[1] == 'M') {
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
} else if (rawPacketString[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
} else if (rawPacketString[1] == 'z') {
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
}
}
return std::make_unique<CommandPacket>(rawPacket);
}
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(std::vector<unsigned char> buffer) {
std::vector<std::vector<unsigned char>> output;
std::size_t bufferIndex;
std::size_t bufferSize = buffer.size();
unsigned char byte;
for (bufferIndex = 0; bufferIndex < bufferSize; bufferIndex++) {
byte = buffer[bufferIndex];
if (byte == 0x03) {
/*
* This is an interrupt packet - it doesn't carry any of the usual packet frame bytes, so we'll just
* add them here, in order to keep things consistent.
*
* Because we're effectively faking the packet frame, we can use any value for the checksum.
*/
output.push_back({'$', byte, '#', 'F', 'F'});
} else if (byte == '$') {
// Beginning of packet
std::vector<unsigned char> rawPacket;
rawPacket.push_back('$');
auto packetIndex = bufferIndex;
bool validPacket = false;
bool isByteEscaped = false;
for (packetIndex++; packetIndex < bufferSize; packetIndex++) {
byte = buffer[packetIndex];
if (byte == '}' && !isByteEscaped) {
isByteEscaped = true;
continue;
}
if (byte == '$' && !isByteEscaped) {
// Unexpected end of packet
validPacket = false;
break;
}
if (byte == '#' && !isByteEscaped) {
// End of packet data
if ((bufferSize - 1) < (packetIndex + 2)) {
// There should be at least two more bytes in the buffer, for the checksum.
break;
}
rawPacket.push_back(byte);
// Add the checksum bytes and break the loop
rawPacket.push_back(buffer[++packetIndex]);
rawPacket.push_back(buffer[++packetIndex]);
validPacket = true;
break;
}
if (isByteEscaped) {
// Escaped bytes are XOR'd with a 0x20 mask.
byte ^= 0x20;
isByteEscaped = false;
}
rawPacket.push_back(byte);
}
if (validPacket) {
output.push_back(rawPacket);
bufferIndex = packetIndex;
}
}
}
return output;
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include <vector>
#include <memory>
// Command packets
#include "CommandPacket.hpp"
#include "InterruptExecution.hpp"
#include "SupportedFeaturesQuery.hpp"
#include "ReadGeneralRegisters.hpp"
#include "WriteGeneralRegisters.hpp"
#include "ReadMemory.hpp"
#include "WriteMemory.hpp"
#include "StepExecution.hpp"
#include "ContinueExecution.hpp"
#include "SetBreakpoint.hpp"
#include "RemoveBreakpoint.hpp"
namespace Bloom::DebugServers::Gdb
{
using namespace CommandPackets;
/**
* The CommandPacketFactory class provides a means for extracting raw packet data from a raw buffer, and
* constructing the appropriate CommandPacket objects.
*/
class CommandPacketFactory
{
public:
/**
* Extracts raw GDB RSP packets from buffer.
*
* @param buffer
* The buffer from which to extract the raw GDB RSP packets.
*
* @return
* A vector of raw packets.
*/
static std::vector<std::vector<unsigned char>> extractRawPackets(std::vector<unsigned char> buffer);
/**
* Constructs the appropriate CommandPacket object from a single raw GDB RSP packet.
*
* @param rawPacket
* The raw GDB RSP packet from which to construct the CommandPacket object.
*
* @return
*/
static std::unique_ptr<CommandPacket> create(std::vector<unsigned char> rawPacket);
};
}

View File

@@ -0,0 +1,18 @@
#include <cstdint>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
#include "ContinueExecution.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void ContinueExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
}
}
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <optional>
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The ContinueExecution class implements a structure for "c" packets. These packets instruct the server
* to continue execution on the target.
*
* See @link https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#Packets for more on this.
*/
class ContinueExecution: public CommandPacket
{
private:
void init();
public:
/**
* The "c" packet can contain an address which defines the point from which the execution should be resumed on
* the target.
*
* Although the packet *can* contain this address, it is not required, hence the optional.
*/
std::optional<std::uint32_t> fromProgramCounter;
ContinueExecution(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,8 @@
#include "InterruptExecution.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The InterruptException class represents interrupt command packets. Upon receiving an interrupt packet, the
* server is expected to interrupt execution on the target.
*
* Technically, interrupts are not sent by the client in the form of a typical GDP RSP packet. Instead, they're
* just sent as a single byte from the client. We fake the packet on our end, to save us the headache of dealing
* with this inconsistency. We do this in CommandPacketFactory::extractRawPackets().
*/
class InterruptExecution: public CommandPacket
{
public:
InterruptExecution(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,15 @@
#include "ReadGeneralRegisters.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void ReadGeneralRegisters::init() {
if (this->data.size() >= 2 && this->data.front() == 'p') {
// This command packet is requesting a specific register
this->registerNumber = static_cast<size_t>(std::stoi(std::string(this->data.begin() + 1, this->data.end())));
}
}
void ReadGeneralRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The ReadGeneralRegisters class implements a structure for "g" and "p" command packets. In response to these
* packets, the server is expected to send register values for all registers (for "g" packets) or for a single
* register (for "p" packets).
*/
class ReadGeneralRegisters: public CommandPacket
{
private:
void init();
public:
/**
* "p" packets include a register number to indicate which register is requested for reading. When this is set,
* the server is expected to respond with only the value of the requested register.
*
* If the register number is not supplied (as is the case with "g" packets), the server is expected to respond
* with values for all registers.
*/
std::optional<int> registerNumber;
ReadGeneralRegisters(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,43 @@
#include "ReadMemory.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
void ReadMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
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.
*/
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;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from read memory packet data");
}
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse read length from read memory packet data");
}
}
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <cstdint>
#include <optional>
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The ReadMemory class implements a structure for "m" packets. Upon receiving these packets, the server is
* expected to read memory from the target and send it the client.
*/
class ReadMemory: public CommandPacket
{
private:
void init();
public:
/**
* The startAddress sent from the GDB client may include additional bits used to indicate the memory type.
* These bits have to be removed from the address before it can be used as a start address. This is not done
* here, as it's target specific.
*
* For an example of where GDB does this, see the AvrGdbRsp class.
*/
std::uint32_t startAddress;
/**
* Number of bytes to read.
*/
std::uint32_t bytes;
ReadMemory(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,38 @@
#include <QtCore/QString>
#include "RemoveBreakpoint.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
void RemoveBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected RemoveBreakpoint packet size");
}
// z0 = SW breakpoint, z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet");
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
}
}
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "../BreakpointType.hpp"
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb {
enum class Feature: int;
}
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The RemoveBreakpoint class implements the structure for "z" command packets. Upon receiving this command, the
* server is expected to remove a breakpoint at the specified address.
*/
class RemoveBreakpoint: public CommandPacket
{
private:
void init();
public:
/**
* Breakpoint type (Software or Hardware)
*/
BreakpointType type = BreakpointType::UNKNOWN;
/**
* Address at which the breakpoint should be located.
*/
std::uint32_t address;
RemoveBreakpoint(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
this->init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,39 @@
#include "SetBreakpoint.hpp"
#include <QtCore/QString>
#include <QtCore/QStringList>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
void SetBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected SetBreakpoint packet size");
}
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in SetBreakpoint packet");
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
}
}
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "../BreakpointType.hpp"
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb {
enum class Feature: int;
}
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The SetBreakpoint class implements the structure for "Z" command packets. Upon receiving this command, the
* server is expected to set a breakpoint at the specified address.
*/
class SetBreakpoint: public CommandPacket
{
private:
void init();
public:
/**
* Breakpoint type (Software or Hardware)
*/
BreakpointType type = BreakpointType::UNKNOWN;
/**
* Address at which the breakpoint should be located.
*/
std::uint32_t address;
SetBreakpoint(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
this->init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,18 @@
#include <cstdint>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
#include "StepExecution.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void StepExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
}
}
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The StepExecution class implements the structure for "s" command packets. Upon receiving this command, the
* server is expected to step execution on the target.
*/
class StepExecution: public CommandPacket
{
private:
void init();
public:
/**
* The address from which to begin the step.
*/
std::optional<size_t> fromProgramCounter;
StepExecution(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,43 @@
#include "SupportedFeaturesQuery.hpp"
#include <QtCore/QString>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
#include "../Feature.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void SupportedFeaturesQuery::init() {
/*
* For qSupported packets, supported and unsupported GDB features are reported in the packet
* data, where each GDB feature is separated by a semicolon.
*/
// The "qSupported:" prefix occupies 11 bytes
if (data.size() > 11) {
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 11),
static_cast<int>(this->data.size() - 11)
);
auto featureList = packetData.split(";");
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
for (int i = 0; i < featureList.size(); i++) {
auto featureString = featureList.at(i);
// We only care about supported features. Supported features will precede a '+' character.
if (featureString[featureString.size() - 1] == "+") {
featureString.remove("+");
auto feature = gdbFeatureMapping.valueAt(featureString.toStdString());
if (feature.has_value()) {
this->supportedFeatures.insert(static_cast<decltype(feature)::value_type>(feature.value()));
}
}
}
}
}
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <string>
#include <set>
#include "CommandPacket.hpp"
#include "../Feature.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
/**
* The SupportedFeaturesQuery command packet is a query from the GDB client, requesting a list of GDB features
* supported by the GDB server. The body of this packet also contains a list GDB features that are supported or
* unsupported by the GDB client.
*
* The command packet is identified by its 'qSupported' prefix in the command packet data. Following the prefix is
* a list of GDB features that are supported/unsupported by the client. For more info on this command
* packet, see the GDP RSP documentation.
*
* Responses to this command packet should take the form of a ResponsePackets::SupportedFeaturesResponse.
*/
class SupportedFeaturesQuery: public CommandPacket
{
private:
std::set<Feature> supportedFeatures;
void init();
public:
SupportedFeaturesQuery(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
this->init();
};
bool isFeatureSupported(const Feature& feature) const {
return this->supportedFeatures.find(feature) != this->supportedFeatures.end();
}
const std::set<Feature>& getSupportedFeatures() const {
return this->supportedFeatures;
}
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,26 @@
#include "WriteGeneralRegisters.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void WriteGeneralRegisters::init() {
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
if (packet.size() < 6) {
throw Exception("Invalid P command packet - insufficient data in packet.");
}
if (packet.find("=") == std::string::npos) {
throw Exception("Invalid P command packet - unexpected format");
}
auto packetSegments = QString::fromStdString(packet).split("=");
this->registerNumber = packetSegments.front().mid(1).toUInt(nullptr, 16);
this->registerValue = this->hexToData(packetSegments.back().toStdString());
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
void WriteGeneralRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <optional>
#include "CommandPacket.hpp"
#include "src/Targets/TargetRegister.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
using Bloom::Targets::TargetRegister;
using Bloom::Targets::TargetRegisterMap;
/**
* The WriteGeneralRegisters class implements the structure for "G" and "P" packets. Upon receiving this packet,
* server is expected to write register values to the target.
*/
class WriteGeneralRegisters: public CommandPacket
{
private:
void init();
public:
TargetRegisterMap registerMap;
int registerNumber;
std::vector<unsigned char> registerValue;
WriteGeneralRegisters(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}

View File

@@ -0,0 +1,52 @@
#include "WriteMemory.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
void WriteMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
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.
*/
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;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from write memory packet data");
}
auto lengthAndBufferSegments = packetSegments.at(1).split(":");
if (lengthAndBufferSegments.size() != 2) {
throw Exception("Unexpected number of segments in packet data: " + std::to_string(lengthAndBufferSegments.size()));
}
auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse write length from write memory packet data");
}
this->buffer = this->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::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <optional>
#include "CommandPacket.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::DebugServers::Gdb;
using Bloom::Targets::TargetMemoryBuffer;
/**
* The WriteMemory class implements the structure for "M" packets. Upon receiving this packet, the server is
* expected to write data to the target's memory, at the specified start address.
*/
class WriteMemory: public CommandPacket
{
private:
void init();
public:
/**
* Like with the ReadMemory command packet, the start address carries additional bits that indicate
* the memory type.
*/
std::uint32_t startAddress;
TargetMemoryBuffer buffer;
WriteMemory(std::vector<unsigned char> rawPacket) : CommandPacket(rawPacket) {
init();
};
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
};
}