motor_f407/User/lib/bootload/bootload.c

230 lines
7.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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();
}