RISC-V GDB server
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
103
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashDone.cpp
Normal file
103
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashDone.cpp
Normal 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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashDone.hpp
Normal file
28
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashDone.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
74
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashErase.cpp
Normal file
74
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashErase.cpp
Normal 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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashErase.hpp
Normal file
31
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashErase.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
99
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashWrite.cpp
Normal file
99
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashWrite.cpp
Normal 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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashWrite.hpp
Normal file
30
src/DebugServer/Gdb/RiscVGdb/CommandPackets/FlashWrite.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
137
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadMemory.cpp
Normal file
137
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadMemory.cpp
Normal 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)
|
||||
{}
|
||||
}
|
||||
47
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadMemory.hpp
Normal file
47
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadMemory.hpp
Normal 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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
95
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadRegister.cpp
Normal file
95
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadRegister.cpp
Normal 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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadRegister.hpp
Normal file
26
src/DebugServer/Gdb/RiscVGdb/CommandPackets/ReadRegister.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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"});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
132
src/DebugServer/Gdb/RiscVGdb/CommandPackets/WriteMemory.cpp
Normal file
132
src/DebugServer/Gdb/RiscVGdb/CommandPackets/WriteMemory.cpp
Normal 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"};
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/DebugServer/Gdb/RiscVGdb/CommandPackets/WriteMemory.hpp
Normal file
42
src/DebugServer/Gdb/RiscVGdb/CommandPackets/WriteMemory.hpp
Normal 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);
|
||||
};
|
||||
}
|
||||
@@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
135
src/DebugServer/Gdb/RiscVGdb/RiscVGdbRsp.cpp
Normal file
135
src/DebugServer/Gdb/RiscVGdb/RiscVGdbRsp.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
35
src/DebugServer/Gdb/RiscVGdb/RiscVGdbRsp.hpp
Normal file
35
src/DebugServer/Gdb/RiscVGdb/RiscVGdbRsp.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
40
src/DebugServer/Gdb/RiscVGdb/RiscVGdbTargetDescriptor.cpp
Normal file
40
src/DebugServer/Gdb/RiscVGdb/RiscVGdbTargetDescriptor.cpp
Normal 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}
|
||||
);
|
||||
}
|
||||
}
|
||||
29
src/DebugServer/Gdb/RiscVGdb/RiscVGdbTargetDescriptor.hpp
Normal file
29
src/DebugServer/Gdb/RiscVGdb/RiscVGdbTargetDescriptor.hpp
Normal 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);
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user