Back to skills
SKILL.md
Bootloader Patterns
ASecurityBootloader patterns for STM32, nRF52, and SAM parts: boot decision tree, RTC backup register boot flags, dual-bank flash layout, YMODEM receive over UART, post-flash verification, and watchdog handling during boot. Use when building or reviewing the boot path of a device that updates in the field.
- 50 stars
- 0 votes
- 0 copies
- 1 view
- Added October 1, 2026
Security analysis
100/100npx -y skills add HermeticOrmus/LibreEmbed-Claude-Code --skill bootloader-patterns --agent claude-codeAre you the author of Bootloader Patterns?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/hermeticormus-bootloader-patterns)---
name: "bootloader-patterns"
description: "Bootloader patterns for STM32, nRF52, and SAM parts: boot decision tree, RTC backup register boot flags, dual-bank flash layout, YMODEM receive over UART, post-flash verification, and watchdog handling during boot. Use when building or reviewing the boot path of a device that updates in the field."
---
# bootloader-patterns
## Knowledge Base
Production bootloader patterns for STM32, nRF52, and SAM MCUs.
---
## Pattern 1: Boot Decision Tree
```
Power-on Reset
│
▼
Read boot flag (RTC backup register or dedicated NVM byte)
│
├── FLAG_UPDATE_PENDING ──► Receive new image via UART/CAN/USB
│ │
│ ▼
│ Write to inactive bank
│ │
│ Verify CRC + signature
│ │
│ Set FLAG_BOOT_NEW, reset
│
├── FLAG_BOOT_NEW ──► Validate image at new bank
│ │
│ ├── OK ─► Clear flag, increment boot counter,
│ │ jump to new image
│ │
│ └── FAIL ─► Increment failure counter
│ │
│ └── > 3 fails ─► Revert to golden
│
└── FLAG_NONE ──► Validate active bank image
│
├── OK ─► Jump to application
│
└── FAIL ─► Fall back to recovery UART mode
```
---
## Pattern 2: RTC Backup Register for Boot Flags
STM32 RTC backup registers survive reset (not power-off). Ideal for bootloader flags.
```c
#define BKP_REG_BOOT_FLAG RTC->BKP0R
#define BOOT_FLAG_NONE 0x00000000UL
#define BOOT_FLAG_UPDATE 0xA5A5A5A5UL
#define BOOT_FLAG_BOOT_NEW 0x5A5A5A5AUL
void bootflag_set(uint32_t flag)
{
/* Enable backup domain access */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP;
BKP_REG_BOOT_FLAG = flag;
PWR->CR &= ~PWR_CR_DBP;
}
uint32_t bootflag_get(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP;
return BKP_REG_BOOT_FLAG;
}
```
---
## Pattern 3: Dual-Bank Flash Layout (STM32F407, 1MB)
```
0x08000000 ┌─────────────────────────┐ Sector 0 (16KB)
│ Primary Bootloader │ Write-protected
│ (golden copy) │
0x08004000 ├─────────────────────────┤ Sector 1 (16KB)
│ Bootloader config │
│ (version NVM, keys) │
0x08008000 ├─────────────────────────┤ Sectors 2-3 (32KB)
│ Application Bank A │ Active
│ (img_header + app) │
0x08040000 ├─────────────────────────┤ Sector 6 (128KB)
│ Application Bank B │ OTA target
│ (img_header + app) │
0x080C0000 ├─────────────────────────┤ Sector 10 (128KB)
│ Application Data │
│ (settings, logs) │
0x08100000 └─────────────────────────┘
```
Linker scripts for application: `ORIGIN = 0x08008080` (0x08008000 + 0x80 for header).
---
## Pattern 4: YMODEM Receive Over UART
YMODEM-1K is the standard for bootloader firmware transfer. 1024-byte packets, CRC-16.
```c
/* Simplified YMODEM receive state machine */
#define YMODEM_SOH 0x01 /* 128-byte packet start */
#define YMODEM_STX 0x02 /* 1024-byte packet start */
#define YMODEM_EOT 0x04 /* End of transmission */
#define YMODEM_ACK 0x06
#define YMODEM_NAK 0x15
#define YMODEM_CAN 0x18
#define YMODEM_C 0x43 /* 'C': request CRC mode */
typedef enum {
YMODEM_WAIT_HEADER,
YMODEM_RECV_DATA,
YMODEM_VERIFY,
YMODEM_DONE,
YMODEM_ERROR
} ymodem_state_t;
/* Host tool: sz --ymodem firmware.bin /dev/ttyUSB0 */
```
Libraries: libxmodem (open-source), or roll your own with 200 lines of C.
---
## Pattern 5: Post-Flash Verification
After writing each page to flash, read it back and compare.
```c
bool flash_verify_region(uint32_t flash_addr, const uint8_t *src, uint32_t len)
{
const uint8_t *flash = (const uint8_t *)flash_addr;
for (uint32_t i = 0; i < len; i++) {
if (flash[i] != src[i]) { return false; }
}
return true;
}
/* Verify full image CRC after writing all pages */
bool bootloader_verify_written_image(uint32_t app_base, uint32_t img_size)
{
const img_header_t *hdr = (const img_header_t *)app_base;
if (hdr->magic != IMAGE_MAGIC) { return false; }
const uint8_t *body = (const uint8_t *)app_base + sizeof(img_header_t);
uint32_t crc = crc32_compute(body, img_size);
return crc == hdr->crc32;
}
```
---
## Pattern 6: Watchdog During Bootloader
Always feed a watchdog during flash write loops. If flash write hangs (bad sector), WDT resets and bootloader recovers.
```c
/* Configure IWDG for 4-second timeout (STM32, LSI ~32kHz) */
void iwdg_init(void)
{
IWDG->KR = 0xCCCC; /* Start IWDG */
IWDG->KR = 0x5555; /* Enable register write */
IWDG->PR = 6U; /* Prescaler /256: 32kHz/256 = 125 Hz */
IWDG->RLR = 500U; /* Reload: 500/125 Hz = 4 seconds */
IWDG->KR = 0xAAAA; /* Reload (arm) */
}
void iwdg_feed(void) { IWDG->KR = 0xAAAA; }
/* In flash write loop: */
for (uint32_t page = 0; page < total_pages; page++) {
flash_write_page(base + page * PAGE_SIZE, buf + page * PAGE_SIZE);
iwdg_feed();
}
```
---
## Anti-Patterns
- **Jumping to app without disabling IRQs**: Application installs its own vector table; pending IRQs from bootloader fire in application context and crash it.
- **Writing new image over running firmware without dual-bank**: Power loss during write = unrecoverable brick.
- **CRC only, no signature**: CRC protects against corruption, not malicious injection. Add ECDSA for production.
- **No boot counter with fallback**: One bad image = permanent brick in the field.
- **Bootloader in non-protected flash**: Application bug can overwrite the bootloader.
## References
- AN3965 (ST): STM32F40x/41x in-application programming using USART
- mcuboot: https://github.com/mcu-tools/mcuboot
- mbedTLS ECDSA: https://mbed-tls.readthedocs.io/
- YMODEM protocol specification by Chuck Forsberg
Attribution
Comments
Loading comments…