diff --git a/conf_general.c b/conf_general.c index af021431..c589445a 100644 --- a/conf_general.c +++ b/conf_general.c @@ -65,6 +65,7 @@ void conf_general_init(void) { FLASH_ClearFlag(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); EE_Init(); + FLASH_Lock(); } /** @@ -113,6 +114,7 @@ bool conf_general_store_app_configuration(app_configuration *conf) { uint8_t *conf_addr = (uint8_t*)conf; uint16_t var; + FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); @@ -125,6 +127,7 @@ bool conf_general_store_app_configuration(app_configuration *conf) { break; } } + FLASH_Lock(); timeout_configure_IWDT(); @@ -179,6 +182,7 @@ bool conf_general_store_mc_configuration(mc_configuration *conf) { bool is_ok = true; uint8_t *conf_addr = (uint8_t*)conf; + FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); @@ -191,6 +195,7 @@ bool conf_general_store_mc_configuration(mc_configuration *conf) { break; } } + FLASH_Lock(); timeout_configure_IWDT(); diff --git a/crc.c b/crc.c index 2691acfc..654a9c9d 100644 --- a/crc.c +++ b/crc.c @@ -18,6 +18,7 @@ */ #include "crc.h" +#include "stm32f4xx.h" // CRC Table const unsigned short crc16_tab[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, @@ -58,3 +59,29 @@ unsigned short crc16(unsigned char *buf, unsigned int len) { } return cksum; } + +/** + * @brief Computes the 32-bit CRC of a given buffer of data word(32-bit) using + * Hardware Acceleration. + * @param pBuffer: pointer to the buffer containing the data to be computed + * @param BufferLength: length of the buffer to be computed + * @retval 32-bit CRC + */ +uint32_t crc32(uint32_t *pBuffer, uint32_t BufferLength) { + uint32_t index = 0; + + for(index = 0; index < BufferLength; index++) { + CRC->DR = pBuffer[index]; + } + return (CRC->DR); +} + +/** + * @brief Resets the CRC Data register (DR). + * @param None + * @retval None + */ +void crc32_reset(void) { + /* Reset CRC generator */ + CRC->CR = CRC_CR_RESET; +} diff --git a/crc.h b/crc.h index e5b9f738..b70b7b8d 100644 --- a/crc.h +++ b/crc.h @@ -20,9 +20,13 @@ #ifndef CRC_H_ #define CRC_H_ +#include + /* * Functions */ unsigned short crc16(unsigned char *buf, unsigned int len); +uint32_t crc32(uint32_t *buf, uint32_t len); +void crc32_reset(void); #endif /* CRC_H_ */ diff --git a/datatypes.h b/datatypes.h index f18eb986..b3e1244c 100644 --- a/datatypes.h +++ b/datatypes.h @@ -92,7 +92,8 @@ typedef enum { FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET, FAULT_CODE_ENCODER_SPI, FAULT_CODE_ENCODER_SINCOS_BELOW_MIN_AMPLITUDE, - FAULT_CODE_ENCODER_SINCOS_ABOVE_MAX_AMPLITUDE + FAULT_CODE_ENCODER_SINCOS_ABOVE_MAX_AMPLITUDE, + FAULT_CODE_FLASH_CORRUPTION } mc_fault_code; typedef enum { diff --git a/flash_helper.c b/flash_helper.c index ed5ba7cf..7814d296 100644 --- a/flash_helper.c +++ b/flash_helper.c @@ -25,6 +25,7 @@ #include "mc_interface.h" #include "timeout.h" #include "hw.h" +#include "crc.h" #include /* @@ -35,6 +36,8 @@ #define APP_BASE 0 #define NEW_APP_BASE 8 #define NEW_APP_SECTORS 3 +#define APP_MAX_SIZE (3 * (1 << 17)) +#define EEPROM_EMULATION_SIZE 0x8000 // Base address of the Flash sectors #define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) // Base @ of Sector 0, 16 Kbytes @@ -50,6 +53,23 @@ #define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) // Base @ of Sector 10, 128 Kbytes #define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) // Base @ of Sector 11, 128 Kbytes +#define APP_CRC_WAS_CALCULATED_FLAG ((uint32_t)0xAAAAAAAA) +#define APP_CRC_WAS_CALCULATED_FLAG_ADDRESS (uint32_t*)(APP_MAX_SIZE - 8) + +#define VECTOR_TABLE_ADDRESS ((uint32_t *)ADDR_FLASH_SECTOR_0) +#define VECTOR_TABLE_SIZE ((uint32_t)(ADDR_FLASH_SECTOR_1 - ADDR_FLASH_SECTOR_0)) + +#define APP_START_ADDRESS ((uint32_t *)(ADDR_FLASH_SECTOR_1 + EEPROM_EMULATION_SIZE)) +#define APP_SIZE ((uint32_t)(APP_MAX_SIZE - VECTOR_TABLE_SIZE - EEPROM_EMULATION_SIZE)) + +typedef struct { + uint32_t crc_flag; + uint32_t crc; +}crc_info_t; + +//Make sure the app image has the CRC bits set to '1' to later write the flag and CRC. +const crc_info_t __attribute__((section (".crcinfo"))) crc_info = {0xFFFFFFFF, 0xFFFFFFFF}; + // Private constants static const uint32_t flash_addr[FLASH_SECTORS] = { ADDR_FLASH_SECTOR_0, @@ -96,12 +116,14 @@ uint16_t flash_helper_erase_new_app(uint32_t new_app_size) { if (new_app_size > flash_addr[NEW_APP_BASE + i]) { uint16_t res = FLASH_EraseSector(flash_sector[NEW_APP_BASE + i], VoltageRange_3); if (res != FLASH_COMPLETE) { + FLASH_Lock(); return res; } } else { break; } } + FLASH_Lock(); timeout_configure_IWDT(); utils_sys_unlock_cnt(); @@ -110,6 +132,7 @@ uint16_t flash_helper_erase_new_app(uint32_t new_app_size) { } uint16_t flash_helper_write_new_app_data(uint32_t offset, uint8_t *data, uint32_t len) { + FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); @@ -121,9 +144,11 @@ uint16_t flash_helper_write_new_app_data(uint32_t offset, uint8_t *data, uint32_ for (uint32_t i = 0;i < len;i++) { uint16_t res = FLASH_ProgramByte(flash_addr[NEW_APP_BASE] + offset + i, data[i]); if (res != FLASH_COMPLETE) { + FLASH_Lock(); return res; } } + FLASH_Lock(); timeout_configure_IWDT(); @@ -191,3 +216,98 @@ uint8_t* flash_helper_get_sector_address(uint32_t fsector) { return res; } + +/** + * @brief Compute the CRC of the application code to verify its integrity + * @retval FAULT_CODE_NONE or FAULT_CODE_FLASH_CORRUPTION + */ +uint32_t flash_helper_verify_flash_memory(void) { + uint32_t crc; + // Look for a flag indicating that the CRC was previously computed. + // If it is blank (0xFFFFFFFF), calculate and store the CRC. + if( (APP_CRC_WAS_CALCULATED_FLAG_ADDRESS)[0] == APP_CRC_WAS_CALCULATED_FLAG ) + { + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE); + crc32_reset(); + + // compute vector table (sector 0) + crc32((VECTOR_TABLE_ADDRESS), (VECTOR_TABLE_SIZE)/4); + + // skip emulated EEPROM (sector 1 and 2) + + // compute application code + crc = crc32(APP_START_ADDRESS, (APP_SIZE)/4); + + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, DISABLE); + + // A CRC over the full image should return zero. + return (crc == 0)? FAULT_CODE_NONE : FAULT_CODE_FLASH_CORRUPTION; + } + else { + FLASH_Unlock(); + FLASH_ClearFlag(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | + FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); + + //Write the flag to indicate CRC has been computed. + uint16_t res = FLASH_ProgramWord((uint32_t)APP_CRC_WAS_CALCULATED_FLAG_ADDRESS, APP_CRC_WAS_CALCULATED_FLAG); + if (res != FLASH_COMPLETE) { + FLASH_Lock(); + return FAULT_CODE_FLASH_CORRUPTION; + } + + // Compute flash crc including the new flag + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE); + crc32_reset(); + + // compute vector table (sector 0) + crc32(VECTOR_TABLE_ADDRESS, (VECTOR_TABLE_SIZE)/4); + + // skip emulated EEPROM (sector 1 and 2) + + // compute application code + crc = crc32(APP_START_ADDRESS, (APP_SIZE - 4)/4); + + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, DISABLE); + + //Store CRC + res = FLASH_ProgramWord(APP_MAX_SIZE - 4, crc); + if (res != FLASH_COMPLETE) { + FLASH_Lock(); + return FAULT_CODE_FLASH_CORRUPTION; + } + FLASH_Lock(); + + // reboot + NVIC_SystemReset(); + return FAULT_CODE_NONE; + } +} + +uint32_t flash_helper_verify_flash_memory_chunk(void) { + static uint32_t index = 0; + const uint32_t chunk_size = 8192; + uint32_t res = FAULT_CODE_NONE; + uint32_t crc = 0; + + // Make sure RCC_AHB1Periph_CRC is enabled + if (index == 0) { + crc32_reset(); + } + + if (index < VECTOR_TABLE_SIZE) { + crc32((VECTOR_TABLE_ADDRESS + index), chunk_size/4); + } + else { + crc = crc32((uint32_t*)((uint32_t)APP_START_ADDRESS + index - VECTOR_TABLE_SIZE), chunk_size/4); + } + + index += chunk_size; + if (index >= (VECTOR_TABLE_SIZE + APP_SIZE)) { + index = 0; + if (crc != 0) { + res = FAULT_CODE_FLASH_CORRUPTION; + } + } + return res; +} + diff --git a/flash_helper.h b/flash_helper.h index 897d4a9b..053f1231 100644 --- a/flash_helper.h +++ b/flash_helper.h @@ -27,5 +27,7 @@ uint16_t flash_helper_erase_new_app(uint32_t new_app_size); uint16_t flash_helper_write_new_app_data(uint32_t offset, uint8_t *data, uint32_t len); void flash_helper_jump_to_bootloader(void); uint8_t* flash_helper_get_sector_address(uint32_t fsector); +uint32_t flash_helper_verify_flash_memory(void); +uint32_t flash_helper_verify_flash_memory_chunk(void); #endif /* FLASH_HELPER_H_ */ diff --git a/ld_eeprom_emu.ld b/ld_eeprom_emu.ld index 95456e82..7ddb0991 100644 --- a/ld_eeprom_emu.ld +++ b/ld_eeprom_emu.ld @@ -20,11 +20,14 @@ /* * STM32F407xG memory setup. * Note: Use of ram1 and ram2 is mutually exclusive with use of ram0. + * Note: flash2 length decreased by 8 bytes to use it as hardcoded CRC + * flags. */ MEMORY { flash : org = 0x08000000, len = 16k - flash2 : org = 0x0800C000, len = 480k + flash2 : org = 0x0800C000, len = 393216 - 8 /* NEW_APP_MAX_SIZE - CRC_INFO */ + crcinfo : org = 0x0805FFF8, len = 8 /* CRC info*/ ram0 : org = 0x20000000, len = 128k /* SRAM1 + SRAM2 */ ram1 : org = 0x20000000, len = 112k /* SRAM1 */ ram2 : org = 0x2001C000, len = 16k /* SRAM2 */ @@ -142,6 +145,13 @@ SECTIONS . = ALIGN(4); _etext = .; _textdata = _etext; + + _crcinfo_start_address = 0x0805FFF8; + + .crcinfo _crcinfo_start_address : + { + KEEP(*(.crcinfo)) + } > crcinfo .mstack : { diff --git a/main.c b/main.c index 7a6a68aa..f96d2f30 100644 --- a/main.c +++ b/main.c @@ -49,6 +49,7 @@ #include "spi_sw.h" #include "timer.h" #include "imu.h" +#include "flash_helper.h" #if HAS_BLACKMAGIC #include "bm_if.h" #endif @@ -74,6 +75,22 @@ // Private variables static THD_WORKING_AREA(periodic_thread_wa, 1024); static THD_WORKING_AREA(timer_thread_wa, 128); +static THD_WORKING_AREA(flash_integrity_check_thread_wa, 1024); + +static THD_FUNCTION(flash_integrity_check_thread, arg) { + (void)arg; + + chRegSetThreadName("Flash integrity check"); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE); + + for(;;) { + if (flash_helper_verify_flash_memory_chunk() == FAULT_CODE_FLASH_CORRUPTION) { + NVIC_SystemReset(); + } + + chThdSleepMilliseconds(50); + } +} static THD_FUNCTION(periodic_thread, arg) { (void)arg; @@ -183,6 +200,17 @@ int main(void) { timer_init(); conf_general_init(); + + if( flash_helper_verify_flash_memory() == FAULT_CODE_FLASH_CORRUPTION ) { + // Loop here, it is not safe to run any code + while (1) { + chThdSleepMilliseconds(100); + LED_RED_ON(); + chThdSleepMilliseconds(75); + LED_RED_OFF(); + } + } + ledpwm_init(); mc_configuration mcconf; @@ -233,6 +261,7 @@ int main(void) { // Threads chThdCreateStatic(periodic_thread_wa, sizeof(periodic_thread_wa), NORMALPRIO, periodic_thread, NULL); chThdCreateStatic(timer_thread_wa, sizeof(timer_thread_wa), NORMALPRIO, timer_thread, NULL); + chThdCreateStatic(flash_integrity_check_thread_wa, sizeof(flash_integrity_check_thread_wa), LOWPRIO, flash_integrity_check_thread, NULL); #if WS2811_TEST unsigned int color_ind = 0;