diff --git a/common/config/commonSubsystemIds.h b/common/config/commonSubsystemIds.h index 7c2cb605..ab87d119 100644 --- a/common/config/commonSubsystemIds.h +++ b/common/config/commonSubsystemIds.h @@ -17,6 +17,7 @@ enum: uint8_t { STR_HANDLER = 114, PLOC_SUPERVISOR_HANDLER = 115, FILE_SYSTEM = 116, + PLOC_UPDATER = 117, COMMON_SUBSYSTEM_ID_END }; } diff --git a/mission/devices/PlocSupervisorHandler.cpp b/mission/devices/PlocSupervisorHandler.cpp index 9c4dc73e..409a48c5 100644 --- a/mission/devices/PlocSupervisorHandler.cpp +++ b/mission/devices/PlocSupervisorHandler.cpp @@ -241,6 +241,12 @@ ReturnValue_t PlocSupervisorHandler::buildCommandFromCommand( result = RETURN_OK; break; } + case(PLOC_SPV::UPDATE_AVAILABLE): + case(PLOC_SPV::UPDATE_AVAILABLE): + case(PLOC_SPV::UPDATE_AVAILABLE): + // Simply forward data from PLOC Updater to supervisor + std::memcpy(rawPacket, commandData, commandDataLen); + break; default: sif::debug << "PlocSupervisorHandler::buildCommandFromCommand: Command not implemented" << std::endl; diff --git a/mission/devices/PlocSupervisorHandler.h b/mission/devices/PlocSupervisorHandler.h index df4645b2..c98cb37c 100644 --- a/mission/devices/PlocSupervisorHandler.h +++ b/mission/devices/PlocSupervisorHandler.h @@ -82,7 +82,7 @@ private: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_SUPERVISOR_HANDLER; - //! [EXPORT] : [COMMENT] PLOC supervrisor crc failure in telemetry packet + //! [EXPORT] : [COMMENT] PLOC supervisor crc failure in telemetry packet static const Event SUPV_MEMORY_READ_RPT_CRC_FAILURE = MAKE_EVENT(1, severity::LOW); //! [EXPORT] : [COMMENT] PLOC supervisor received acknowledgment failure report static const Event SUPV_ACK_FAILURE = MAKE_EVENT(2, severity::LOW); diff --git a/mission/devices/PlocUpdater.cpp b/mission/devices/PlocUpdater.cpp index c7310298..9639ff87 100644 --- a/mission/devices/PlocUpdater.cpp +++ b/mission/devices/PlocUpdater.cpp @@ -1,4 +1,5 @@ #include +#include PlocUpdater::PlocUpdater() : commandActionHelper(this) { commandQueue = QueueFactory::instance()->createMessageQueue(QUEUE_SIZE); @@ -7,11 +8,16 @@ PlocUpdater::PlocUpdater() : commandActionHelper(this) { PlocUpdater::~PlocUpdater() { } +ReturnValue_t SystemObject::initialize() { + sdcMan = SdCardManager::instance(); + return HasReturnvaluesIF::RETURN_OK; +} + ReturnValue_t PlocUpdater::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size) { ReturnValue_t result = RETURN_FAILED; - if (state == BUSY) { + if (state != IDLE) { return IS_BUSY; } @@ -21,20 +27,27 @@ ReturnValue_t PlocUpdater::executeAction(ActionId_t actionId, switch (actionId) { case UPDATE_NVM0_A: - result = prepareNvm0AUpdate(std::string(data, size)); + updateImage = Image::A; + updateMemory = Memory::NVM0; break; case UPDATE_NVM0_B: - result = prepareNvm0BUpdate(); + updateImage = Image::B; + updateMemory = Memory::NVM0; break; case UPDATE_NVM1_A: - result = prepareNvm1AUpdate(); + updateImage = Image::A; + updateMemory = Memory::NVM1; break; case UPDATE_NVM1_B: - result = prepareNvm1BUpdate(); + updateImage = Image::B; + updateMemory = Memory::NVM1; break; default: return INVALID_ACTION_ID; } + + result = getImageLocation(data, size); + return result; } @@ -42,13 +55,265 @@ MessageQueueId_t PlocUpdater::getCommandQueue() const { return commandQueue.getId(); } -ReturnValue_t PlocUpdater::checkPath(size_t size) { +void PlocUpdater::readCommandQueue() { + CommandMessage message; + ReturnValue_t result = commandQueue->receiveMessage(&message); + if (result != RETURN_OK) { + return; + } + result = actionHelper.handleActionMessage(&message); + if (result == HasReturnvaluesIF::RETURN_OK) { + continue; + } + + result = commandActionHelper.handleReply(&message); + if (result == HasReturnvaluesIF::RETURN_OK) { + continue; + } + result = parameterHelper.handleParameterMessage(&message); + if (result == HasReturnvaluesIF::RETURN_OK) { + continue; + } + message.setToUnknownCommand(); + commandQueue.reply(&message); +} + +void PlocUpdater::doStateMachine() { + switch (state) { + case State::IDLE: + break; + case State::UPDATE_AVAILABLE: + commandUpdateAvailable(); + break; + case State::UPDATE_TRANSFER: + commandUpdatePacket(); + break; + case State::UPDATE_VERIFY: + break; + default: + sif::debug << "PlocUpdater::doStateMachine: Invalid state" << std::endl; + break; + } +} + +ReturnValue_t PlocUpdater::checkNameLength(size_t size) { if (size > MAX_PLOC_UPDATE_PATH) { - return PATH_TOO_LONG; + return NAME_TOO_LONG; } return RETURN_OK; } -PlocUpdater::prepare -getCurrentMountPrefix +ReturnValue_t PlocUpdater::getImageLocation(const uint8_t* data, size_t size) { + ReturnValue_t result = checkNameLength(size); + if (result != RETURN_OK) { + return result; + } + // Check if file is stored on SD card and if associated SD card is mounted + if (std::string(data, SD_PREFIX_LENGTH) == std::string(SdCardManager::SD_0_MOUNT_POINT)) { + if (!isSdCardMounted(sd::SLOT_0)) { + sif::warning << "PlocUpdater::prepareNvm0AUpdate: SD card 0 not mounted" << std::endl; + return SD_NOT_MOUNTED; + } + } + else if (std::string(data, SD_PREFIX_LENGTH) == std::string(SdCardManager::SD_1_MOUNT_POINT)) { + if (!isSdCardMounted(sd::SLOT_0)) { + sif::warning << "PlocUpdater::prepareNvm0AUpdate: SD card 1 not mounted" << std::endl; + return SD_NOT_MOUNTED; + } + } + else { + //update image not stored on SD card + } + + updateFile = std::string(reinterpret_cast(data), size); + + // Check if file exists + if(not std::filesystem::exists(updateFile)) { + return FILE_NOT_EXISTS; + } + return RETURN_OK; +} + +bool PlocUpdater::isSdCardMounted(sd::SdCard sdCard) { + SdStatusPair active; + ReturnValue_t result = sdcMan->getSdCardActiveStatus(&active); + if (result != RETURN_OK) { + sif::debug << "PlocUpdater::isSdCardMounted: Failed to get SD card active state"; + return false; + } + if (sdCard == sd::SLOT_0) { + if (active.first == sd::MOUNTED) { + return true; + } + else { + return false; + } + } + else if (sdCard == sd::SLOT_1) { + if (active.second == sd::MOUNTED) { + return true; + } + else { + return false; + } + } + else { + sif::debug << "PlocUpdater::isSdCardMounted: Unknown SD card specified" << std::endl; + } + return false; +} + +void PlocUpdater::stepSuccessfulReceived(ActionId_t actionId, + uint8_t step) { +} + +void PlocUpdater::stepFailedReceived(ActionId_t actionId, uint8_t step, + ReturnValue_t returnCode) { +} + +void PlocUpdater::completionSuccessfulReceived(ActionId_t actionId) { + switch (actionId) { + case UPDATE_AVAILABLE: + state = State::UPDATE_TRANSFER; + break; + case UPDATE_IMAGE_DATA: + if (remainingPackets == 0) { + packetsSent = 0; // Reset packets sent variable for next update sequence + state = State::UPDATE_VERIFY; + } + break; + case UPDATE_VERIFY: + triggerEvent(UPDATE_FINISHED); + break; + default: + sif::debug << "PlocUpdater::completionSuccessfulReceived: Action message with has unknown " + << "action id" << std::endl; + break; + } +} + +void PlocUpdater::completionFailedReceived(ActionId_t actionId, + ReturnValue_t returnCode) { + state = State::IDLE; + switch(state) { + case(State::UPDATE_AVAILABLE): { + triggerEvent(UPDATE_AVAILABLE_FAILED); + break; + } + case(State::UPDATE_TRANSFER): { + triggerEvent(UPDATE_TRANSFER_FAILED); + break; + } + case(State::UPDATE_VERIFY): { + triggerEvent(UPDATE_VERIFY_FAILED); + break; + } + default: + sif::debug << "PlocUpdater::completionFailedReceived: Action message with has unknown " + << "action id" << std::endl; + break; + } +} + +void PlocUpdater::commandUpdateAvailable() { + ReturnValue_t result = RETURN_OK; + + if (not std::filesystem::exists(updateFile)) { + triggerEvent(UPDATE_FILE_NOT_EXISTS, state); + state = State::IDLE; + return; + } + + std::ifstream file(updateFile, std::ifstream::binary); + file.seekg(0, file.end); + imageSize = static_cast(file.tellg()); + file.close(); + + numOfUpdatePackets = imageSize / MAX_SP_DATA ; + if (! imageSize % MAX_SP_DATA) { + numOfUpdatePackets++; + } + + remainingPackets = numOfUpdatePackets; + packetsSent = 0; + + uint32_t imageCrc = makeCrc(); + + PLOC_SPV::UpdateInfo packet(PLOC_SPV::APID_UPDATE_AVAILABLE, static_cast(updateMemory), + static_cast(updatePartition), imageSize, imageCrc, numOfUpdatePackets); + + result = commandActionHelper.commandAction(objects::PLOC_SUPERVISOR_HANDLER, UPDATE_AVAILABLE, + packet.getFullSize()); + if (result != RETURN_OK) { + sif::warning << "PlocUpdater::commandUpdateAvailable: Failed to send update available" + << " packet to supervisor handler" << std::endl; + triggerEvent(ACTION_COMMANDING_FAILED, result, UPDATE_AVAILABLE); + return; + } + return; +} + +void PlocUpdater::commandUpdatePacket() { + uint16_t payloadLength = 0; + + if (not std::filesystem::exists(updateFile)) { + triggerEvent(UPDATE_FILE_NOT_EXISTS, state, packetsSent); + state = State::IDLE; + return; + } + + std::ifstream file(updateFile, std::ifstream::binary); + file.seekg(packetsSent * MAX_SP_DATA, file.beg); + + if (remainingPackets == 1) { + payloadLength = static_cast(file.tellg()); + } + else { + payloadLength = MAX_SP_DATA; + } + + PLOC_SPV::UpdatePacket packet(payloadLength); + file.read(static_cast(packet.getDataFieldPointer()), payloadLength); + file.close(); + packet.updateCrc(); + + commandActionHelper.commandAction(objects::PLOC_SUPERVISOR_HANDLER, UPDATE_IMAGE_DATA, + packet.getDataFieldPointer(), packet.getFullSize()); + remainingPackets--; +} + +void PlocUpdater::commandUpdateVerify() { + ReturnValue_t result = RETURN_OK; + + if (not std::filesystem::exists(updateFile)) { + triggerEvent(UPDATE_FILE_NOT_EXISTS, state); + state = State::IDLE; + return; + } + + remainingPackets = imageSize / MAX_SP_DATA ; + if (! imageSize % MAX_SP_DATA) { + remainingPackets++; + } + + uint32_t imageCrc = makeCrc(); + + PLOC_SPV::UpdateInfo packet(PLOC_SPV::APID_UPDATE_VERIFY, static_cast(updateMemory), + static_cast(updatePartition), imageSize, imageCrc, numOfUpdatePackets); + + result = commandActionHelper.commandAction(objects::PLOC_SUPERVISOR_HANDLER, UPDATE_AVAILABLE, + packet.getFullSize()); + if (result != RETURN_OK) { + sif::warning << "PlocUpdater::commandUpdateAvailable: Failed to send update available" + << " packet to supervisor handler" << std::endl; + triggerEvent(ACTION_COMMANDING_FAILED, result, UPDATE_AVAILABLE); + return; + } + return; +} + +ReturnValue_t PlocUpdater::makeCrc() { + //TODO: Waiting on input from TAS about the CRC to use + return 0; +} diff --git a/mission/devices/PlocUpdater.h b/mission/devices/PlocUpdater.h index dd37898e..42c855f3 100644 --- a/mission/devices/PlocUpdater.h +++ b/mission/devices/PlocUpdater.h @@ -3,13 +3,16 @@ #include "fsfw/action/CommandActionHelper.h" #include "fsfw/returnvalues/HasReturnValuesIF.h" +#include "bsp_q7s/memory/SdCardManager.h" +#include "linux/fsfwconfig/objects/systemObjectList.h" +#include "fsfw/tmtcpacket/SpacePacket.h" /** * @brief An object of this class can be used to perform the software updates of the PLOC. The * software update will be fetched via file system messages from the SD card handler, * split into multiple space packets and sent to the PlocSupervisorHandler. * - * @details The MPSoC has to boot memories (NVM0 and NVM1) where each stores two images (Partition A + * @details The MPSoC has two boot memories (NVM0 and NVM1) where each stores two images (Partition A * and Partition B) * * @author J. Meier @@ -17,7 +20,8 @@ class PlocUpdater : public SystemObject, public HasActionsIF, public ExecutableObjectIF, - public HasReturnvaluesIF { + public HasReturnvaluesIF, + public CommandsActionsIF { public: static const ActionId_t UPDATE_NVM0_A = 0; @@ -31,6 +35,12 @@ public: ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size); MessageQueueId_t getCommandQueue() const; + ReturnValue_t initialize() override; + void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) override; + void stepFailedReceived(ActionId_t actionId, uint8_t step, ReturnValue_t returnCode) override; + void dataReceived(ActionId_t actionId, const uint8_t* data, uint32_t size) override; + void completionSuccessfulReceived(ActionId_t actionId) override; + void completionFailedReceived(ActionId_t actionId, ReturnValue_t returnCode) override; private: @@ -39,14 +49,88 @@ private: //! [EXPORT] : [COMMENT] Updater is already performing an update static const ReturnValue_t UPDATER_BUSY = MAKE_RETURN_CODE(0xA0); //! [EXPORT] : [COMMENT] Received update command with invalid path string (too long). - static const ReturnValue_t PATH_TOO_LONG = MAKE_RETURN_CODE(0xA0); + static const ReturnValue_t NAME_TOO_LONG = MAKE_RETURN_CODE(0xA1); + //! [EXPORT] : [COMMENT] Received command to initiate update but SD card with update image not mounted. + static const ReturnValue_t SD_NOT_MOUNTED = MAKE_RETURN_CODE(0xA2); + //! [EXPORT] : [COMMENT] Update file received with update command does not exist. + static const ReturnValue_t FILE_NOT_EXISTS = MAKE_RETURN_CODE(0xA3); + + static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_UPDATER; + + //! [EXPORT] : [COMMENT] Try to read update file but the file does not exist. + //! P1: Indicates in which state the file read fails + //! P2: During the update transfer the second parameter gives information about the number of already sent packets + static const Event UPDATE_FILE_NOT_EXISTS = MAKE_EVENT(0, severity::LOW); + //! [EXPORT] : [COMMENT] Failed to send command to supervisor handler + //! P1: Return value of CommandActionHelper::commandAction + //! P2: Action ID of command to send + static const Event ACTION_COMMANDING_FAILED = MAKE_EVENT(1, severity::LOW); + //! [EXPORT] : [COMMENT] Supervisor handler replied action message indicating a command execution failure of the update available command + static const Event UPDATE_AVAILABLE_FAILED = MAKE_EVENT(2, severity::LOW); + //! [EXPORT] : [COMMENT] Supervisor handler failed to transfer an update space packet. + //! P1: Parameter holds the number of update packets already sent. + static const Event UPDATE_TRANSFER_FAILED = MAKE_EVENT(3, severity::LOW); + //! [EXPORT] : [COMMENT] Supervisor failed to execute the update verify command. + static const Event UPDATE_VERIFY_FAILED = MAKE_EVENT(4, severity::LOW); + //! [EXPORT] : [COMMENT] MPSoC update successful completed + static const Event UPDATE_FINISHED = MAKE_EVENT(5, SEVERITY::INFO); static const uint32_t QUEUE_SIZE = config::PLOC_UPDATER_QUEUE_SIZE; static const size_t MAX_PLOC_UPDATE_PATH = 20; + static const size_t SD_PREFIX_LENGTH = 8; + // Maximum size of update payload data per space packet (max size of space packet is 1024 bytes) + static const size_t MAX_SP_DATA = 1016; + + static const ActionId_t UPDATE_AVAILABLE = 12; + static const ActionId_t UPDATE_IMAGE_DATA = 39; + static const ActionId_t UPDATE_VERIFY = 42; + + SdCardManager* sdcMan = nullptr; CommandActionHelper commandActionHelper; - ReturnValue_t checkPath(size_t size); + enum class State: uint8_t { + IDLE, + UPDATE_AVAILABLE, + UPDATE_TRANSFER, + UPDATE_VERIFY + }; + + State state = State::IDLE; + + enum class Memory: uint8_t { + NVM0, + NVM1 + }; + + Memory updateMemory = Memory::NVM0; + + enum class Partition: uint8_t { + A, + B + }; + + Partition updatePartition = Partition::A; + + State state = State::IDLE; + + uint32_t packetsSent = 0; + uint32_t remainingPackets = 0; + // Number of packets required to transfer the update image + uint32_t numOfUpdatePackets = 0; + + std::string updateFile; + uint32_t imageSize = 0; + uint32_t imageCrc = 0; + + ReturnValue_t checkNameLength(size_t size); + + /** + * @brief Checks whether the SD card to read from is mounted or not. + */ + bool isSdCardMounted(sd::SdCard sdCard); + + ReturnValue_t makeCrc(); }; #endif /* MISSION_DEVICES_PLOCUPDATER_H_ */ diff --git a/mission/devices/devicedefinitions/PlocSupervisorDefinitions.h b/mission/devices/devicedefinitions/PlocSupervisorDefinitions.h index cc7be993..32e682a1 100644 --- a/mission/devices/devicedefinitions/PlocSupervisorDefinitions.h +++ b/mission/devices/devicedefinitions/PlocSupervisorDefinitions.h @@ -52,6 +52,7 @@ static const DeviceCommandId_t REQUEST_LOGGING_DATA = 38; static const DeviceCommandId_t UPDATE_IMAGE_DATA = 39; static const DeviceCommandId_t FACTORY_RESET_CLEAR_MIRROR = 40; static const DeviceCommandId_t FACTORY_RESET_CLEAR_CIRCULAR = 41; +static const DeviceCommandId_t UPDATE_VERIFY = 42; /** Reply IDs */ static const DeviceCommandId_t ACK_REPORT = 50; @@ -1406,6 +1407,119 @@ private: } }; +class SupvTcSpacePacket : public SpacePacket { +public: + SupvTcSpacePacket(size_t payloadDataLen, uint16_t apid) : + SpacePacket(payloadLen + 1, true, apid, + DEFAULT_SEQUENCE_COUNT), payloadDataLen(payloadDataLen) { + initPacket(); + makeCrc(); + } + +protected: + void makeCrc() { + serializedSize = 0; + uint16_t crc = CRC::crc16ccitt(this->localData.byteStream, + sizeof(CCSDSPrimaryHeader) + payloadDataLen); + uint8_t* crcPos = this->localData.fields.buffer + payloadDataLen; + SerializeAdapter::serialize(&crc, &crcPos, &serializedSize, sizeof(crc), + SerializeIF::Endianness::BIG); + } + +private: + // The sequence count of most of the TC packets for the supervisor is 1. + static const uint16_t DEFAULT_SEQUENCE_COUNT = 1; + + // The size of the payload data (data field without crc size) + size_t payloadDataLen = 0; + + void virtual initPacket() { + // Perform stuff to fill packet data field + } +}; + +/** + * @brief This class packages the update available space packet. This is the first packet sent + * to the supervisor in an update procedure. + */ +class UpdateInfo: public SupvTcSpacePacket { +public: + + /** + * @brief Constructor + * + * @param apid Packet can be used to generate the update available and the update verify + * packet. Thus the APID must be specified here. + * @param memory The memory to apply the update (NVM0 - 0, NVM1 - 1) + * @param partition The partition to update (A - 0, B - 1) + * @param imageSize The size of the update image + * param numPackets The number of space packets required to transfer all data. + */ + UpdateInfo(uint16_t apid, uint8_t memory, uint8_t partition, uint32_t imageSize, + uint32_t imageCrc, uint32_t numPackets) : + SupvTcSpacePacket(PAYLOAD_LENGTH, apid), memory(memory), partition(partition), imageSize( + imageSize), numPackets(numPackets) { + initPacket(); + } + +private: + + static const uint16_t PAYLOAD_LENGTH = 14; // length without CRC field + + uint8_t memory = 0; + uint8_t partition = 0; + uint32_t imageSize = 0; + uint32_t imageCrc = 0; + uint32_t numPackets = 0; + + void initPacket() { + size_t serializedSize = 0; + uint8_t* data_field_ptr = this->localData.fields.buffer; + SerializeAdapter::serialize(memory, &data_field_ptr, &serializedSize, + sizeof(memory), SerializeIF::Endianness::BIG); + serializedSize = 0; + SerializeAdapter::serialize(&partition, &data_field_ptr, &serializedSize, + sizeof(partition), SerializeIF::Endianness::BIG); + serializedSize = 0; + SerializeAdapter::serialize(&imageSize, &data_field_ptr, &serializedSize, + sizeof(imageSize), SerializeIF::Endianness::BIG); + serializedSize = 0; + SerializeAdapter::serialize(&imageCrc, &data_field_ptr, &serializedSize, + sizeof(imageCrc), SerializeIF::Endianness::BIG); + serializedSize = 0; + SerializeAdapter::serialize(&numPackets, &data_field_ptr, &serializedSize, + sizeof(numPackets), SerializeIF::Endianness::BIG); + } +}; + +/** + * @brief This class packages the space packet transporting a part of an MPSoC update. + */ +class UpdatePacket: public SupvTcSpacePacket { +public: + + /** + * @brief Constructor + * + * @param payloadLength Update data length (data field length without CRC) + */ + UpdatePacket(uint16_t payloadLength) : + SupvTcSpacePacket(payloadLength, APID_UPDATE_IMAGE_DATA) { + initPacket(); + } + + /** + * @brief Returns the pointer to the beginning of the data field. + */ + uint8_t* getDataFieldPointer() { + return this->localData.fields.buffer; + } + + void updateCrc() { + this->makeCrc(); + } +}; + /** * @brief This dataset stores the boot status report of the supervisor. */