#include "bootload.h" #include "ymodem.h" #include "sys.h" #include "delay.h" #include "cmac.h" #define AES_CMAC_DIGEST_LENGTH 16 typedef void (*fnc_ptr)(void); // 用于跳转到应用程序的函数指针 static bootload_transmit_callback transmit_callback = NULL; static bootload_end_callback end_callback = NULL; uint8_t upgrade_key[] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥 static AES_CMAC_CTX upgrade_ctx; /**< AES CMAC context for upgrade process */ static uint8_t pre_upgrade_mic[AES_CMAC_DIGEST_LENGTH]; /**< MIC (Message Integrity Code) before upgrade */ static uint8_t upgrade_mic[AES_CMAC_DIGEST_LENGTH]; /**< MIC (Message Integrity Code) after upgrade */ static uint32_t flashdestination = BOOTLOAD_APP_BACKUP_ADDR_START; /**< Flash destination address for bootloading */ static uint32_t upgrade_size = 0; /**< Size of the upgrade */ static uint8_t read_cache[LL_FLASH_PAGE_SIZE]; /**< Read cache for flash pages */ static uint8_t data_src_from; /**< Data source for bootloading */ /** * @brief Perform bootload inspection. * * This function calls the `rym_process()` function to perform bootload inspection. */ void bootload_inspection(void) { rym_process(); } /** * @brief Checks if the bootload timeout has occurred. * * This function calls the `rym_timeout()` function to determine if the bootload timeout has occurred. * * @return TRUE if the bootload timeout has occurred, FALSE otherwise. */ BOOL bootload_timeout(void) { return rym_timeout(); } /** * @brief Handles the beginning of the bootloading process. * * This function is responsible for handling the initial steps of the bootloading process. * It checks if the length of the data to be bootloaded is greater than the end address of the application area. * If the length is greater, it returns an error code indicating that the bootloading cannot proceed. * Otherwise, it sets the flash destination address to the start address of the backup area, * erases the bank of the flash memory where the application is stored, * and initializes the AES-CMAC context for the upgrade process. * * @param p Pointer to the data to be bootloaded. * @param len Length of the data to be bootloaded. * @return Error code indicating the result of the operation. * - RYM_CODE_CAN: If the length of the data is greater than the end address of the application area. * - RYM_CODE_NONE: If the operation is successful. */ static rym_code_e on_begin(uint8_t *p, uint32_t len) { if (len > BOOTLOAD_APP_END_ADDRESS) { return RYM_CODE_CAN; } flashdestination = BOOTLOAD_APP_BACKUP_ADDR_START; LL_FLASH_Unlock(FLASH); LL_FLASH_EraseBank(LL_FLASH_BANK2); LL_FLASH_Lock(FLASH); AES_CMAC_Init(&upgrade_ctx); AES_CMAC_SetKey(&upgrade_ctx, upgrade_key); return RYM_CODE_NONE; } /** * @brief Handles the received data and programs it into the flash memory. * * This function unlocks the flash memory, programs the received data into the specified flash destination, * locks the flash memory again, updates the AES-CMAC context with the received data, and increments the flash destination. * * @param p Pointer to the data buffer. * @param len Length of the data buffer. * @return The result code indicating the success or failure of the operation. */ static rym_code_e on_data(uint8_t *p, uint32_t len) { LL_FLASH_Unlock(FLASH); LL_FLASH_Program(flashdestination, p, len); LL_FLASH_Lock(FLASH); AES_CMAC_Update(&upgrade_ctx, p, len); flashdestination += len; return RYM_CODE_NONE; } /** * @brief Calculate the MIC (Message Integrity Code) for the firmware upgrade. * * This function calculates the MIC using AES-CMAC algorithm for the firmware upgrade data. * It reads the firmware data from flash memory in pages and updates the MIC calculation. * Finally, it compares the calculated MIC with the pre-upgrade MIC to determine the success of the upgrade. * * @param p Pointer to the firmware data. * @param len Length of the firmware data. * @return The result code indicating the success or failure of the upgrade process. */ static rym_code_e on_end(uint8_t *p, uint32_t len) { AES_CMAC_Final(pre_upgrade_mic, &upgrade_ctx); upgrade_size = flashdestination - BOOTLOAD_APP_BACKUP_ADDR_START; AES_CMAC_Init(&upgrade_ctx); AES_CMAC_SetKey(&upgrade_ctx, upgrade_key); uint32_t start = BOOTLOAD_APP_BACKUP_ADDR_START; uint16_t num = (upgrade_size / LL_FLASH_PAGE_SIZE); uint16_t remain = (upgrade_size % LL_FLASH_PAGE_SIZE); // STM32L476RG的flash页大小为2K,先读取整数页,再读取余数 for (uint16_t i = 0; i < num; i++) { LL_FLASH_Read((start + i * (LL_FLASH_PAGE_SIZE)), read_cache, LL_FLASH_PAGE_SIZE); AES_CMAC_Update(&upgrade_ctx, read_cache, LL_FLASH_PAGE_SIZE); } if (remain) { osel_memset(read_cache, 0, LL_FLASH_PAGE_SIZE); LL_FLASH_Read((start + num * (LL_FLASH_PAGE_SIZE)), read_cache, remain); AES_CMAC_Update(&upgrade_ctx, read_cache, remain); } AES_CMAC_Final(upgrade_mic, &upgrade_ctx); // 比较mic,相同可以写入标志位告知应用程序升级成功 if (osel_memcmp(upgrade_mic, pre_upgrade_mic, AES_CMAC_DIGEST_LENGTH) == 0) { end_callback(TRUE); } else { end_callback(FALSE); } return RYM_CODE_NONE; } /** * @brief Handles the transmission of data. * * This function calls the transmit_callback function to transmit the data from the specified source. * * @param p Pointer to the data buffer. * @param len Length of the data buffer. * @return The return code indicating the status of the transmission. */ static rym_code_e on_transmit(uint8_t *p, uint32_t len) { transmit_callback(data_src_from, p, (uint16_t)len); return RYM_CODE_NONE; } /** * @brief Initializes the bootload module. * * This function initializes the bootload module by setting the transmit callback * and configuring the RYM module. It asserts if the initialization fails. * * @param transmit The transmit callback function. */ void bootload_init(bootload_transmit_callback transmit, bootload_end_callback end) { BOOL res = FALSE; transmit_callback = transmit; end_callback = end; res = rym_init(); DBG_ASSERT(res == TRUE __DBG_LINE); res = rym_config(on_begin, on_data, on_end, on_transmit, BOOTLOAD_TIMEOUT); DBG_ASSERT(res == TRUE __DBG_LINE); } /** * @brief Sets the data source index for bootload transmission. * * This function sets the data source index for bootload transmission. The data source index * determines the starting point from which the data will be transmitted. * * @param to_index The index of the data source. */ void bootload_transmit_from(const uint8_t to_index) { data_src_from = to_index; } /** * @brief Jump to the specified address and execute the bootloader. * * @param address The entry address of the bootloader. */ void bootload_jump(uint32_t address) { // Get the entry address of the application program fnc_ptr jump_to_bootload; jump_to_bootload = (fnc_ptr)(*(__IO uint32_t *)(address + 4)); // Disable RCC RCC->APB1ENR1 = 0; RCC->APB1ENR2 = 0; RCC->APB2ENR = 0; RCC->AHB1ENR = 0; RCC->AHB2ENR = 0; RCC->AHB3ENR = 0; // Disable SysTick SysTick->CTRL = 0; // 清空SysTick SysTick->LOAD = 0; // 清空SysTick SysTick->VAL = 0; // 设置向量表偏移地址 SCB->VTOR = address; // 设置堆栈指针 sys_msr_msp(*(__IO uint32_t *)address); // 跳转 jump_to_bootload(); }