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/VContSupportedActionsQuery.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/EepromFill.cpp
|
${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)
|
if (NOT EXCLUDE_INSIGHT)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
// Debug server implementations
|
// Debug server implementations
|
||||||
#include "Gdb/AvrGdb/AvrGdbRsp.hpp"
|
#include "Gdb/AvrGdb/AvrGdbRsp.hpp"
|
||||||
|
#include "Gdb/RiscVGdb/RiscVGdbRsp.hpp"
|
||||||
|
|
||||||
#include "src/Exceptions/InvalidConfig.hpp"
|
#include "src/Exceptions/InvalidConfig.hpp"
|
||||||
#include "src/Logger/Logger.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_program_memory").inspectionEnabled = true;
|
||||||
sysAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_ram").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;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,12 +109,16 @@ namespace Targets::RiscV::Wch
|
|||||||
const TargetMemoryBuffer& buffer
|
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.
|
* memory segment.
|
||||||
*
|
*
|
||||||
* Reading directly from the mapped memory segment is fine, but we cannot write to it - the operation just
|
* Reading directly from this memory segment is fine, but we cannot write to it - the operation just fails
|
||||||
* fails silently. We handle this by altering the write operation so that we write to the appropriate,
|
* silently. We handle this by forwarding any write operations on that segment to the appropriate (aliased)
|
||||||
* non-mapped segment.
|
* 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) {
|
if (memorySegmentDescriptor == this->mappedProgramMemorySegmentDescriptor) {
|
||||||
const auto newAddress = startAddress - this->mappedProgramMemorySegmentDescriptor.addressRange.startAddress
|
const auto newAddress = startAddress - this->mappedProgramMemorySegmentDescriptor.addressRange.startAddress
|
||||||
|
|||||||
Reference in New Issue
Block a user