From ffe4f5bfc57901907e15d4c897e89452e836ea95 Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 10 Dec 2022 19:22:53 +0000 Subject: [PATCH] New `monitor eeprom fill` GDB command --- src/DebugServer/CMakeLists.txt | 1 + .../Gdb/CommandPackets/EepromFill.cpp | 110 ++++++++++++++++++ .../Gdb/CommandPackets/EepromFill.hpp | 29 +++++ .../Gdb/Exceptions/InvalidCommandOption.hpp | 26 +++++ src/DebugServer/Gdb/GdbRspDebugServer.cpp | 5 + 5 files changed, 171 insertions(+) create mode 100644 src/DebugServer/Gdb/CommandPackets/EepromFill.cpp create mode 100644 src/DebugServer/Gdb/CommandPackets/EepromFill.hpp create mode 100644 src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp diff --git a/src/DebugServer/CMakeLists.txt b/src/DebugServer/CMakeLists.txt index d13ae1e8..2dd23c0d 100755 --- a/src/DebugServer/CMakeLists.txt +++ b/src/DebugServer/CMakeLists.txt @@ -25,6 +25,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/GenerateSvd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/Detach.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/EepromFill.cpp # AVR GDB RSP Server ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp diff --git a/src/DebugServer/Gdb/CommandPackets/EepromFill.cpp b/src/DebugServer/Gdb/CommandPackets/EepromFill.cpp new file mode 100644 index 00000000..45882732 --- /dev/null +++ b/src/DebugServer/Gdb/CommandPackets/EepromFill.cpp @@ -0,0 +1,110 @@ +#include "EepromFill.hpp" + +#include +#include + +#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp" +#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" + +#include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp" +#include "src/Exceptions/Exception.hpp" +#include "src/Logger/Logger.hpp" + +namespace Bloom::DebugServer::Gdb::CommandPackets +{ + using TargetController::TargetControllerConsole; + + using ResponsePackets::ResponsePacket; + using ResponsePackets::ErrorResponsePacket; + using Bloom::Exceptions::Exception; + using Exceptions::InvalidCommandOption; + + EepromFill::EepromFill(Monitor&& monitorPacket) + : Monitor(std::move(monitorPacket)) + { + const auto fillValueOptionIt = this->commandOptions.find("value"); + + if (fillValueOptionIt == this->commandOptions.end() || !fillValueOptionIt->second.has_value()) { + return; + } + + const auto fillValueByteArray = QByteArray::fromHex(QByteArray::fromStdString(*fillValueOptionIt->second)); + this->fillValue = Targets::TargetMemoryBuffer(fillValueByteArray.begin(), fillValueByteArray.end()); + } + + void EepromFill::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) { + Logger::debug("Handling EepromFill packet"); + + try { + const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor; + + const auto eepromDescriptorIt = targetDescriptor.memoryDescriptorsByType.find( + Targets::TargetMemoryType::EEPROM + ); + + if (eepromDescriptorIt == targetDescriptor.memoryDescriptorsByType.end()) { + throw Exception("Target has no EEPROM"); + } + + const auto& eepromDescriptor = eepromDescriptorIt->second; + const auto eepromSize = eepromDescriptor.size(); + + const auto fillValueSize = this->fillValue.size(); + + if (fillValueSize == 0) { + throw InvalidCommandOption("Fill value required"); + } + + if (fillValueSize > eepromSize) { + throw InvalidCommandOption( + "Fill value size ("+ std::to_string(fillValueSize) + " bytes) exceeds EEPROM size (" + + std::to_string(eepromSize) + " bytes)" + ); + } + + if ((eepromSize % fillValueSize) != 0) { + Logger::warning( + "The fill value size (" + std::to_string(fillValueSize) + " bytes) is not a multiple of the EEPROM " + "size (" + std::to_string(eepromSize) + " bytes) - the fill value will be truncated" + ); + } + + Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM"); + + auto data = Targets::TargetMemoryBuffer(); + data.reserve(eepromSize); + + // Repeat this->fillValue until we've filled `data` + while (data.size() < eepromSize) { + data.insert( + data.end(), + this->fillValue.begin(), + this->fillValue.begin() + static_cast( + std::min(fillValueSize, (eepromSize - data.size())) + ) + ); + } + + const auto hexValues = Packet::toHex(data); + Logger::debug("Filling EEPROM with values: " + hexValues); + + targetControllerConsole.writeMemory( + Targets::TargetMemoryType::EEPROM, + eepromDescriptor.addressRange.startAddress, + std::move(data) + ); + + debugSession.connection.writePacket(ResponsePacket(Packet::toHex( + "Filled " + std::to_string(eepromSize) + " bytes of EEPROM, with values: " + hexValues + "\n" + ))); + + } catch (const InvalidCommandOption& exception) { + Logger::error(exception.getMessage()); + debugSession.connection.writePacket(ResponsePacket(Packet::toHex(exception.getMessage() + "\n"))); + + } catch (const Exception& exception) { + Logger::error("Failed to fill EEPROM - " + exception.getMessage()); + debugSession.connection.writePacket(ErrorResponsePacket()); + } + } +} diff --git a/src/DebugServer/Gdb/CommandPackets/EepromFill.hpp b/src/DebugServer/Gdb/CommandPackets/EepromFill.hpp new file mode 100644 index 00000000..534f847c --- /dev/null +++ b/src/DebugServer/Gdb/CommandPackets/EepromFill.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "Monitor.hpp" + +#include "src/Targets/TargetMemory.hpp" + +namespace Bloom::DebugServer::Gdb::CommandPackets +{ + /** + * The EepromFill class implements a structure for the "monitor eeprom fill" GDB command. + * + * This command fills the target's EEPROM with the given value. + */ + class EepromFill: public Monitor + { + public: + explicit EepromFill(Monitor&& monitorPacket); + + void handle( + DebugSession& debugSession, + TargetController::TargetControllerConsole& targetControllerConsole + ) override; + + private: + Targets::TargetMemoryBuffer fillValue; + }; +} diff --git a/src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp b/src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp new file mode 100644 index 00000000..3376392c --- /dev/null +++ b/src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "src/Exceptions/Exception.hpp" + +namespace Bloom::DebugServer::Gdb::Exceptions +{ + /** + * For GDB monitor commands, each command can define a set of required/optional command options. + * This exception is thrown if an invalid option is given. + * + * This exception is typically thrown and caught within the handling of monitor commands. + */ + class InvalidCommandOption: public Bloom::Exceptions::Exception + { + public: + explicit InvalidCommandOption(const std::string& message) + : Bloom::Exceptions::Exception(message) + {} + + explicit InvalidCommandOption(const char* message) + : Bloom::Exceptions::Exception(message) + {} + + explicit InvalidCommandOption() = default; + }; +} diff --git a/src/DebugServer/Gdb/GdbRspDebugServer.cpp b/src/DebugServer/Gdb/GdbRspDebugServer.cpp index 31b60625..286ed981 100644 --- a/src/DebugServer/Gdb/GdbRspDebugServer.cpp +++ b/src/DebugServer/Gdb/GdbRspDebugServer.cpp @@ -32,6 +32,7 @@ #include "CommandPackets/BloomVersionMachine.hpp" #include "CommandPackets/GenerateSvd.hpp" #include "CommandPackets/Detach.hpp" +#include "CommandPackets/EepromFill.hpp" // Response packets #include "ResponsePackets/TargetStopped.hpp" @@ -314,6 +315,10 @@ namespace Bloom::DebugServer::Gdb return std::make_unique(std::move(*(monitorCommand.release()))); } + if (monitorCommand->command.find("eeprom fill") == 0) { + return std::make_unique(std::move(*(monitorCommand.release()))); + } + return monitorCommand; } }