STM32 Bootloader
Customizable Bootloader for STM32 microcontrollers
flash.c
Go to the documentation of this file.
1 
25 #include "flash.h"
26 
27 #include "mcu_hal.h"
28 #include "rcc.h"
29 
30 #include <string.h>
31 
32 /* Private defines -----------------------------------------------------------*/
33 // Message: A conversion should not be performed between a pointer to object
34 // and an integer type [misra-c2012-11.4]
35 // Reason: The wrapped register defines from the vendor are addressing the
36 // memory mapped base register of the FLASH hardware peripheral.
37 // In order not to supress the message every time the defines are
38 // used, they are wrapped with defines for which the message is
39 // suppressed; resulting in a single suppression.
40 // Risk: Conversion of a pointer to object into an integer may
41 // produce a value that cannot be represented in the chosen
42 // integer type resulting in undefined behavior.
43 // Prevention: Code reviews.
44 
46 // cppcheck-suppress [misra-c2012-11.4]
47 #define FLASH_SIZE_WRAPPER (uint32_t)(FLASH_SIZE)
48 
50 // cppcheck-suppress [misra-c2012-11.4]
51 #define FLASH_BANK_SIZE_WRAPPER (uint32_t)(FLASH_BANK_SIZE)
52 
54 // cppcheck-suppress [misra-c2012-11.4]
55 #define FLASH_PAGE_NB_PER_BANK \
56  (uint32_t)(FLASH_BANK_SIZE_WRAPPER / FLASH_PAGE_SIZE)
57 
58 /* Private function prototypes -----------------------------------------------*/
72 static bool FlashPerformErase(const uint32_t address, const uint32_t length);
73 
89 static uint32_t FlashPerformWrite(const uint8_t* const data,
90  const uint32_t address,
91  const uint32_t length);
92 
101 static uint32_t FlashGetBank(const uint32_t address);
102 
115 static uint32_t FlashGetPage(const uint32_t address);
116 
127 static bool FlashCheckIfPageRangesOverlap(const uint32_t range1Start,
128  const uint32_t range1End,
129  const uint32_t range2Start,
130  const uint32_t range2End);
131 
132 /* Functions -----------------------------------------------------------------*/
134 {
135  (void)HAL_FLASH_Unlock();
136  // cppcheck-suppress [misra-c2012-11.4]
137  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
138  (void)HAL_FLASH_Lock();
139 }
140 
141 bool FlashCheckIfDataFits(const uint32_t address, const uint32_t length)
142 {
143  return (address >= FLASH_BASE) &&
144  ((address + length) <= (FLASH_BASE + FLASH_SIZE_WRAPPER));
145 }
146 
147 bool FlashCheckIfWriteProtectionEnabled(const uint32_t address,
148  const uint32_t length)
149 {
150  bool result = false;
151 
152  FLASH_OBProgramInitTypeDef obProgram = {0};
153 
154  const uint32_t bankStart = FlashGetBank(address);
155  const uint32_t bankEnd = FlashGetBank(address + length - 1U);
156  uint32_t pageStart = FlashGetPage(address);
157  uint32_t pageEnd = FlashGetPage(address + length - 1U);
158 
159  // Check Bank 1
160  if (bankStart == FLASH_BANK_1)
161  {
162  // If the address range to be checked spans over both banks, pin the
163  // ending page number to the max value of the bank
164  if (bankStart != bankEnd)
165  {
166  pageEnd = FLASH_PAGE_NB_PER_BANK - 1U;
167  }
168 
169  // Check Bank Area A
170  obProgram.WRPArea = OB_WRPAREA_BANK1_AREAA;
171  HAL_FLASHEx_OBGetConfig(&obProgram);
172  if (FlashCheckIfPageRangesOverlap(obProgram.WRPStartOffset,
173  obProgram.WRPEndOffset, pageStart,
174  pageEnd))
175  {
176  result = true;
177  }
178 
179  // Check Bank Area B
180  obProgram.WRPArea = OB_WRPAREA_BANK1_AREAB;
181  HAL_FLASHEx_OBGetConfig(&obProgram);
182  if (FlashCheckIfPageRangesOverlap(obProgram.WRPStartOffset,
183  obProgram.WRPEndOffset, pageStart,
184  pageEnd))
185  {
186  result = true;
187  }
188  }
189 
190  // Check Bank 2
191  if (bankEnd == FLASH_BANK_2)
192  {
193  // If the address range to be checked spans over both banks, pin the
194  // starting page number to 0
195  if (bankStart != bankEnd)
196  {
197  pageStart = 0U;
198  }
199 
200  // Check Bank Area A
201  obProgram.WRPArea = OB_WRPAREA_BANK2_AREAA;
202  HAL_FLASHEx_OBGetConfig(&obProgram);
203  if (FlashCheckIfPageRangesOverlap(obProgram.WRPStartOffset,
204  obProgram.WRPEndOffset, pageStart,
205  pageEnd))
206  {
207  result = true;
208  }
209 
210  // Check Bank Area B
211  obProgram.WRPArea = OB_WRPAREA_BANK2_AREAB;
212  HAL_FLASHEx_OBGetConfig(&obProgram);
213  if (FlashCheckIfPageRangesOverlap(obProgram.WRPStartOffset,
214  obProgram.WRPEndOffset, pageStart,
215  pageEnd))
216  {
217  result = true;
218  }
219  }
220 
221  return result;
222 }
223 
225 {
226  FLASH_OBProgramInitTypeDef obProgram = {0};
227 
228  // Unlock the flash control & flash option bytes registers
229  bool result = (HAL_OK == HAL_FLASH_Unlock());
230  if (result)
231  {
232  result = (HAL_OK == HAL_FLASH_OB_Unlock());
233  }
234 
235  /* Remove write protection in bank 1 area A */
236  obProgram.WRPArea = OB_WRPAREA_BANK1_AREAA;
237  obProgram.OptionType = OPTIONBYTE_WRP;
238  obProgram.WRPStartOffset = 0xFF;
239  obProgram.WRPEndOffset = 0x00;
240  if (result)
241  {
242  result = (HAL_OK == HAL_FLASHEx_OBProgram(&obProgram));
243  }
244 
245  /* Remove write protection in bank 1 area B */
246  obProgram.WRPArea = OB_WRPAREA_BANK1_AREAB;
247  obProgram.OptionType = OPTIONBYTE_WRP;
248  obProgram.WRPStartOffset = 0xFF;
249  obProgram.WRPEndOffset = 0x00;
250  if (result)
251  {
252  result = (HAL_OK == HAL_FLASHEx_OBProgram(&obProgram));
253  }
254 
255  /* Remove write protection in bank 2 area A */
256  obProgram.WRPArea = OB_WRPAREA_BANK2_AREAA;
257  obProgram.OptionType = OPTIONBYTE_WRP;
258  obProgram.WRPStartOffset = 0xFF;
259  obProgram.WRPEndOffset = 0x00;
260  if (result)
261  {
262  result = (HAL_OK == HAL_FLASHEx_OBProgram(&obProgram));
263  }
264 
265  /* Remove write protection in bank 2 area B */
266  obProgram.WRPArea = OB_WRPAREA_BANK2_AREAB;
267  obProgram.OptionType = OPTIONBYTE_WRP;
268  obProgram.WRPStartOffset = 0xFF;
269  obProgram.WRPEndOffset = 0x00;
270  if (result)
271  {
272  result = (HAL_OK == HAL_FLASHEx_OBProgram(&obProgram));
273  }
274 
275  // Launch the loading of flash option bytes
276  // Note: this generates a system reset!
277  if (result)
278  {
279  result = (HAL_OK == HAL_FLASH_OB_Launch());
280  }
281 
282  // Lock the flash control & flash option bytes registers
283  // Note: the functions always return HAL_OK; therefore their return values
284  // are ignored
285  (void)HAL_FLASH_OB_Lock();
286  (void)HAL_FLASH_Lock();
287 
288  return result;
289 }
290 
291 bool FlashEnableWriteProtection(const uint32_t address, const uint32_t length)
292 {
293  FLASH_OBProgramInitTypeDef obProgram = {0};
294 
295  const uint32_t bankStart = FlashGetBank(address);
296  const uint32_t bankEnd = FlashGetBank(address + length - 1U);
297  uint32_t pageStart = FlashGetPage(address);
298  uint32_t pageEnd = FlashGetPage(address + length - 1U);
299 
300  // Unlock the flash control & flash option bytes registers
301  bool result = (HAL_OK == HAL_FLASH_Unlock());
302  if (result)
303  {
304  result = (HAL_OK == HAL_FLASH_OB_Unlock());
305  }
306 
307  // Check if address range is part of bank 1
308  if (bankStart == FLASH_BANK_1)
309  {
310  // If the address range to be checked spans over both banks, pin the
311  // ending page number to the max value of the bank
312  if (bankStart != bankEnd)
313  {
314  pageEnd = FLASH_PAGE_NB_PER_BANK - 1U;
315  }
316 
317  // Enable write protection in bank 1 using area A
318  // Note: area B is not used
319  obProgram.WRPArea = OB_WRPAREA_BANK1_AREAA;
320  obProgram.OptionType = OPTIONBYTE_WRP;
321  obProgram.WRPStartOffset = pageStart;
322  obProgram.WRPEndOffset = pageEnd;
323  if (result)
324  {
325  result = (HAL_OK == HAL_FLASHEx_OBProgram(&obProgram));
326  }
327  }
328 
329  // Check if address range is part of bank 2
330  if (bankEnd == FLASH_BANK_2)
331  {
332  // If the address range to be checked spans over both banks, pin the
333  // starting page number to 0
334  if (bankStart != bankEnd)
335  {
336  pageStart = 0U;
337  }
338 
339  // Enable write protection in bank 1 using area A
340  // Note: area B is not used
341  obProgram.WRPArea = OB_WRPAREA_BANK2_AREAA;
342  obProgram.OptionType = OPTIONBYTE_WRP;
343  obProgram.WRPStartOffset = pageStart;
344  obProgram.WRPEndOffset = pageEnd;
345  if (result)
346  {
347  result = (HAL_OK == HAL_FLASHEx_OBProgram(&obProgram));
348  }
349  }
350 
351  // Launch the loading of flash option bytes
352  // Note: this generates a system reset!
353  if (result)
354  {
355  result = (HAL_OK == HAL_FLASH_OB_Launch());
356  }
357 
358  // Lock the flash control & flash option bytes registers
359  // Note: the functions always return HAL_OK; therefore their return values
360  // are ignored
361  (void)HAL_FLASH_OB_Lock();
362  (void)HAL_FLASH_Lock();
363 
364  return result;
365 }
366 
367 bool FlashCompare(const uint8_t* const data,
368  const uint32_t address,
369  const uint32_t length)
370 {
371  bool result = false;
372  if ((length > 0U) && FlashCheckIfDataFits(address, length))
373  {
374  // Message: A conversion should not be performed between a pointer
375  // to object and an integer type [misra-c2012-11.4]
376  // Reason: The memcmp function expects its arguments as pointers,
377  // meanwhile the address is contained in an integer.
378  // Risk: Conversion of an integer into a pointer to void may
379  // result in a pointer that is not correctly aligned,
380  // resulting in undefined behavior.
381  // Prevention: Code reviews.
382  // cppcheck-suppress [misra-c2012-11.4]
383  result = (memcmp(data, (const uint8_t*)address, length) == 0);
384  }
385  return result;
386 }
387 
388 uint32_t FlashRead(uint8_t* const data,
389  const uint32_t address,
390  const uint32_t length)
391 {
392  uint32_t readBytes = 0U;
393  if ((length > 0U) && FlashCheckIfDataFits(address, length))
394  {
395  // Message: A cast shall not be performed between pointer to void
396  // and an arithmetic type [misra-c2012-11.6]
397  // Reason: The memcpy function expects its argument(s) as pointer
398  // to void, meanwhile the address is contained in an
399  // integer.
400  // Risk: Conversion of an integer into a pointer to void may
401  // result in a pointer that is not correctly aligned,
402  // resulting in undefined behavior.
403  // Prevention: Code reviews.
404  // cppcheck-suppress [misra-c2012-11.6]
405  // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
406  (void)memcpy((void*)data, (void*)address, length);
407  readBytes = length;
408  }
409  return readBytes;
410 }
411 
412 uint32_t FlashWrite(const uint8_t* const data,
413  const uint32_t address,
414  const uint32_t length)
415 {
416  size_t writtenBytes = 0U;
417 
418  if ((length > 0U) && FlashCheckIfDataFits(address, length))
419  {
420  // Write to flash only if the flash contents do not match with the
421  // contents of the buffer to be written
422  if (!FlashCompare(data, address, length))
423  {
424  // Unlock flash
425  (void)HAL_FLASH_Unlock();
426 
427  // Erase page(s) and perform writing data into flash
428  if (FlashPerformErase(address, length))
429  {
430  writtenBytes = FlashPerformWrite(data, address, length);
431  }
432 
433  // Lock flash
434  (void)HAL_FLASH_Lock();
435  }
436  else
437  {
438  writtenBytes += length;
439  }
440  }
441 
442  return writtenBytes;
443 }
444 
446 {
447  RccEnableSysCfg();
448  // cppcheck-suppress [misra-c2012-11.4]
449  __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
450 }
451 
452 static bool FlashPerformErase(const uint32_t address, const uint32_t length)
453 {
454  bool result = false;
455 
456  HAL_StatusTypeDef status = HAL_OK;
457  FLASH_EraseInitTypeDef eraseInit = {0U};
458  uint32_t pageError = 0U;
459 
460  const uint32_t bankStart = FlashGetBank(address);
461  const uint32_t bankEnd = FlashGetBank(address + length - 1U);
462  const uint32_t pageStart = FlashGetPage(address);
463  const uint32_t pageEnd = FlashGetPage(address + length - 1U);
464 
465  /* Check if the data spans over both banks */
466  if (bankStart != bankEnd)
467  {
468  /* Erase first bank */
469  eraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
470  eraseInit.Banks = FLASH_BANK_1;
471  eraseInit.Page = pageStart;
472  eraseInit.NbPages = FLASH_PAGE_NB_PER_BANK - pageStart;
473 
474  status = HAL_FLASHEx_Erase(&eraseInit, &pageError);
475  if ((status == HAL_OK) && (pageError == 0xFFFFFFFFU))
476  {
477  /* Erase second bank */
478  eraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
479  eraseInit.Banks = FLASH_BANK_2;
480  eraseInit.Page = 0U;
481  eraseInit.NbPages = pageEnd + 1U;
482 
483  status = HAL_FLASHEx_Erase(&eraseInit, &pageError);
484  if ((status == HAL_OK) && (pageError == 0xFFFFFFFFU))
485  {
486  result = true;
487  }
488  }
489  }
490  else
491  {
492  /* Erase pages in a single bank */
493  eraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
494  eraseInit.Banks = bankStart;
495  eraseInit.Page = pageStart;
496  eraseInit.NbPages = pageEnd - pageStart + 1U;
497 
498  status = HAL_FLASHEx_Erase(&eraseInit, &pageError);
499  if ((status == HAL_OK) && (pageError == 0xFFFFFFFFU))
500  {
501  result = true;
502  }
503  }
504 
505  return result;
506 }
507 
508 static uint32_t FlashPerformWrite(const uint8_t* const data,
509  const uint32_t address,
510  const uint32_t length)
511 {
512  uint32_t writtenBytes = 0U;
513 
514  for (uint32_t i = 0U; i < length; (i = i + 8U))
515  {
516  HAL_StatusTypeDef status = HAL_OK;
517  uint64_t dataChunkValue = 0U;
518  // The length of the data buffer might not be the multiplication of 8
519  // (bytes). In this case, buffer overflow must be avoided during the
520  // writing of the last 8-byte chunk.
521  const uint8_t dataChunkSize =
522  ((length - i) < 8U) ? (uint8_t)(length - i) : 8U;
523  // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
524  (void)memcpy(&dataChunkValue, (const void*)&data[i], dataChunkSize);
525 
526  status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (address + i),
527  dataChunkValue);
528 
529  if (status == HAL_OK)
530  {
531  writtenBytes += dataChunkSize;
532  }
533  else
534  {
535  break;
536  }
537  }
538 
539  return writtenBytes;
540 }
541 
542 static uint32_t FlashGetBank(const uint32_t address)
543 {
544  return (address < (FLASH_BASE + FLASH_BANK_SIZE_WRAPPER)) ? FLASH_BANK_1
545  : FLASH_BANK_2;
546 }
547 
548 static uint32_t FlashGetPage(const uint32_t address)
549 {
550  return (address < (FLASH_BASE + FLASH_BANK_SIZE_WRAPPER))
551  ? ((address - FLASH_BASE) / FLASH_PAGE_SIZE)
552  : ((address - (FLASH_BASE + FLASH_BANK_SIZE_WRAPPER)) /
553  FLASH_PAGE_SIZE);
554 }
555 
556 static bool FlashCheckIfPageRangesOverlap(const uint32_t range1Start,
557  const uint32_t range1End,
558  const uint32_t range2Start,
559  const uint32_t range2End)
560 {
561  bool isOverlap = false;
562  if ((range1End > range1Start) && (range2End > range2Start))
563  {
564  isOverlap = ((range1Start <= range2End) && (range2Start <= range1End));
565  }
566  return isOverlap;
567 }
568 
This file contains the MCU internal flash driver function prototypes for read, write,...
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
bool FlashEnableWriteProtection(const uint32_t address, const uint32_t length)
Enable flash write protection for the specified region.
Definition: flash.c:291
#define FLASH_PAGE_NB_PER_BANK
Definition: flash.c:55
bool FlashCompare(const uint8_t *const data, const uint32_t address, const uint32_t length)
Compare buffer content with microcontroller flash content.
Definition: flash.c:367
static bool FlashPerformErase(const uint32_t address, const uint32_t length)
Private helper function to perform the actual erase of the required flash pages.
Definition: flash.c:452
void FlashRemapMemoryToSystemFlash(void)
Remap the microcontroller flash memory to the built-in system flash memory.
Definition: flash.c:445
bool FlashCheckIfWriteProtectionEnabled(const uint32_t address, const uint32_t length)
Check if flash write protection is enabled for the specified region.
Definition: flash.c:147
#define FLASH_SIZE_WRAPPER
Definition: flash.c:47
static uint32_t FlashGetBank(const uint32_t address)
Get the bank where the provided address is located.
Definition: flash.c:542
static uint32_t FlashPerformWrite(const uint8_t *const data, const uint32_t address, const uint32_t length)
Private helper function to perform the actual writing of the provided data into the flash.
Definition: flash.c:508
static uint32_t FlashGetPage(const uint32_t address)
Get the page number where the provided address is located.
Definition: flash.c:548
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
bool FlashCheckIfDataFits(const uint32_t address, const uint32_t length)
Check if an array of data fits into the flash.
Definition: flash.c:141
#define FLASH_BANK_SIZE_WRAPPER
Definition: flash.c:51
void FlashClearErrorFlags(void)
Clear all flash error flags.
Definition: flash.c:133
bool FlashDisableWriteProtection(void)
Disable flash write protection for the application area.
Definition: flash.c:224
static bool FlashCheckIfPageRangesOverlap(const uint32_t range1Start, const uint32_t range1End, const uint32_t range2Start, const uint32_t range2End)
Check if two flash page ranges overlap.
Definition: flash.c:556
void RccEnableSysCfg(void)
Enable the SYSCFG clock.
Definition: rcc.c:122
This file includes the microcontroller-specific HAL header files.
This file contains the RCC (Reset and Clock Control) driver function prototypes for enabling and disa...