STM32 Bootloader
Customizable Bootloader for STM32 microcontrollers
bootloader.c
Go to the documentation of this file.
1 
24 /* Includes ------------------------------------------------------------------*/
25 #include "bootloader.h"
26 
27 #include "board.h"
28 #include "crc.h"
29 #include "ff.h"
30 #include "flash.h"
31 #include "led.h"
32 #include "log.h"
33 #include "mcu_hal.h"
34 #include "sd_diskio.h"
35 #include "sd_fatfs.h"
36 #include "sdmmc.h"
37 #include "systick.h"
38 
39 #include <errno.h>
40 #include <stdlib.h>
41 
42 /* Private type definitions --------------------------------------------------*/
43 typedef void (*Function)(void);
45 /* Private variables ---------------------------------------------------------*/
47 static uint8_t readBuffer[FLASH_PAGE_SIZE] = {0};
48 
49 /* Private function prototypes -----------------------------------------------*/
57 static bool CheckApplicationSize(void);
58 
67 static bool CheckApplicationCrc(void);
68 
76 static bool UpdateApplication(void);
77 
85 static bool VerifyApplication(void);
86 
95 static bool CalculateCrcOfFile(uint32_t* const crc);
96 
107 static bool ReadCrcFromFile(uint32_t* const crc);
108 
109 /* Functions -----------------------------------------------------------------*/
111 {
112  bool result = SdCardMount();
113 
114  result = result && CheckApplicationSize();
115  result = result && CheckApplicationCrc();
116  result = result && UpdateApplication();
117  result = result && VerifyApplication();
118 
119  SdCardUnMount();
120  return result;
121 }
122 
124 {
125  bool result = true;
126 
127  // Message: A conversion should not be performed between a pointer to
128  // object and an integer type [misra-c2012-11.4]
129  // Reason: The first 32-bit value of the application represents the
130  // initial stack pointer value. The jump address (the starting
131  // address of the application - which is the address of the
132  // reset exception handler) is the second 32-bit value. These
133  // values need to be accessed and converted into integers.
134  // Risk: Conversion of an integer into a pointer to void may result
135  // in a pointer that is not correctly aligned, resulting in
136  // undefined behavior.
137  // Prevention: Code reviews.
138  // cppcheck-suppress [misra-c2012-11.4]
139  const uint32_t jumpAddress = *(__IO uint32_t*)(APP_ADDRESS + 4U);
140  // cppcheck-suppress [misra-c2012-11.4]
141  const uint32_t stackPointer = *(__IO uint32_t*)APP_ADDRESS;
142 
143  // Check if the application reset handler is within the application space
144  if ((jumpAddress < APP_ADDRESS) || (jumpAddress == 0xFFFFFFFFU))
145  {
146  result = false;
147  }
148 
149  // Check if the stack pointer is within the RAM region
150  if ((stackPointer < SRAM_BASE) ||
151  (stackPointer > (SRAM_BASE + SRAM1_SIZE_MAX + SRAM2_SIZE)))
152  {
153  result = false;
154  }
155 
156  return result;
157 }
158 
160 {
161  // Message: A conversion should not be performed between a pointer to
162  // object and an integer type [misra-c2012-11.4]
163  // Reason: The jump address (the starting address of the application;
164  // the address of the reset handler) is the second 32-bit value
165  // of the application. This value needs to be accessed and
166  // converted into an integer.
167  // Risk: Conversion of an integer into a pointer to void may
168  // result in a pointer that is not correctly aligned,
169  // resulting in undefined behavior.
170  // Prevention: Code reviews.
171  // cppcheck-suppress [misra-c2012-11.4]
172  uint32_t jumpAddress = *(__IO uint32_t*)(APP_ADDRESS + 4U);
173 
174  // Message: A cast shall not be performed between pointer to void and
175  // an arithmetic type [misra-c2012-11.6]
176  // Reason: The jump address needs to be casted to a function pointer
177  // in order to perform the actual jump.
178  // Risk: Conversion of an integer into a pointer to void may result
179  // in a pointer that is not correctly aligned, resulting in
180  // undefined behavior.
181  // Prevention: Code reviews and testing.
182  // cppcheck-suppress [misra-c2012-11.6]
183  Function jump = (Function)jumpAddress;
184 
186 
187  // Message: A conversion should not be performed between a pointer to
188  // object and an integer type [misra-c2012-11.4]
189  // Reason: The first 32-bit value of the application represents the
190  // initial stack pointer value. This values need to be accessed
191  // and converted into an integer.
192  // Risk: Conversion of an integer into a pointer to void may result
193  // in a pointer that is not correctly aligned, resulting in
194  // undefined behavior.
195  // Prevention: Code reviews.
196  // cppcheck-suppress [misra-c2012-11.4]
197  __set_MSP(*(__IO uint32_t*)APP_ADDRESS);
198  jump();
199 }
200 
202 {
203  // Message: A conversion should not be performed between a pointer to
204  // object and an integer type [misra-c2012-11.4]
205  // Reason: The jump address (the starting address of the application;
206  // the address of the reset handler) is the second 32-bit value
207  // of the application. This value needs to be accessed and
208  // converted into an integer.
209  // Risk: Conversion of an integer into a pointer to void may
210  // result in a pointer that is not correctly aligned,
211  // resulting in undefined behavior.
212  // Prevention: Code reviews.
213  // cppcheck-suppress [misra-c2012-11.4]
214  uint32_t jumpAddress = *(__IO uint32_t*)(SYSTEM_MEMORY_ADDRESS + 4U);
215 
216  // Message: A cast shall not be performed between pointer to void and
217  // an arithmetic type [misra-c2012-11.6]
218  // Reason: The jump address needs to be casted to a function pointer
219  // in order to perform the actual jump.
220  // Risk: Conversion of an integer into a pointer to void may result
221  // in a pointer that is not correctly aligned, resulting in
222  // undefined behavior.
223  // Prevention: Code reviews and testing.
224  // cppcheck-suppress [misra-c2012-11.6]
225  Function jump = (Function)jumpAddress;
226 
228 
229  // Message: A conversion should not be performed between a pointer to
230  // object and an integer type [misra-c2012-11.4]
231  // Reason: The first 32-bit value of the application represents the
232  // initial stack pointer value. This values need to be accessed
233  // and converted into an integer.
234  // Risk: Conversion of an integer into a pointer to void may result
235  // in a pointer that is not correctly aligned, resulting in
236  // undefined behavior.
237  // Prevention: Code reviews.
238  // cppcheck-suppress [misra-c2012-11.4]
239  __set_MSP(*(__IO uint32_t*)SYSTEM_MEMORY_ADDRESS);
240  jump();
241 }
242 
243 static bool CheckApplicationSize(void)
244 {
245  FIL file;
246  bool result = true;
247 
248  if (FR_OK == f_open(&file, APP_FILE_NAME, FA_READ))
249  {
250  const uint32_t appSize = f_size(&file);
251  LogPrint("Size of application on the SD card: %u bytes\n", appSize);
252  if (appSize > APP_SIZE)
253  {
254  LogPrint("Error: Application on SD card is too large.\n");
255  LogPrint("Max allowed size: %u bytes\n", APP_SIZE);
256  result = false;
257  }
258  (void)f_close(&file);
259  }
260  else
261  {
262  LogPrint("Error: Application file cannot be opened.\n");
263  result = false;
264  }
265 
266  return result;
267 }
268 
269 static bool CheckApplicationCrc(void)
270 {
271  uint32_t calculatedCrc = 0U;
272  uint32_t providedCrc = 0U;
273  bool result;
274 
275  result = CalculateCrcOfFile(&calculatedCrc);
276  result = result && ReadCrcFromFile(&providedCrc);
277 
278  // Compare CRC values
279  if (result)
280  {
281  if (providedCrc == calculatedCrc)
282  {
283  LogPrint("CRC matches. CRC value: 0x%x\n", calculatedCrc);
284  }
285  else
286  {
287  LogPrint("Error: CRC mismatch.\n");
288  LogPrint("Provided application CRC: 0x%08x\n", providedCrc);
289  LogPrint("Calculated application CRC: 0x%08x\n", calculatedCrc);
290  result = false;
291  }
292  }
293 
294  return result;
295 }
296 
298 {
299  FIL file;
300  bool result = false;
301 
302  LogPrint("Starting programming...\n");
303  LedGreen2On();
304 
305  if (FR_OK == f_open(&file, APP_FILE_NAME, FA_READ))
306  {
307  UINT bytesRead;
308  FRESULT status;
309  uint32_t bytesFlashed = 0;
310 
312 
313  // Message: There should be no more than one break or goto statement
314  // used to terminate any iteration statement
315  // [misra-c2012-15.4]
316  // Reason: Multiple and early break statements are used for
317  // avoiding a complex implementation of checking the
318  // results of the intermediate operations. It also allows
319  // better code readability.
320  // Risk: A single point of exit is required by IEC 61508 and ISO
321  // 26262 as part of the requirements. Early returns may
322  // lead to the unintentional omission of function
323  // termination code. If a function has exit points
324  // interspersed with statements that produce persistent
325  // side effects, it is not easy to determine which side
326  // effects will occur when the function is executed.
327  // Prevention: Code reviews and testing.
328  // cppcheck-suppress-begin [misra-c2012-15.4]
329  do
330  {
331  status = f_read(&file, readBuffer, FLASH_PAGE_SIZE, &bytesRead);
332  if (status != FR_OK)
333  {
334  LogPrint("Error: Application file read error at: %u byte\n",
335  (uint32_t)(file.fptr));
336  break;
337  }
338 
339  if (bytesRead > 0U)
340  {
341  if (FlashWrite(readBuffer, (APP_ADDRESS + bytesFlashed),
342  (uint32_t)bytesRead) != (uint32_t)bytesRead)
343  {
344  LogPrint("Error: Programming error at: %u byte\n",
345  bytesFlashed);
346  break;
347  }
348 
349  bytesFlashed += bytesRead;
350  }
351 
352  // Toggle LED during programming
353  LedGreen1Toggle();
354  } while (bytesRead > 0U);
355  // cppcheck-suppress-end [misra-c2012-15.4]
356 
357  (void)f_close(&file);
358 
359  if (status == FR_OK)
360  {
361  LogPrint("Programming successful. Flashed: %u bytes\n",
362  bytesFlashed);
363  result = true;
364  }
365  }
366  else
367  {
368  LogPrint("Error: Application file cannot be opened.\n");
369  }
370 
371  LedGreen1Off();
372  LedGreen2Off();
373  return result;
374 }
375 
376 static bool VerifyApplication(void)
377 {
378  FIL file;
379  bool result = true;
380 
381  LogPrint("Starting verification...\n");
382 
383  /* Open file for verification */
384  if (FR_OK == f_open(&file, APP_FILE_NAME, FA_READ))
385  {
386  UINT bytesRead;
387  uint32_t addressToVerify = APP_ADDRESS;
388 
389  // Message: There should be no more than one break or goto statement
390  // used to terminate any iteration statement
391  // [misra-c2012-15.4]
392  // Reason: Multiple and early break statements are used for
393  // avoiding a complex implementation of checking the
394  // results of the intermediate operations. It also allows
395  // better code readability.
396  // Risk: A single point of exit is required by IEC 61508 and ISO
397  // 26262 as part of the requirements. Early returns may
398  // lead to the unintentional omission of function
399  // termination code. If a function has exit points
400  // interspersed with statements that produce persistent
401  // side effects, it is not easy to determine which side
402  // effects will occur when the function is executed.
403  // Prevention: Code reviews and testing.
404  // cppcheck-suppress-begin [misra-c2012-15.4]
405  do
406  {
407  uint32_t dataChunkFromFile = 0U;
408  if (FR_OK != f_read(&file, &dataChunkFromFile,
409  sizeof(dataChunkFromFile), &bytesRead))
410  {
411  LogPrint("Error: Application file read error at: %u byte\n",
412  (uint32_t)(file.fptr));
413  result = false;
414  break;
415  }
416 
417  if (bytesRead > 0U)
418  {
419  uint32_t dataChunkFromFlash = 0U;
420  (void)FlashRead((uint8_t*)&dataChunkFromFlash, addressToVerify,
421  sizeof(dataChunkFromFlash));
422  if (dataChunkFromFlash != dataChunkFromFile)
423  {
424  LogPrint("Error: Verification error at: 0x%x\n",
425  addressToVerify);
426  result = false;
427  break;
428  }
429 
430  addressToVerify += bytesRead;
431  }
432  } while (bytesRead > 0U);
433  // cppcheck-suppress-end [misra-c2012-15.4]
434 
435  (void)f_close(&file);
436 
437  if (result)
438  {
439  LogPrint("Verification successful. Verified: %u bytes\n",
440  (addressToVerify - APP_ADDRESS));
441  }
442  }
443  else
444  {
445  LogPrint("Error: Application file cannot be opened.\n");
446  }
447 
448  return result;
449 }
450 
451 static bool CalculateCrcOfFile(uint32_t* const crc)
452 {
453  assert_param(crc != NULL);
454 
455  FIL file;
456  bool result = true;
457  uint32_t calculatedCrc = 0U;
458 
459  CrcInit();
460 
461  // Open application file and calculate CRC
462  if (FR_OK == f_open(&file, APP_FILE_NAME, FA_READ))
463  {
464  UINT bytesRead = 0U;
465  do
466  {
467  if (FR_OK != f_read(&file, readBuffer, FLASH_PAGE_SIZE, &bytesRead))
468  {
469  LogPrint("Error: Application file read error at: %u byte\n",
470  (uint32_t)(file.fptr));
471  result = false;
472  break;
473  }
474  if (bytesRead > 0U)
475  {
476  calculatedCrc = CrcAccumulate(readBuffer, (uint32_t)bytesRead);
477  }
478  } while (bytesRead > 0U);
479  (void)f_close(&file);
480  }
481  else
482  {
483  LogPrint("Error: Application file cannot be opened.\n");
484  result = false;
485  }
486 
487  CrcDeInit();
488 
489  *crc = calculatedCrc;
490  return result;
491 }
492 
493 static bool ReadCrcFromFile(uint32_t* const crc)
494 {
495  assert_param(crc != NULL);
496 
497  FIL file;
498  bool result = false;
499 
500  if (FR_OK == f_open(&file, CRC_FILE_NAME, FA_READ))
501  {
502  UINT bytesRead = 0U;
503  if (FR_OK == f_read(&file, readBuffer, 8U, &bytesRead))
504  {
505  readBuffer[bytesRead] = 0x00U;
506  errno = 0;
507  *crc = strtoul((const char*)readBuffer, NULL, 16);
508  if (errno == 0)
509  {
510  result = true;
511  }
512  else
513  {
514  LogPrint(
515  "Error: CRC cannot be converted to numerical value.\n");
516  }
517  }
518  else
519  {
520  LogPrint("Error: CRC file cannot be read.\n");
521  }
522  (void)f_close(&file);
523  }
524  else
525  {
526  LogPrint("Error: CRC file cannot be opened.\n");
527  }
528 
529  return result;
530 }
531 
This file contains the board-specific GPIO pin definitions, peripheral configurations,...
This file contains the bootloader configuration parameters, function prototypes, and other required d...
This file contains the CRC peripheral driver function prototypes for initialization,...
This file contains the MCU internal flash driver function prototypes for read, write,...
bool BootloaderUpdateFirmware(void)
This function performs the complete application update procedure.
Definition: bootloader.c:110
static bool CheckApplicationCrc(void)
Validate the integrity of the application binary by comparing a CRC-32 value calculated over the file...
Definition: bootloader.c:269
bool BootloaderCheckForApplication(void)
This function checks whether a valid application exists in flash.
Definition: bootloader.c:123
static bool CalculateCrcOfFile(uint32_t *const crc)
Calculate the CRC-32 checksum of the application binary file on the SD card using the hardware CRC pe...
Definition: bootloader.c:451
static bool CheckApplicationSize(void)
Check whether the application binary on the SD card fits into the available flash memory region desig...
Definition: bootloader.c:243
static uint8_t readBuffer[FLASH_PAGE_SIZE]
Definition: bootloader.c:47
#define APP_FILE_NAME
Definition: bootloader.h:46
#define APP_SIZE
Definition: bootloader.h:83
void(* Function)(void)
Definition: bootloader.c:43
static bool ReadCrcFromFile(uint32_t *const crc)
Read the expected CRC-32 value from a dedicated CRC file on the SD card and convert the hexadecimal s...
Definition: bootloader.c:493
static bool VerifyApplication(void)
Verify the programmed flash contents against the original application binary file on the SD card by p...
Definition: bootloader.c:376
#define CRC_FILE_NAME
Definition: bootloader.h:49
void BootloaderJumpToApplication(void)
This function performs the jump to the user application in flash.
Definition: bootloader.c:159
void BootloaderJumpToSysMem(void)
This function performs the jump to the microcontroller System Memory (ST built-in bootloader).
Definition: bootloader.c:201
static bool UpdateApplication(void)
Erase the application flash region and program it with the contents of the application binary file fr...
Definition: bootloader.c:297
#define APP_ADDRESS
Definition: bootloader.h:70
bool SdCardMount(void)
Mount the FatFs file system on the SD card by linking the disk I/O driver and mounting the default lo...
Definition: sd_fatfs.c:36
void SdCardUnMount(void)
Unmount the FatFs file system from the SD card and unlink the disk I/O driver.
Definition: sd_fatfs.c:80
void SetVectorTableLocation(const uint32_t address)
Set the vector table location to the specified address.
Definition: board.c:79
uint32_t CrcAccumulate(const uint8_t *const data, const uint32_t size)
Calculate the CRC value over the provided buffer.
Definition: crc.c:71
void CrcDeInit(void)
De-initialize the CRC peripheral.
Definition: crc.c:65
void CrcInit(void)
Initialize the CRC peripheral.
Definition: crc.c:37
uint32_t FlashRead(uint8_t *const data, const uint32_t address, const uint32_t length)
Read data from the microcontroller flash.
Definition: flash.c:388
#define SYSTEM_MEMORY_ADDRESS
Definition: flash.h:38
void FlashRemapMemoryToSystemFlash(void)
Remap the microcontroller flash memory to the built-in system flash memory.
Definition: flash.c:445
uint32_t FlashWrite(const uint8_t *const data, const uint32_t address, const uint32_t length)
Write data into the microcontroller flash.
Definition: flash.c:412
void FlashClearErrorFlags(void)
Clear all flash error flags.
Definition: flash.c:133
void LedGreen1Off(void)
Turn off the 1st green LED (LD2).
Definition: led.c:38
void LedGreen1Toggle(void)
Toggle the 1st green LED (LD2).
Definition: led.c:44
void LedGreen2On(void)
Turn on the 2nd green LED (LD3).
Definition: led.c:49
void LedGreen2Off(void)
Turn off the 2nd green LED (LD3).
Definition: led.c:55
void LogPrint(const char *format,...)
Print a formatted log message over the UART interface.
Definition: log.c:40
#define assert_param(expr)
The assert_param macro is used for function parameter check.
This file contains the LED driver function prototypes for controlling the green LEDs on the STM32L496...
This file contains the UART-based logging function prototypes for the debug output.
This file includes the microcontroller-specific HAL header files.
This file contains the SD card disk I/O driver interface for the FatFs middleware.
This file contains the SD card FatFs file system mount and unmount function prototypes.
This file contains the SDMMC peripheral driver function prototypes for SD card initialization,...
This file contains the SysTick timer initialization and de-initialization function prototypes.