#include "TargetControllerComponent.hpp" #include #include #include #include "Responses/Error.hpp" #include "src/Services/ProcessService.hpp" #include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/InvalidConfig.hpp" namespace TargetController { using namespace Targets; using namespace Events; using namespace Exceptions; using Commands::CommandIdType; using Commands::Command; using Commands::StartAtomicSession; using Commands::EndAtomicSession; using Commands::Shutdown; using Commands::GetTargetDescriptor; using Commands::GetTargetState; using Commands::StopTargetExecution; using Commands::ResumeTargetExecution; using Commands::ResetTarget; using Commands::ReadTargetRegisters; using Commands::WriteTargetRegisters; using Commands::ReadTargetMemory; using Commands::WriteTargetMemory; using Commands::EraseTargetMemory; using Commands::StepTargetExecution; using Commands::SetBreakpoint; using Commands::RemoveBreakpoint; using Commands::SetTargetProgramCounter; using Commands::GetTargetPinStates; using Commands::SetTargetPinState; using Commands::GetTargetStackPointer; using Commands::GetTargetProgramCounter; using Commands::EnableProgrammingMode; using Commands::DisableProgrammingMode; using Responses::Response; using Responses::AtomicSessionId; using Responses::TargetRegistersRead; using Responses::TargetMemoryRead; using Responses::TargetPinStates; using Responses::TargetStackPointer; using Responses::TargetProgramCounter; using Responses::Breakpoint; TargetControllerComponent::TargetControllerComponent( const ProjectConfig& projectConfig, const EnvironmentConfig& environmentConfig ) : projectConfig(projectConfig) , environmentConfig(environmentConfig) {} void TargetControllerComponent::run() { try { this->startup(); this->setThreadStateAndEmitEvent(ThreadState::READY); Logger::debug("TargetController ready and waiting for events."); while (this->getThreadState() == ThreadState::READY) { this->fireTargetEvents(); TargetControllerComponent::notifier.waitForNotification(std::chrono::milliseconds(60)); this->processQueuedCommands(); this->eventListener->dispatchCurrentEvents(); } } catch (const std::exception& exception) { Logger::error("The TargetController encountered a fatal error. See below for errors:"); Logger::error(std::string(exception.what())); } this->shutdown(); } void TargetControllerComponent::registerCommand( std::unique_ptr command, const std::optional& atomicSessionId ) { if (TargetControllerComponent::state != TargetControllerState::ACTIVE) { throw Exception("Command rejected - TargetController not in active state."); } if (atomicSessionId.has_value()) { // This command is part of an atomic session - put it in the dedicated queue TargetControllerComponent::atomicSessionCommandQueue.accessor()->push(std::move(command)); TargetControllerComponent::notifier.notify(); return; } TargetControllerComponent::commandQueue.accessor()->push(std::move(command)); TargetControllerComponent::notifier.notify(); } std::optional> TargetControllerComponent::waitForResponse( CommandIdType commandId, std::optional timeout ) { auto response = std::unique_ptr(nullptr); const auto predicate = [commandId, &response] { // We will already hold the lock here, so we can use Synchronised::unsafeReference() here. auto& responsesByCommandId = TargetControllerComponent::responsesByCommandId.unsafeReference(); auto responseIt = responsesByCommandId.find(commandId); if (responseIt != responsesByCommandId.end()) { response.swap(responseIt->second); responsesByCommandId.erase(responseIt); return true; } return false; }; auto responsesByCommandIdLock = TargetControllerComponent::responsesByCommandId.lock(); if (timeout.has_value()) { TargetControllerComponent::responsesByCommandIdCv.wait_for( responsesByCommandIdLock, timeout.value(), predicate ); } else { TargetControllerComponent::responsesByCommandIdCv.wait(responsesByCommandIdLock, predicate); } return (response != nullptr) ? std::optional(std::move(response)) : std::nullopt; } void TargetControllerComponent::deregisterCommandHandler(Commands::CommandType commandType) { this->commandHandlersByCommandType.erase(commandType); } void TargetControllerComponent::startup() { this->setName("TC"); Logger::info("Starting TargetController"); this->threadState = ThreadState::STARTING; this->blockAllSignals(); this->eventListener->setInterruptEventNotifier(&TargetControllerComponent::notifier); EventManager::registerListener(this->eventListener); // Register command handlers this->registerCommandHandler( std::bind(&TargetControllerComponent::handleStartAtomicSession, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleEndAtomicSession, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleShutdown, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleGetTargetDescriptor, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleGetTargetState, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleStopTargetExecution, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleResumeTargetExecution, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleResetTarget, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleReadTargetRegisters, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleWriteTargetRegisters, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleReadTargetMemory, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleWriteTargetMemory, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleEraseTargetMemory, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleStepTargetExecution, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleSetBreakpoint, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleRemoveBreakpoint, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleSetProgramCounter, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleGetTargetPinStates, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleSetTargetPinState, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleGetTargetStackPointer, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleGetTargetProgramCounter, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleEnableProgrammingMode, this, std::placeholders::_1) ); this->registerCommandHandler( std::bind(&TargetControllerComponent::handleDisableProgrammingMode, this, std::placeholders::_1) ); // Register event handlers this->eventListener->registerCallbackForEventType( std::bind(&TargetControllerComponent::onShutdownTargetControllerEvent, this, std::placeholders::_1) ); this->eventListener->registerCallbackForEventType( std::bind(&TargetControllerComponent::onDebugSessionFinishedEvent, this, std::placeholders::_1) ); this->acquireHardware(); this->loadRegisterDescriptors(); if (this->target->getState() != TargetState::RUNNING) { this->target->run(); this->lastTargetState = TargetState::RUNNING; } this->state = TargetControllerState::ACTIVE; } void TargetControllerComponent::shutdown() { const auto threadState = this->getThreadState(); if (threadState == ThreadState::SHUTDOWN_INITIATED || threadState == ThreadState::STOPPED) { return; } this->threadState = ThreadState::SHUTDOWN_INITIATED; try { Logger::info("Shutting down TargetController"); this->state = TargetControllerState::INACTIVE; EventManager::deregisterListener(this->eventListener->getId()); if (this->activeAtomicSession.has_value()) { // Reject any commands on the dedicated queue this->processQueuedCommands(); this->endActiveAtomicSession(); } // Reject any commands still waiting in the queue this->processQueuedCommands(); this->releaseHardware(); } catch (const std::exception& exception) { this->target.reset(); this->debugTool.reset(); Logger::error( "Failed to properly shut down TargetController. Error: " + std::string(exception.what()) ); } this->setThreadStateAndEmitEvent(ThreadState::STOPPED); } std::map< std::string, std::function()> > TargetControllerComponent::getSupportedDebugTools() { // The debug tool names in this mapping should always be lower-case. return std::map()>> { { "atmel-ice", [] { return std::make_unique(); } }, { "power-debugger", [] { return std::make_unique(); } }, { "snap", [] { return std::make_unique(); } }, { "pickit-4", [] { return std::make_unique(); } }, { "xplained-pro", [] { return std::make_unique(); } }, { "xplained-mini", [] { return std::make_unique(); } }, { "xplained-nano", [] { return std::make_unique(); } }, { "curiosity-nano", [] { return std::make_unique(); } }, { "jtagice3", [] { return std::make_unique(); } }, }; } std::map< std::string, std::function(const TargetConfig&)> > TargetControllerComponent::getSupportedTargets() { using Avr8TargetDescriptionFile = Targets::Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile; auto mapping = std::map(const TargetConfig&)>>(); // Include all targets from AVR8 target description files const auto avr8PdMapping = Avr8TargetDescriptionFile::getTargetDescriptionMapping(); for (auto mapIt = avr8PdMapping.begin(); mapIt != avr8PdMapping.end(); ++mapIt) { const auto mappingObject = mapIt.value().toObject(); const auto targetName = mappingObject.find("name").value().toString().toLower().toStdString(); if (!mapping.contains(targetName)) { mapping.insert({ targetName, [targetName] (const TargetConfig& targetConfig) { return std::make_unique(targetConfig); } }); } } return mapping; } void TargetControllerComponent::processQueuedCommands() { auto commands = std::queue>(); commands.swap( this->activeAtomicSession.has_value() ? *(TargetControllerComponent::atomicSessionCommandQueue.accessor()) : *(TargetControllerComponent::commandQueue.accessor()) ); while (!commands.empty()) { const auto command = std::move(commands.front()); commands.pop(); const auto commandId = command->id; const auto commandType = command->getType(); try { const auto commandHandlerIt = this->commandHandlersByCommandType.find(commandType); if (commandHandlerIt == this->commandHandlersByCommandType.end()) { throw Exception("No handler registered for this command."); } if (this->state != TargetControllerState::ACTIVE) { throw Exception("Command rejected - TargetController not in active state."); } if (command->requiresStoppedTargetState() && this->lastTargetState != TargetState::STOPPED) { throw Exception("Command rejected - command requires target execution to be stopped."); } if (this->target->programmingModeEnabled() && command->requiresDebugMode()) { throw Exception( "Command rejected - command cannot be serviced whilst the target is in programming mode." ); } this->registerCommandResponse(commandId, commandHandlerIt->second(*(command.get()))); } catch (const DeviceFailure& exception) { this->registerCommandResponse( commandId, std::make_unique(exception.getMessage()) ); throw exception; } catch (const Exception& exception) { this->registerCommandResponse( commandId, std::make_unique(exception.getMessage()) ); } } } void TargetControllerComponent::registerCommandResponse( CommandIdType commandId, std::unique_ptr response ) { TargetControllerComponent::responsesByCommandId.accessor()->emplace(commandId, std::move(response)); TargetControllerComponent::responsesByCommandIdCv.notify_all(); } void TargetControllerComponent::acquireHardware() { const auto& debugToolName = this->environmentConfig.debugToolConfig.name; const auto& targetName = this->environmentConfig.targetConfig.name; static const auto supportedDebugTools = this->getSupportedDebugTools(); static const auto supportedTargets = this->getSupportedTargets(); const auto debugToolIt = supportedDebugTools.find(debugToolName); const auto targetIt = supportedTargets.find(targetName); if (debugToolIt == supportedDebugTools.end()) { throw Exceptions::InvalidConfig( "Debug tool name (\"" + debugToolName + "\") not recognised. Please check your configuration!" ); } if (targetIt == supportedTargets.end()) { throw Exceptions::InvalidConfig( "Target name (\"" + targetName + "\") not recognised. Please check your configuration!" ); } this->debugTool = debugToolIt->second(); Logger::info("Connecting to debug tool"); this->debugTool->init(); Logger::info("Debug tool connected"); Logger::info("Debug tool name: " + this->debugTool->getName()); Logger::info("Debug tool serial: " + this->debugTool->getSerialNumber()); this->target = targetIt->second(this->environmentConfig.targetConfig); const auto& targetDescriptor = this->getTargetDescriptor(); if (!this->target->supportsDebugTool(this->debugTool.get())) { throw Exceptions::InvalidConfig( "Debug tool (\"" + this->debugTool->getName() + "\") not supported by target (\"" + targetDescriptor.name + "\")." ); } this->target->setDebugTool(this->debugTool.get()); Logger::info("Activating target"); this->target->activate(); Logger::info("Target activated"); Logger::info("Target ID: " + targetDescriptor.id); Logger::info("Target name: " + targetDescriptor.name); if (!this->environmentConfig.targetConfig.hardwareBreakpoints) { Logger::warning("Hardware breakpoints have been disabled"); } else if (targetDescriptor.breakpointResources.maximumHardwareBreakpoints.has_value()) { Logger::info( "Available hardware breakpoints: " + std::to_string( *(targetDescriptor.breakpointResources.maximumHardwareBreakpoints) ) ); } this->programMemoryCache = std::make_unique( targetDescriptor.memoryDescriptorsByType.at(targetDescriptor.programMemoryType) ); } void TargetControllerComponent::releaseHardware() { /* * Transferring ownership of this->debugTool and this->target to this function block means if an exception is * thrown, the objects will still be destroyed. */ auto debugTool = std::move(this->debugTool); auto target = std::move(this->target); if (debugTool != nullptr && debugTool->isInitialised()) { if (target != nullptr) { /* * We call deactivate() without checking if the target is activated. This will address any cases * where a target is only partially activated (where the call to activate() failed). */ Logger::info("Deactivating target"); target->deactivate(); } Logger::info("Closing debug tool"); debugTool->close(); } } void TargetControllerComponent::startAtomicSession() { if (this->activeAtomicSession.has_value()) { throw Exception("Atomic session already active - nested sessions are not supported"); } this->activeAtomicSession.emplace(); } void TargetControllerComponent::endActiveAtomicSession() { if (!this->activeAtomicSession.has_value()) { return; } { auto commandQueue = TargetControllerComponent::atomicSessionCommandQueue.accessor(); auto empty = std::queue>(); commandQueue->swap(empty); } this->activeAtomicSession.reset(); TargetControllerComponent::notifier.notify(); } void TargetControllerComponent::loadRegisterDescriptors() { const auto& targetDescriptor = this->getTargetDescriptor(); for (const auto& [registerDescriptorId, registerDescriptor] : targetDescriptor.registerDescriptorsById) { auto startAddress = registerDescriptor.startAddress.value_or(0); auto endAddress = startAddress + (registerDescriptor.size - 1); const auto registerAddressRangeIt = this->registerAddressRangeByMemoryType.find( registerDescriptor.memoryType ); if (registerAddressRangeIt == this->registerAddressRangeByMemoryType.end()) { this->registerAddressRangeByMemoryType.insert( std::pair(registerDescriptor.memoryType, TargetMemoryAddressRange(startAddress, endAddress)) ); } else { auto& addressRange = registerAddressRangeIt->second; if (startAddress < addressRange.startAddress) { addressRange.startAddress = startAddress; } if (endAddress > addressRange.endAddress) { addressRange.endAddress = endAddress; } } this->registerDescriptorsByMemoryType[registerDescriptor.memoryType].insert(registerDescriptor); } } TargetRegisterDescriptors TargetControllerComponent::getRegisterDescriptorsWithinAddressRange( Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryAddress endAddress, Targets::TargetMemoryType memoryType ) { auto output = TargetRegisterDescriptors(); const auto registerAddressRangeIt = this->registerAddressRangeByMemoryType.find(memoryType); const auto registerDescriptorsIt = this->registerDescriptorsByMemoryType.find(memoryType); if ( registerAddressRangeIt != this->registerAddressRangeByMemoryType.end() && registerDescriptorsIt != this->registerDescriptorsByMemoryType.end() ) { const auto& registersAddressRange = registerAddressRangeIt->second; if ( (startAddress <= registersAddressRange.startAddress && endAddress >= registersAddressRange.startAddress) || (startAddress <= registersAddressRange.endAddress && endAddress >= registersAddressRange.startAddress) ) { const auto& registerDescriptors = registerDescriptorsIt->second; for (const auto& registerDescriptor : registerDescriptors) { if (!registerDescriptor.startAddress.has_value() || registerDescriptor.size < 1) { continue; } const auto registerStartAddress = registerDescriptor.startAddress.value(); const auto registerEndAddress = registerStartAddress + registerDescriptor.size; if ( (startAddress <= registerStartAddress && endAddress >= registerStartAddress) || (startAddress <= registerEndAddress && endAddress >= registerStartAddress) ) { output.insert(registerDescriptor); } } } } return output; } void TargetControllerComponent::fireTargetEvents() { auto newTargetState = this->target->getState(); if (newTargetState != this->lastTargetState) { this->lastTargetState = newTargetState; if (newTargetState == TargetState::STOPPED) { Logger::debug("Target state changed - STOPPED"); EventManager::triggerEvent(std::make_shared( this->target->getProgramCounter(), TargetBreakCause::UNKNOWN )); } if (newTargetState == TargetState::RUNNING) { Logger::debug("Target state changed - RUNNING"); EventManager::triggerEvent(std::make_shared(false)); } } } void TargetControllerComponent::resetTarget() { this->target->reset(); EventManager::triggerEvent(std::make_shared()); } void TargetControllerComponent::enableProgrammingMode() { Logger::debug("Enabling programming mode"); this->target->enableProgrammingMode(); Logger::warning("Programming mode enabled"); EventManager::triggerEvent(std::make_shared()); } void TargetControllerComponent::disableProgrammingMode() { Logger::debug("Disabling programming mode"); this->target->disableProgrammingMode(); Logger::info("Programming mode disabled"); EventManager::triggerEvent(std::make_shared()); } const Targets::TargetDescriptor& TargetControllerComponent::getTargetDescriptor() { if (!this->targetDescriptor.has_value()) { this->targetDescriptor.emplace(this->target->getDescriptor()); } return *this->targetDescriptor; } void TargetControllerComponent::onShutdownTargetControllerEvent(const Events::ShutdownTargetController&) { this->shutdown(); } void TargetControllerComponent::onDebugSessionFinishedEvent(const DebugSessionFinished&) { if (this->state != TargetControllerState::ACTIVE) { return; } if (this->target->getState() != TargetState::RUNNING) { this->target->run(); this->fireTargetEvents(); } } std::unique_ptr TargetControllerComponent::handleStartAtomicSession(StartAtomicSession& command) { this->startAtomicSession(); return std::make_unique(this->activeAtomicSession->id); } std::unique_ptr TargetControllerComponent::handleEndAtomicSession(EndAtomicSession& command) { if (!this->activeAtomicSession.has_value() || this->activeAtomicSession->id != command.sessionId) { throw Exception("Atomic session is not active"); } this->endActiveAtomicSession(); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleShutdown(Shutdown& command) { this->shutdown(); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleGetTargetDescriptor( GetTargetDescriptor& command ) { return std::make_unique(this->getTargetDescriptor()); } std::unique_ptr TargetControllerComponent::handleGetTargetState(GetTargetState& command) { return std::make_unique(this->target->getState()); } std::unique_ptr TargetControllerComponent::handleStopTargetExecution(StopTargetExecution& command) { if (this->target->getState() != TargetState::STOPPED) { this->target->stop(); this->lastTargetState = TargetState::STOPPED; } EventManager::triggerEvent(std::make_shared( this->target->getProgramCounter(), TargetBreakCause::UNKNOWN )); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleResumeTargetExecution( ResumeTargetExecution& command ) { if (this->target->getState() != TargetState::RUNNING) { if (command.fromAddress.has_value()) { this->target->setProgramCounter(*command.fromAddress); } this->target->run(command.toAddress); this->lastTargetState = TargetState::RUNNING; } EventManager::triggerEvent(std::make_shared(false)); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleResetTarget(ResetTarget& command) { this->resetTarget(); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleReadTargetRegisters( ReadTargetRegisters& command ) { return std::make_unique( !command.descriptorIds.empty() ? this->target->readRegisters(command.descriptorIds) : Targets::TargetRegisters() ); } std::unique_ptr TargetControllerComponent::handleWriteTargetRegisters(WriteTargetRegisters& command) { if (!command.registers.empty()) { this->target->writeRegisters(command.registers); } auto registersWrittenEvent = std::make_shared(); registersWrittenEvent->registers = std::move(command.registers); EventManager::triggerEvent(registersWrittenEvent); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleReadTargetMemory(ReadTargetMemory& command) { const auto& targetDescriptor = this->getTargetDescriptor(); if ( command.memoryType == targetDescriptor.programMemoryType && this->environmentConfig.targetConfig.programMemoryCache ) { assert(this->programMemoryCache); if (!this->programMemoryCache->contains(command.startAddress, command.bytes)) { Logger::debug( "Program memory cache miss at 0x" + Services::StringService::toHex(command.startAddress) + ", " + std::to_string(command.bytes) + " bytes" ); /* * TODO: We're currently ignoring command.excludedAddressRanges when populating the program * memory cache. This isn't a big deal, so I'll sort it later. */ this->programMemoryCache->insert( command.startAddress, this->target->readMemory( command.memoryType, command.startAddress, std::max( command.bytes, targetDescriptor.memoryDescriptorsByType.at(command.memoryType).pageSize.value_or(0) ) ) ); } return std::make_unique( this->programMemoryCache->fetch(command.startAddress, command.bytes) ); } return std::make_unique( command.bytes > 0 ? this->target->readMemory( command.memoryType, command.startAddress, command.bytes, command.excludedAddressRanges ) : Targets::TargetMemoryBuffer() ); } std::unique_ptr TargetControllerComponent::handleWriteTargetMemory(WriteTargetMemory& command) { const auto& buffer = command.buffer; const auto bufferSize = command.buffer.size(); const auto bufferStartAddress = command.startAddress; const auto& targetDescriptor = this->getTargetDescriptor(); if (command.memoryType == targetDescriptor.programMemoryType && !this->target->programmingModeEnabled()) { throw Exception("Cannot write to program memory - programming mode not enabled."); } this->target->writeMemory(command.memoryType, bufferStartAddress, buffer); if ( command.memoryType == targetDescriptor.programMemoryType && this->environmentConfig.targetConfig.programMemoryCache ) { this->programMemoryCache->insert(bufferStartAddress, buffer); } EventManager::triggerEvent( std::make_shared(command.memoryType, bufferStartAddress, bufferSize) ); if ( EventManager::isEventTypeListenedFor(Events::RegistersWrittenToTarget::type) && command.memoryType == targetDescriptor.programMemoryType && this->registerDescriptorsByMemoryType.contains(command.memoryType) ) { /* * The memory type we just wrote to contains some number of registers - if we've written to any address * that is known to store the value of a register, trigger a RegistersWrittenToTarget event */ const auto bufferEndAddress = static_cast(bufferStartAddress + (bufferSize - 1)); auto registerDescriptors = this->getRegisterDescriptorsWithinAddressRange( bufferStartAddress, bufferEndAddress, command.memoryType ); if (!registerDescriptors.empty()) { auto registersWrittenEvent = std::make_shared(); for (const auto& registerDescriptor : registerDescriptors) { const auto registerSize = registerDescriptor.size; const auto registerStartAddress = registerDescriptor.startAddress.value(); const auto registerEndAddress = registerStartAddress + (registerSize - 1); if (registerStartAddress < bufferStartAddress || registerEndAddress > bufferEndAddress) { continue; } const auto bufferBeginIt = buffer.begin() + (registerStartAddress - bufferStartAddress); registersWrittenEvent->registers.emplace_back(TargetRegister( registerDescriptor.id, TargetMemoryBuffer(bufferBeginIt, bufferBeginIt + registerSize) )); } EventManager::triggerEvent(registersWrittenEvent); } } return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleEraseTargetMemory(EraseTargetMemory& command) { if (command.memoryType == this->getTargetDescriptor().programMemoryType) { if (!this->target->programmingModeEnabled()) { throw Exception("Cannot erase program memory - programming mode not enabled."); } if (this->environmentConfig.targetConfig.programMemoryCache) { assert(this->programMemoryCache); Logger::debug("Clearing program memory cache"); this->programMemoryCache->clear(); } } this->target->eraseMemory(command.memoryType); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleStepTargetExecution(StepTargetExecution& command) { if (command.fromProgramCounter.has_value()) { this->target->setProgramCounter(command.fromProgramCounter.value()); } this->target->step(); this->lastTargetState = TargetState::RUNNING; EventManager::triggerEvent(std::make_shared(true)); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleSetBreakpoint(SetBreakpoint& command) { using Targets::TargetBreakpoint; using Services::StringService; auto breakpoint = TargetBreakpoint(command.address, TargetBreakpoint::Type::SOFTWARE); const auto& targetBreakpointResources = this->getTargetDescriptor().breakpointResources; if ( command.preferredType == Targets::TargetBreakpoint::Type::HARDWARE && this->environmentConfig.targetConfig.hardwareBreakpoints ) { static auto exhaustedResourcesWarning = false; if ( !targetBreakpointResources.maximumHardwareBreakpoints.has_value() || this->hardwareBreakpointsByAddress.size() < *(targetBreakpointResources.maximumHardwareBreakpoints) ) { exhaustedResourcesWarning = true; Logger::debug( "Installing hardware breakpoint at byte address 0x" + StringService::toHex(command.address) ); this->target->setHardwareBreakpoint(command.address); this->hardwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint)); breakpoint.type = TargetBreakpoint::Type::HARDWARE; return std::make_unique(breakpoint); } if (exhaustedResourcesWarning) { exhaustedResourcesWarning = false; Logger::warning( "Hardware breakpoint resources have been exhausted. Falling back to software breakpoints" ); } } Logger::debug( "Installing software breakpoint at byte address 0x" + StringService::toHex(command.address) ); this->target->setSoftwareBreakpoint(command.address); this->softwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint)); return std::make_unique(breakpoint); } std::unique_ptr TargetControllerComponent::handleRemoveBreakpoint(RemoveBreakpoint& command) { using Services::StringService; if (command.breakpoint.type == Targets::TargetBreakpoint::Type::HARDWARE) { assert(this->environmentConfig.targetConfig.hardwareBreakpoints); Logger::debug( "Removing hardware breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address) ); this->target->removeHardwareBreakpoint(command.breakpoint.address); this->hardwareBreakpointsByAddress.erase(command.breakpoint.address); } else { Logger::debug( "Removing software breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address) ); this->target->removeSoftwareBreakpoint(command.breakpoint.address); this->softwareBreakpointsByAddress.erase(command.breakpoint.address); } return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleSetProgramCounter(SetTargetProgramCounter& command) { this->target->setProgramCounter(command.address); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleGetTargetPinStates(GetTargetPinStates& command) { return std::make_unique(this->target->getPinStates(command.variantId)); } std::unique_ptr TargetControllerComponent::handleSetTargetPinState(SetTargetPinState& command) { this->target->setPinState(command.pinDescriptor, command.pinState); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleGetTargetStackPointer( GetTargetStackPointer& command ) { return std::make_unique(this->target->getStackPointer()); } std::unique_ptr TargetControllerComponent::handleGetTargetProgramCounter( GetTargetProgramCounter& command ) { return std::make_unique(this->target->getProgramCounter()); } std::unique_ptr TargetControllerComponent::handleEnableProgrammingMode(EnableProgrammingMode& command) { if (!this->target->programmingModeEnabled()) { this->enableProgrammingMode(); } return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleDisableProgrammingMode(DisableProgrammingMode& command) { if (this->target->programmingModeEnabled()) { this->disableProgrammingMode(); } return std::make_unique(); } }