RISC-V GDB server

This commit is contained in:
Nav
2024-11-16 20:43:22 +00:00
parent 26f4f8f90e
commit eebba986b5
28 changed files with 1582 additions and 4 deletions

View File

@@ -43,6 +43,20 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/EepromFill.cpp
# RISC-V GDB RSP Server
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/RiscVGdbRsp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/RiscVGdbTargetDescriptor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/ReadRegisters.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/ReadRegister.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/WriteRegister.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/ReadMemory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/WriteMemory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/ReadMemoryMap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/FlashErase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/FlashWrite.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/FlashDone.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/RiscVGdb/CommandPackets/VContSupportedActionsQuery.cpp
)
if (NOT EXCLUDE_INSIGHT)

View File

@@ -4,6 +4,7 @@
// Debug server implementations
#include "Gdb/AvrGdb/AvrGdbRsp.hpp"
#include "Gdb/RiscVGdb/RiscVGdbRsp.hpp"
#include "src/Exceptions/InvalidConfig.hpp"
#include "src/Logger/Logger.hpp"
@@ -55,6 +56,21 @@ namespace DebugServer
);
}
},
{
"riscv-gdb-rsp",
[this] () -> std::unique_ptr<ServerInterface> {
if (this->targetDescriptor.family != Targets::TargetFamily::RISC_V) {
throw Exceptions::Exception{"The RISC-V GDB RSP server is only compatible with RISC-V targets."};
}
return std::make_unique<DebugServer::Gdb::RiscVGdb::RiscVGdbRsp>(
this->debugServerConfig,
this->targetDescriptor,
*(this->eventListener.get()),
this->interruptEventNotifier
);
}
},
};
}

View File

@@ -0,0 +1,103 @@
#include "FlashDone.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::OkResponsePacket;
using namespace Exceptions;
FlashDone::FlashDone(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{}
void FlashDone::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling FlashDone packet");
try {
if (!debugSession.programmingSession.has_value()) {
/*
* GDB will send a VFlashDone packet even it only performs an erase. In this case, there's nothing more
* to do, as erase operations are executed immediately.
*/
targetControllerService.disableProgrammingMode();
debugSession.connection.writePacket(OkResponsePacket{});
return;
}
Logger::info(
"Flushing " + std::to_string(debugSession.programmingSession->buffer.size())
+ " bytes to target's program memory"
);
const auto addressRange = Targets::TargetMemoryAddressRange{
debugSession.programmingSession->startAddress,
debugSession.programmingSession->startAddress
+ static_cast<Targets::TargetMemorySize>(debugSession.programmingSession->buffer.size()) - 1
};
const auto memorySegmentDescriptors = gdbTargetDescriptor.systemAddressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
addressRange
);
if (memorySegmentDescriptors.size() != 1) {
throw Exception{
memorySegmentDescriptors.empty()
? "Invalid command - no containing memory segments found for the given address range"
: "Invalid command - address range intersects with multiple memory segments"
};
}
const auto& segmentDescriptor = *(memorySegmentDescriptors.front());
if (!segmentDescriptor.programmingModeAccess.writeable) {
throw Exception{"Memory segment (\"" + segmentDescriptor.name + "\") not writable in programming mode"};
}
targetControllerService.enableProgrammingMode();
targetControllerService.writeMemory(
gdbTargetDescriptor.systemAddressSpaceDescriptor,
segmentDescriptor,
debugSession.programmingSession->startAddress,
std::move(debugSession.programmingSession->buffer)
);
debugSession.programmingSession.reset();
Logger::warning("Program memory updated");
targetControllerService.disableProgrammingMode();
Logger::warning("Resetting target");
targetControllerService.resetTarget();
Logger::info("Target reset complete");
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to handle FlashDone packet - " + exception.getMessage());
debugSession.programmingSession.reset();
try {
targetControllerService.disableProgrammingMode();
} catch (const Exception& exception) {
Logger::error("Failed to disable programming mode - " + exception.getMessage());
}
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <cstdint>
#include <optional>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class FlashDone
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
explicit FlashDone(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,74 @@
#include "FlashErase.hpp"
#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"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::OkResponsePacket;
using namespace Exceptions;
FlashErase::FlashErase(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
using Services::StringService;
if (rawPacket.size() < 8) {
throw Exception{"Invalid packet length"};
}
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"};
}
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
this->bytes = StringService::toUint32(command.substr(delimiterPos + 1), 16);
}
void FlashErase::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling FlashErase packet");
try {
targetControllerService.enableProgrammingMode();
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(
gdbTargetDescriptor.systemAddressSpaceDescriptor,
gdbTargetDescriptor.programMemorySegmentDescriptor
);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to erase flash memory - " + exception.getMessage());
debugSession.programmingSession.reset();
try {
targetControllerService.disableProgrammingMode();
} catch (const Exception& exception) {
Logger::error("Failed to disable programming mode - " + exception.getMessage());
}
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <optional>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class FlashErase
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
std::uint32_t startAddress = 0;
std::uint32_t bytes = 0;
explicit FlashErase(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,99 @@
#include "FlashWrite.hpp"
#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"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::OkResponsePacket;
using namespace Exceptions;
FlashWrite::FlashWrite(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
using Services::StringService;
if (this->data.size() < 15) {
throw Exception{"Invalid packet length"};
}
/*
* The flash write ('vFlashWrite') packet consists of two segments: an address and a buffer, seperated by a
* colon.
*/
const auto delimiterIt = std::find(this->data.begin() + 12, this->data.end(), ':');
if (delimiterIt == this->data.end()) {
throw Exception{"Failed to find colon delimiter in write flash packet."};
}
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,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling FlashWrite packet");
try {
if (this->buffer.empty()) {
throw Exception{"Received empty buffer from GDB"};
}
if (!debugSession.programmingSession.has_value()) {
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
} else {
auto& programmingSession = debugSession.programmingSession.value();
const auto currentEndAddress = programmingSession.startAddress + programmingSession.buffer.size() - 1;
const auto expectedStartAddress = (currentEndAddress + 1);
if (this->startAddress < expectedStartAddress) {
throw Exception{"Invalid start address from GDB - the buffer would overlap a previous buffer"};
}
if (this->startAddress > expectedStartAddress) {
// There is a gap in the buffer sent by GDB. Fill it with 0xFF
programmingSession.buffer.insert(
programmingSession.buffer.end(),
this->startAddress - expectedStartAddress,
0xFF
);
}
programmingSession.buffer.insert(
programmingSession.buffer.end(),
this->buffer.begin(),
this->buffer.end()
);
}
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to handle FlashWrite packet - " + exception.getMessage());
debugSession.programmingSession.reset();
try {
targetControllerService.disableProgrammingMode();
} catch (const Exception& exception) {
Logger::error("Failed to disable programming mode - " + exception.getMessage());
}
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <optional>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class FlashWrite
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemoryBuffer buffer;
explicit FlashWrite(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,137 @@
#include "ReadMemory.hpp"
#include <cassert>
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::ResponsePacket;
using Exceptions::Exception;
ReadMemory::ReadMemory(const RawPacket& rawPacket, const RiscVGdbTargetDescriptor& gdbTargetDescriptor)
: ReadMemory(rawPacket, gdbTargetDescriptor, ReadMemory::extractPacketData(rawPacket))
{}
void ReadMemory::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadMemory packet");
try {
if (this->bytes == 0) {
debugSession.connection.writePacket(ResponsePacket{Targets::TargetMemoryBuffer{}});
return;
}
const auto addressRange = Targets::TargetMemoryAddressRange{
this->startAddress,
this->startAddress + this->bytes - 1
};
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);
}
if (accessibleBytes < this->bytes) {
Logger::debug(
"GDB requested access to memory which does not reside within any memory segment - returning error "
"response"
);
debugSession.connection.writePacket(ErrorResponsePacket{});
return;
}
auto buffer = Targets::TargetMemoryBuffer(this->bytes, 0x00);
{
const auto atomicSession = targetControllerService.makeAtomicSession();
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);
}
}
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{});
}
}
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 {
.gdbStartAddress = StringService::toUint32(command.substr(0, delimiterPos), 16),
.bytes = StringService::toUint32(command.substr(delimiterPos + 1), 16)
};
}
ReadMemory::ReadMemory(
const RawPacket& rawPacket,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
ReadMemory::PacketData&& packetData
)
: CommandPacket(rawPacket)
, addressSpaceDescriptor(gdbTargetDescriptor.systemAddressSpaceDescriptor)
, startAddress(packetData.gdbStartAddress)
, bytes(packetData.bytes)
{}
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <cstdint>
#include <optional>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class ReadMemory
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemorySize bytes;
ReadMemory(const RawPacket& rawPacket, const RiscVGdbTargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& 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 RiscVGdbTargetDescriptor& gdbTargetDescriptor,
PacketData&& packetData
);
};
}

View File

@@ -0,0 +1,99 @@
#include "ReadMemoryMap.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Helpers/BiMap.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using Services::StringService;
using ResponsePackets::ResponsePacket;
using Exceptions::Exception;
ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
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.
*/
const auto command = std::string{this->data.begin() + 23, this->data.end()};
const auto delimiterPos = command.find_first_of(',');
if (delimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
this->offset = StringService::toUint32(command.substr(0, delimiterPos), 16);
this->length = StringService::toUint32(command.substr(delimiterPos + 1), 16);
}
void ReadMemoryMap::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
using Targets::TargetMemorySegmentType;
Logger::info("Handling ReadMemoryMap packet");
static const auto gdbMemoryTypesBySegmentType = BiMap<TargetMemorySegmentType, std::string>{
{TargetMemorySegmentType::FLASH, "flash"},
{TargetMemorySegmentType::RAM, "ram"},
{TargetMemorySegmentType::IO, "ram"},
{TargetMemorySegmentType::ALIASED, "flash"}, // TODO: Assumption made here. Will hold for now. Review later
};
auto memoryMap = std::string{"<memory-map>\n"};
for (const auto& [segmentKey, segmentDescriptor] : gdbTargetDescriptor.systemAddressSpaceDescriptor.segmentDescriptorsByKey) {
const auto gdbMemType = gdbMemoryTypesBySegmentType.valueAt(segmentDescriptor.type);
if (!gdbMemType.has_value()) {
continue;
}
const auto segmentWritable = (
segmentDescriptor.debugModeAccess.writeable
|| segmentDescriptor.programmingModeAccess.writeable
);
memoryMap += "<memory type=\"" + (!segmentWritable ? "rom" : *gdbMemType) + "\" start=\"0x"
+ StringService::toHex(segmentDescriptor.addressRange.startAddress) + "\" length=\""
+ std::to_string(segmentDescriptor.size()) + "\"";
if (segmentWritable && segmentDescriptor.pageSize.has_value()) {
memoryMap += ">\n <property name=\"blocksize\">" + std::to_string(*(segmentDescriptor.pageSize))
+ "</property>\n</memory>\n";
} else {
memoryMap += "/>\n";
}
}
memoryMap += "</memory-map>";
auto responseData = std::vector<unsigned char>{'l'};
if (this->offset < memoryMap.size() && this->length > 0) {
responseData.insert(
responseData.end(),
memoryMap.begin() + this->offset,
memoryMap.begin() + this->offset + std::min(
static_cast<long>(this->length),
static_cast<long>(memoryMap.size() - this->offset)
)
);
}
debugSession.connection.writePacket(ResponsePacket{responseData});
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <cstdint>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class ReadMemoryMap
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
/**
* The offset of the memory map, from which to read.
*/
std::uint32_t offset = 0;
/**
* The length of the memory map to read.
*/
std::uint32_t length = 0;
explicit ReadMemoryMap(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,95 @@
#include "ReadRegister.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetRegisterDescriptors;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
ReadRegister::ReadRegister(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
using Services::StringService;
if (this->data.size() < 2) {
throw Exception{"Invalid packet length"};
}
this->registerId = static_cast<GdbRegisterId>(
StringService::toUint32(std::string{this->data.begin() + 1, this->data.end()}, 16)
);
}
void ReadRegister::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadRegister packet");
try {
Logger::debug("Reading GDB register ID: " + std::to_string(this->registerId));
if (this->registerId == gdbTargetDescriptor.programCounterGdbRegisterId) {
/*
* GDB has requested the program counter. We can't access this in the same way as we do with other
* registers.
*/
const auto programCounter = targetControllerService.getProgramCounter();
debugSession.connection.writePacket(
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 gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId);
const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find(
this->registerId
);
if (
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
) {
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
}
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.
registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00);
}
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{});
}
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class ReadRegister
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
GdbRegisterId registerId;
explicit ReadRegister(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,81 @@
#include "ReadRegisters.hpp"
#include <algorithm>
#include <iterator>
#include <cassert>
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
ReadRegisters::ReadRegisters(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{}
void ReadRegisters::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling ReadRegisters packet");
try {
const auto totalRegBytes = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.size() * 4;
auto buffer = Targets::TargetMemoryBuffer(totalRegBytes, 0x00);
auto gpRegDescriptors = Targets::TargetRegisterDescriptors{};
std::transform(
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.begin(),
gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end(),
std::back_inserter(gpRegDescriptors),
[] (const auto& pair) {
return pair.second;
}
);
{
const auto atomicSession = targetControllerService.makeAtomicSession();
for (auto& [regDesc, regVal] : targetControllerService.readRegisters(gpRegDescriptors)) {
const auto bufferOffset = (
regDesc.startAddress - gdbTargetDescriptor.gpRegistersMemorySegmentDescriptor.addressRange.startAddress
) * gdbTargetDescriptor.gpRegistersMemorySegmentDescriptor.addressSpaceUnitSize;
assert((buffer.size() - bufferOffset) >= regVal.size());
/*
* GDB expects register values in LSB form, which is why we use reverse iterators below.
*/
std::copy(regVal.rbegin(), regVal.rend(), buffer.begin() + bufferOffset);
}
const auto pcValue = targetControllerService.getProgramCounter();
buffer[totalRegBytes - 4] = static_cast<unsigned char>(pcValue);
buffer[totalRegBytes - 3] = static_cast<unsigned char>(pcValue >> 8);
buffer[totalRegBytes - 2] = static_cast<unsigned char>(pcValue >> 16);
buffer[totalRegBytes - 1] = static_cast<unsigned char>(pcValue >> 24);
}
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)});
} catch (const Exception& exception) {
Logger::error("Failed to read registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <optional>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class ReadRegisters
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
explicit ReadRegisters(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/DebugSession.hpp"
#include "src/DebugServer/Gdb/RiscVGdb/RiscVGdbTargetDescriptor.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "src/Services/TargetControllerService.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class RiscVGdbCommandPacketInterface
{
public:
virtual ~RiscVGdbCommandPacketInterface() = default;
/**
* Should handle the command for the current active debug session.
*
* @param debugSession
* The current active debug session.
*
* @param TargetControllerService
*/
virtual void handle(
DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) = 0;
};
}

View File

@@ -0,0 +1,24 @@
#include "VContSupportedActionsQuery.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
VContSupportedActionsQuery::VContSupportedActionsQuery(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{}
void VContSupportedActionsQuery::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling VContSupportedActionsQuery packet");
debugSession.connection.writePacket(ResponsePackets::ResponsePacket{"vCont;c;C;s;S"});
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <string>
#include <set>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class VContSupportedActionsQuery
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
explicit VContSupportedActionsQuery(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,132 @@
#include "WriteMemory.hpp"
#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"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::OkResponsePacket;
using namespace Exceptions;
WriteMemory::WriteMemory(const RawPacket& rawPacket, const RiscVGdbTargetDescriptor& gdbTargetDescriptor)
: WriteMemory(rawPacket, gdbTargetDescriptor, WriteMemory::extractPacketData(rawPacket))
{}
void WriteMemory::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling WriteMemory packet");
try {
if (this->buffer.empty()) {
debugSession.connection.writePacket(OkResponsePacket{});
return;
}
const auto& systemAddressSpaceDescriptor = gdbTargetDescriptor.systemAddressSpaceDescriptor;
const auto addressRange = Targets::TargetMemoryAddressRange{
this->startAddress,
this->startAddress + static_cast<Targets::TargetMemorySize>(this->buffer.size()) - 1
};
const auto memorySegmentDescriptors = systemAddressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
addressRange
);
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(
systemAddressSpaceDescriptor,
*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{});
}
}
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 {
.gdbStartAddress = StringService::toUint32(command.substr(0, commaDelimiterPos), 16),
.bytes = StringService::toUint32(
command.substr(commaDelimiterPos + 1, colonDelimiterPos - (commaDelimiterPos + 1)),
16
),
.buffer = StringService::dataFromHex(command.substr(colonDelimiterPos + 1))
};
}
WriteMemory::WriteMemory(
const RawPacket& rawPacket,
const RiscVGdbTargetDescriptor&,
PacketData&& packetData
)
: CommandPacket(rawPacket)
, startAddress(packetData.gdbStartAddress)
, bytes(packetData.bytes)
, buffer(std::move(packetData.buffer))
{
if (this->buffer.size() != this->bytes) {
throw Exception{"Buffer size does not match length value given in write memory packet"};
}
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <cstdint>
#include <optional>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class WriteMemory
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
Targets::TargetMemoryAddress startAddress;
Targets::TargetMemorySize bytes;
Targets::TargetMemoryBuffer buffer;
explicit WriteMemory(const RawPacket& rawPacket, const RiscVGdbTargetDescriptor& gdbTargetDescriptor);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
private:
struct PacketData
{
GdbMemoryAddress gdbStartAddress;
std::uint32_t bytes;
Targets::TargetMemoryBuffer buffer;
};
static PacketData extractPacketData(const RawPacket& rawPacket);
WriteMemory(const RawPacket& rawPacket, const RiscVGdbTargetDescriptor& gdbTargetDescriptor, PacketData&& packetData);
};
}

View File

@@ -0,0 +1,94 @@
#include "WriteRegister.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::ResponsePacket;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
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 command = std::string{this->data.begin() + 1, this->data.end()};
const auto delimiterPos = command.find_first_of('=');
if (delimiterPos == std::string::npos) {
throw Exception{"Invalid packet"};
}
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"};
}
// LSB to MSB
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
void WriteRegister::handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
Logger::info("Handling WriteRegister packet");
try {
if (this->registerId == gdbTargetDescriptor.programCounterGdbRegisterId) {
if (this->registerValue.size() != 4) {
throw Exception{"Invalid PC value register size"};
}
targetControllerService.setProgramCounter(
static_cast<Targets::TargetMemoryAddress>(
this->registerValue[0] << 24
| this->registerValue[1] << 16
| this->registerValue[2] << 8
| this->registerValue[3]
)
);
debugSession.connection.writePacket(OkResponsePacket{});
return;
}
const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId);
const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find(
this->registerId
);
if (
gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end()
|| targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end()
) {
throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"};
}
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{});
}
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/RegisterDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class WriteRegister
: public RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
GdbRegisterId registerId;
Targets::TargetMemoryBuffer registerValue;
explicit WriteRegister(const RawPacket& rawPacket);
void handle(
Gdb::DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,135 @@
#include "RiscVGdbRsp.hpp"
#include "src/Services/StringService.hpp"
// Command packets
#include "CommandPackets/ReadRegister.hpp"
#include "CommandPackets/ReadRegisters.hpp"
#include "CommandPackets/WriteRegister.hpp"
#include "CommandPackets/ReadMemory.hpp"
#include "CommandPackets/WriteMemory.hpp"
#include "CommandPackets/ReadMemoryMap.hpp"
#include "CommandPackets/FlashErase.hpp"
#include "CommandPackets/FlashWrite.hpp"
#include "CommandPackets/FlashDone.hpp"
#include "CommandPackets/VContSupportedActionsQuery.hpp"
#include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp"
namespace DebugServer::Gdb::RiscVGdb
{
using namespace Exceptions;
using Targets::TargetRegisterDescriptor;
using Targets::TargetRegisterType;
RiscVGdbRsp::RiscVGdbRsp(
const DebugServerConfig& debugServerConfig,
const Targets::TargetDescriptor& targetDescriptor,
EventListener& eventListener,
EventFdNotifier& eventNotifier
)
: GdbRspDebugServer(
debugServerConfig,
targetDescriptor,
RiscVGdbTargetDescriptor{targetDescriptor},
eventListener,
eventNotifier
)
{}
std::string RiscVGdbRsp::getName() const {
return "RISC-V GDB RSP Server";
}
std::unique_ptr<CommandPackets::RiscVGdbCommandPacketInterface> RiscVGdbRsp::rawPacketToCommandPacket(
const RawPacket& rawPacket
) {
using Gdb::CommandPackets::Monitor;
using CommandPackets::ReadRegister;
using CommandPackets::ReadRegisters;
using CommandPackets::WriteRegister;
using CommandPackets::ReadMemory;
using CommandPackets::WriteMemory;
using CommandPackets::ReadMemoryMap;
using CommandPackets::FlashErase;
using CommandPackets::FlashWrite;
using CommandPackets::FlashDone;
using CommandPackets::VContSupportedActionsQuery;
if (rawPacket.size() < 2) {
throw ::Exceptions::Exception{"Invalid raw packet - no data"};
}
if (rawPacket[1] == 'p') {
return std::make_unique<ReadRegister>(rawPacket);
}
if (rawPacket[1] == 'g') {
return std::make_unique<ReadRegisters>(rawPacket);
}
if (rawPacket[1] == 'P') {
return std::make_unique<WriteRegister>(rawPacket);
}
if (rawPacket[1] == 'm') {
return std::make_unique<ReadMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'M') {
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket.size() > 1) {
const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()};
if (rawPacketString.find("qXfer:memory-map:read::") == 0) {
return std::make_unique<ReadMemoryMap>(rawPacket);
}
if (rawPacketString.find("vFlashErase") == 0) {
return std::make_unique<FlashErase>(rawPacket);
}
if (rawPacketString.find("vFlashWrite") == 0) {
return std::make_unique<FlashWrite>(rawPacket);
}
if (rawPacketString.find("vFlashDone") == 0) {
return std::make_unique<FlashDone>(rawPacket);
}
if (rawPacketString.find("vCont?") == 0) {
return std::make_unique<VContSupportedActionsQuery>(rawPacket);
}
}
return nullptr;
}
std::set<std::pair<Feature, std::optional<std::string>>> RiscVGdbRsp::getSupportedFeatures() {
auto output = std::set<std::pair<Feature, std::optional<std::string>>>{
{Feature::HARDWARE_BREAKPOINTS, std::nullopt},
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
{Feature::MEMORY_MAP_READ, std::nullopt},
{Feature::VCONT_ACTIONS_QUERY, std::nullopt},
};
if (!this->debugServerConfig.packetAcknowledgement) {
output.emplace(Feature::NO_ACK_MODE, std::nullopt);
}
return output;
}
void RiscVGdbRsp::handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress) {
using Services::StringService;
Logger::debug("Target stopped at byte address: 0x" + StringService::toHex(programAddress));
// Report the stop to GDB
return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress);
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
#include "src/DebugServer/Gdb/GdbRspDebugServer.hpp"
#include "RiscVGdbTargetDescriptor.hpp"
#include "CommandPackets/RiscVGdbCommandPacketInterface.hpp"
namespace DebugServer::Gdb::RiscVGdb
{
class RiscVGdbRsp
: public GdbRspDebugServer<
RiscVGdbTargetDescriptor,
DebugSession,
CommandPackets::RiscVGdbCommandPacketInterface
>
{
public:
RiscVGdbRsp(
const DebugServerConfig& debugServerConfig,
const Targets::TargetDescriptor& targetDescriptor,
EventListener& eventListener,
EventFdNotifier& eventNotifier
);
std::string getName() const override;
protected:
std::unique_ptr<CommandPackets::RiscVGdbCommandPacketInterface> rawPacketToCommandPacket(
const RawPacket& rawPacket
) override;
std::set<std::pair<Feature, std::optional<std::string>>> getSupportedFeatures() override;
void handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress) override;
};
}

View File

@@ -0,0 +1,40 @@
#include "RiscVGdbTargetDescriptor.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb
{
using Targets::TargetRegisterDescriptor;
using Targets::TargetRegisterType;
using Exceptions::Exception;
RiscVGdbTargetDescriptor::RiscVGdbTargetDescriptor(const Targets::TargetDescriptor& targetDescriptor)
: systemAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("system"))
, cpuAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("debug_module"))
, programMemorySegmentDescriptor(this->systemAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_program_memory"))
, gpRegistersMemorySegmentDescriptor(this->cpuAddressSpaceDescriptor.getMemorySegmentDescriptor("gp_registers"))
, cpuGpPeripheralDescriptor(targetDescriptor.getPeripheralDescriptor("cpu"))
, cpuGpRegisterGroupDescriptor(this->cpuGpPeripheralDescriptor.getRegisterGroupDescriptor("gpr"))
, programCounterGdbRegisterId(static_cast<GdbRegisterId>(this->cpuGpRegisterGroupDescriptor.registerDescriptorsByKey.size()))
{
// Create the GDB register descriptors and populate the mappings for the general purpose registers (ID 0->31)
for (const auto& [key, descriptor] : this->cpuGpRegisterGroupDescriptor.registerDescriptorsByKey) {
if (descriptor.type != TargetRegisterType::GENERAL_PURPOSE_REGISTER) {
continue;
}
const auto gdbRegisterId = static_cast<GdbRegisterId>(
descriptor.startAddress - this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress
);
this->gdbRegisterDescriptorsById.emplace(gdbRegisterId, RegisterDescriptor{gdbRegisterId, 4});
this->targetRegisterDescriptorsByGdbId.emplace(gdbRegisterId, &descriptor);
}
this->gdbRegisterDescriptorsById.emplace(
this->programCounterGdbRegisterId,
RegisterDescriptor{this->programCounterGdbRegisterId, 4}
);
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
#include "src/Targets/TargetPeripheralDescriptor.hpp"
#include "src/Targets/TargetRegisterGroupDescriptor.hpp"
namespace DebugServer::Gdb::RiscVGdb
{
class RiscVGdbTargetDescriptor: public DebugServer::Gdb::TargetDescriptor
{
public:
const Targets::TargetAddressSpaceDescriptor& systemAddressSpaceDescriptor;
const Targets::TargetAddressSpaceDescriptor& cpuAddressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor;
const Targets::TargetPeripheralDescriptor& cpuGpPeripheralDescriptor;
const Targets::TargetRegisterGroupDescriptor& cpuGpRegisterGroupDescriptor;
const GdbRegisterId programCounterGdbRegisterId;
explicit RiscVGdbTargetDescriptor(const Targets::TargetDescriptor& targetDescriptor);
};
}

View File

@@ -85,6 +85,20 @@ namespace Targets::RiscV::Wch
sysAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_program_memory").inspectionEnabled = true;
sysAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_ram").inspectionEnabled = true;
/*
* WCH targets typically possess a memory segment that is mapped to program memory. We cannot write to this
* segment directly, which is why it's described as read-only in Bloom's TDFs. However, we enable writing to
* the segment by forwarding any write operations to the appropriate (aliased) segment.
*
* For this reason, we adjust the access member on the memory segment descriptor so that other components
* within Bloom will see the segment as writeable.
*
* See the overridden WchRiscV::writeMemory() member function below, for more.
*/
sysAddressSpaceDescriptor.getMemorySegmentDescriptor(
this->mappedProgramMemorySegmentDescriptor.key
).programmingModeAccess.writeable = true;
return descriptor;
}
@@ -95,12 +109,16 @@ namespace Targets::RiscV::Wch
const TargetMemoryBuffer& buffer
) {
/*
* WCH targets have a memory segment that maps to either the program memory segment or the boot program
* WCH targets have an alias segment that maps to either the program memory segment or the boot program
* memory segment.
*
* Reading directly from the mapped memory segment is fine, but we cannot write to it - the operation just
* fails silently. We handle this by altering the write operation so that we write to the appropriate,
* non-mapped segment.
* Reading directly from this memory segment is fine, but we cannot write to it - the operation just fails
* silently. We handle this by forwarding any write operations on that segment to the appropriate (aliased)
* segment.
*
* @TODO: Currently, this just assumes that the alias segment always maps to the program memory segment, but I
* believe it may map to the boot program memory segment in some cases. This needs to be revisited
* before v1.1.0.
*/
if (memorySegmentDescriptor == this->mappedProgramMemorySegmentDescriptor) {
const auto newAddress = startAddress - this->mappedProgramMemorySegmentDescriptor.addressRange.startAddress