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  result = false;
447  }
448 
449  return result;
450 }
451 
452 static bool CalculateCrcOfFile(uint32_t* const crc)
453 {
454  assert_param(crc != NULL);
455 
456  FIL file;
457  bool result = true;
458  uint32_t calculatedCrc = 0U;
459 
460  CrcInit();
461 
462  // Open application file and calculate CRC
463  if (FR_OK == f_open(&file, APP_FILE_NAME, FA_READ))
464  {
465  UINT bytesRead = 0U;
466  do
467  {
468  if (FR_OK != f_read(&file, readBuffer, FLASH_PAGE_SIZE, &bytesRead))
469  {
470  LogPrint("Error: Application file read error at: %u byte\n",
471  (uint32_t)(file.fptr));
472  result = false;
473  break;
474  }
475  if (bytesRead > 0U)
476  {
477  calculatedCrc = CrcAccumulate(readBuffer, (uint32_t)bytesRead);
478  }
479  } while (bytesRead > 0U);
480  (void)f_close(&file);
481  }
482  else
483  {
484  LogPrint("Error: Application file cannot be opened.\n");
485  result = false;
486  }
487 
488  CrcDeInit();
489 
490  *crc = calculatedCrc;
491  return result;
492 }
493 
494 static bool ReadCrcFromFile(uint32_t* const crc)
495 {
496  assert_param(crc != NULL);
497 
498  FIL file;
499  bool result = false;
500 
501  if (FR_OK == f_open(&file, CRC_FILE_NAME, FA_READ))
502  {
503  UINT bytesRead = 0U;
504  if (FR_OK == f_read(&file, readBuffer, 8U, &bytesRead))
505  {
506  readBuffer[bytesRead] = 0x00U;
507  errno = 0;
508  *crc = strtoul((const char*)readBuffer, NULL, 16);
509  if (errno == 0)
510  {
511  result = true;
512  }
513  else
514  {
515  LogPrint(
516  "Error: CRC cannot be converted to numerical value.\n");
517  }
518  }
519  else
520  {
521  LogPrint("Error: CRC file cannot be read.\n");
522  }
523  (void)f_close(&file);
524  }
525  else
526  {
527  LogPrint("Error: CRC file cannot be opened.\n");
528  }
529 
530  return result;
531 }
532 
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:452
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:494
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.