/*! * * Copyright (c) 2023 Diality Inc. - All Rights Reserved. * * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file UpdateProtocol.h * \author (last) Phil Braica * \date (last) 23-Jan-2023 * \author (original) Phil Braica * \date (original) 23-Jan-2023 * */ /*! * * This is shared between the 'C' and 'C++' code modules * and defines the principal messaging between the UI and FW * code bases for software update. */ #ifndef UPDATE_PROTOCOL_H_ #define UPDATE_PROTOCOL_H_ #include "HalStdTypes.h" #ifdef __cplusplus extern "C" { #endif /*! Optimize for speed. */ #define MAX_TRANSFER_SIZE (128) ///< Payload size, must be big enough #define MAX_DATA_TRANSFERS (4) ///< Number of active data cmd/response slots at once, per target. #define NUM_CAN_TARGETS (4) ///< There are 4 CAN targets off of the UI. #define CAN_PAYLOAD_SIZE (8) ///< 8 bytes per native CAN frame. #define KBPS_WIRE (250) ///< The line speed in bps, 250 = 250 kbps, max 1250 kbps. /*! *
 
 * Bytes per index increment. At 64, 4,194,304 bytes addressable.
 * 1. We need about half that as a minimum.
 * 2. MAX_TRANSFER_SIZE must be greater than BYTES_PER_INDEX, and
 * 3. MAX_TRANSFER_SIZE / BYTES_PER_INDEX must be an integer.
 * 
*/ #define BYTES_PER_INDEX (64) /*! * The CAN bus IDs used for update. * * So that theoretically an image could do both a FW and FPGA * stream at the same time without the header data transmitted * on each 8 byte transfer... The only way to 100% be sure the * streams don't get confused by the "mailbox" system used by * TI style CAN bus HW is to make them distinct command IDs. * * The app msgs are < 0x8000. We start there. */ typedef enum SwUpdateCanMsgIds { CommandId = 0x801, ///< SwUpdateCommand ID. DataBufferId_HD_FW = 0x802, ///< SwUpdateDataBuffer ID for FW. DataBufferId_HD_FPGA = 0x803, ///< SwUpdateDataBuffer ID for FPGA. DataBufferId_DG_FW = 0x804, ///< SwUpdateDataBuffer ID for FW. DataBufferId_DG_FPGA = 0x805, ///< SwUpdateDataBuffer ID for FPGA. ResponseId = 0x806, ///< SwUpdateResponse ID. VerifyId = 0x807, ///< SwUpdateResponse ID. NumMsgIds ///< Always last. } SwUpdateCanMsgIds; /*! * The target for the update. */ typedef enum SwUpdateTargetEnum { // Start transfer. HD = 0, HDFPGA, DG, DGFPGA, // Only used within UI, never in messages. UI, LINUX } SwUpdateTargetEnum; /*! Normal Protocol: UI->FW: SwUpdateCommand[cmd=start] FW->UI: Secure Ack/Nack loop UI->FW: Data FW->UI: Secure Ack/Nack <- Also flow control. end UI->FW: Verify FW->UI: SwUpdateVerifyResponse UI->FW: Version FW->UI: SwUpdateVerifyResponse UI->FW: Verified UI thinks things are good. FW->UI: ACK/NACK FW has agreed or not and set the EPROM memory that the image is good or not. UI->FW: RunApp Launch the main app. FW->UI: Secure Ack/Nack If streaming encounters problems we use a command with Resync so that the FW will dump it's indexes and be ready for new data. */ typedef enum SwUpdateCmdEnum { Start = 0, Abort, RunApp, Verify, Version, Verified, Resync } SwUpdateCmdEnum; /*! * \brief From a command byte extract the target. * * \param cmd The command byte. * * \return The target of the command. */ extern SwUpdateTargetEnum SwUpdate_getTarget(uint8 cmd); /*! * \brief From a command byte extract the command. * * \param cmd The command byte. * * \return The kind of command. */ extern SwUpdateCmdEnum SwUpdate_getCmd(uint8 cmd); /*! * \brief From target and command kind compose the command byte. * * \param target The processor target. * \param cmd The command kind. * * \return The command byte. */ extern uint8 SwUpdate_FormCommand(SwUpdateTargetEnum target, SwUpdateCmdEnum cmd); /*! * \brief This is a secure response. 1 atomic CAN bus transaction. * * \param id Tell the FW one command discussion is distinct from another. * \param cmd A variety of commands. * \param rand Whiten the data for the security token. * \param token 32 bit security token. */ struct SwUpdateCommand { uint8 id; ///< Command/response slot ID. uint8 cmd; ///< See above. uint16 rand; ///< Whitens the data. uint32 token; ///< Security token. }; /*! * \brief Software update data buffer. * * This maps to the bus data. The protocol allows for a * sender to have MAX_DATA_TRANSFERS active IDs to track * responses. This ensures a conversation with the FW is * stateless, and can be abandoned by the UI to restart / * recover due to CAN bus collisions or 3rd party bus use * issues. * * Things are packed in... */ struct SwUpdateDataBuffer { // First word. uint8 id; ///< Command/response slot ID. uint8 kind; ///< 0=HD, 1=HDFPGA, 2=DG, 3=DGFPGA uint16 index; ///< Storage index, 1 increment is BYTES_PER_INDEX bytes, max is flag for signature. // Second word uint32 token; ///< Security token. // Additional transfers. uint8 buffer[MAX_TRANSFER_SIZE]; ///< Buffer. }; /*! * \brief This is a secure response. 1 atomic CAN bus transaction. */ struct SwUpdateResponse { uint8 id; ///< Command/response slot ID. uint8 ackNack; ///< 1=Ack, 0=Nack. uint16 rand; ///< Whitens the data. uint32 token; ///< Security token. }; /*! * \brief This is a secure response. 1 atomic CAN bus transaction. * * These are tokens, there are two uses of the message, with two different * tokens to verify. */ struct SwUpdateVerifyResponse { uint32 data; ///< Either CRC or version ID. uint32 token; ///< Security token. }; /*! * \brief Create and write into the buffer the security token. * * \param pData Packet to verify. * \param size Size in bytes. */ extern void SwUpdate_createSecurity( uint8* pData, uint32 size); /*! * \brief All packets have the second 32 bit word as security. * * \details If this fails, this function automatically invokes * security features to take note of a potential threat. * * \param target Who is receiving this packet. * \param pData Packet to verify. * \param size Size in bytes. * * \return True if verified. */ extern bool SwUpdate_verifySecurity(const uint8* pData, uint32 size); /*! * \brief Create and write into the buffer the security token for a data transfer * * This is forming a response to the UI, always target of UI. * * \param pResponse Packet responding pointer. * \param pData Data we received pointer. */ extern void SwUpdate_createSecurityForData(struct SwUpdateResponse* pResponse, uint8 * pData); /*! * \brief Encode / obfuscate the data. * * \param pData Data to encode. */ extern void SwUpdate_encodeData(struct SwUpdateDataBuffer* pData); /*! * \brief Decode / de-obfuscate the data. * * \param pData Data to decode. */ extern void SwUpdate_decodeData(struct SwUpdateDataBuffer* pData); /*! * \brief After getting a secure response for data UI sent, check the returned security token. * * This checks the response to a data message. * * \param pResponse Packet responding pointer. * \param pData Data we had sent pointer. */ extern BOOL SwUpdate_checkSecurityForData(const uint8 * pResponse, const uint8 * pData); /*! * \brief Go through the data and stream it into the verification code * * \param target Which target is this for? */ extern void SwUpdate_resetVerificationData(SwUpdateTargetEnum target); /*! * \brief Go through the data and stream it into the verification code * * This currently streams the data in, allowing for retries of the last item, * by using the index value to roll back and re-apply if needed. * * \param index Index of the data. * \param pData Data pointer. * \param size Size of the data. * \param target Which target is this for? */ extern void SwUpdate_updateVerificationData( uint16 index, uint8* pData, uint32 size, SwUpdateTargetEnum target); /*! * \brief Compute / get the data for the image. * * \param asVerify If true, verify token else version token. * \param asFwImage IF true FW image else FPGA image. * * \return Version/verification token. */ extern uint32 SwUpdate_computeVerificationData(bool asVerify, SwUpdateTargetEnum target); /*! * \brief Is the cryptography signature valid? * * \param pSignature Pointer to MAX_TRANSFER_SIZE signature. * \param target SwUpdateTargetEnum, 0=HD, 1=HDFPGA, 2=DG, 3=DGFPGA. * * \return 1 if passed else 0. */ extern uint32 SwUpdate_isCryptoSignedOk(uint8* pSignature, uint8 target); ///////////////////// // ONLY compile this function on the UI SOM. ///////////////////// #ifdef COMPILING_ON_UI /*! * \brief Make a signature. * * \param crc1 CRC1 to do. * \param crc2 CRC2 to do. * \param pSignature Pointer to MAX_TRANSFER_SIZE signature. * * \return 1 if passed else 0. */ void SwUpdate_makeSignature(uint32 crc1, uint32 crc2, uint8 *pSignature); #endif #ifdef __cplusplus } #endif #endif // UPDATE_PROTOCOL_H_