$npx -y skills add mohitmishra786/low-level-dev-skills --skill bootloaders-embeddedEmbedded bootloader skill for firmware update and app handoff. Use when writing a custom bootloader, jumping to application code, relocating VTOR, or implementing DFU/USB firmware update on Cortex-M. Activates on queries about bootloader jump, vector table relocation, application
| 1 | # Embedded Bootloaders |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | Guide agents through embedded bootloader fundamentals: vector table relocation, safe handoff from bootloader to application, flash partitioning, and basic firmware-update patterns (UART, USB DFU, or custom protocol) on Cortex-M and similar MCUs. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Application must run at non-zero flash offset (e.g. `0x08010000`) |
| 10 | - Implementing OTA or USB DFU without vendor HAL |
| 11 | - Debugging "app works when flashed alone but not via bootloader" |
| 12 | - Integrating with `skills/baremetal/baremetal-startup` and `skills/baremetal/stm32-baremetal` |
| 13 | |
| 14 | ## Workflow |
| 15 | |
| 16 | ### 1. Memory layout (typical STM32) |
| 17 | |
| 18 | | Region | Address | Size | Content | |
| 19 | |--------|---------|------|---------| |
| 20 | | Bootloader | `0x08000000` | 16–64 KB | BL code, update logic | |
| 21 | | Application | `0x08010000` | remainder | App vector + code | |
| 22 | |
| 23 | Linker script for app must set `FLASH ORIGIN` to app base; vector table must live at app base. |
| 24 | |
| 25 | ### 2. Valid application image check |
| 26 | |
| 27 | Before jump, verify: |
| 28 | |
| 29 | ``` |
| 30 | App vector[0] (initial SP) points into RAM region |
| 31 | App vector[1] (Reset) points into flash region and has Thumb bit set (LSB=1) |
| 32 | Optional: CRC or magic word in app metadata section |
| 33 | ``` |
| 34 | |
| 35 | ```c |
| 36 | #define APP_BASE 0x08010000U |
| 37 | |
| 38 | static int app_valid(uint32_t base) |
| 39 | { |
| 40 | uint32_t sp = *(uint32_t *)base; |
| 41 | uint32_t reset = *(uint32_t *)(base + 4); |
| 42 | if (sp < SRAM_BASE || sp > SRAM_END) |
| 43 | return 0; |
| 44 | if ((reset & 1U) == 0U) |
| 45 | return 0; |
| 46 | if (reset < base || reset > FLASH_END) |
| 47 | return 0; |
| 48 | return 1; |
| 49 | } |
| 50 | ``` |
| 51 | |
| 52 | ### 3. Cortex-M handoff sequence |
| 53 | |
| 54 | ```c |
| 55 | typedef void (*app_entry_t)(void); |
| 56 | |
| 57 | void jump_to_app(uint32_t app_base) |
| 58 | { |
| 59 | uint32_t sp = *(uint32_t *)app_base; |
| 60 | uint32_t reset = *(uint32_t *)(app_base + 4); |
| 61 | |
| 62 | /* Disable interrupts and de-init peripherals/boot-owned hardware */ |
| 63 | __disable_irq(); |
| 64 | SysTick->CTRL = 0; |
| 65 | for (int i = 0; i < 8; i++) { |
| 66 | NVIC->ICER[i] = 0xFFFFFFFFU; |
| 67 | NVIC->ICPR[i] = 0xFFFFFFFFU; |
| 68 | } |
| 69 | |
| 70 | SCB->VTOR = app_base; |
| 71 | __set_MSP(sp); |
| 72 | __DSB(); |
| 73 | __ISB(); |
| 74 | |
| 75 | app_entry_t entry = (app_entry_t)reset; |
| 76 | entry(); /* does not return */ |
| 77 | } |
| 78 | ``` |
| 79 | |
| 80 | Application must set `SCB->VTOR = APP_BASE` early in `Reset_Handler` if startup assumes relocatable vector table. |
| 81 | |
| 82 | ### 4. Bootloader responsibilities |
| 83 | |
| 84 | ``` |
| 85 | Power-on |
| 86 | ├── Init minimal clock + UART/USB for update |
| 87 | ├── Check update flag in RTC backup / GPIO strap |
| 88 | ├── If update requested → receive image, verify, program flash |
| 89 | └── Else if valid app → jump_to_app() |
| 90 | └── Else stay in bootloader shell |
| 91 | ``` |
| 92 | |
| 93 | ### 5. STM32 system memory DFU (factory ROM) |
| 94 | |
| 95 | STM32 chips expose USB DFU in system memory when BOOT0=1. Custom bootloaders are separate — do not confuse ROM DFU with user flash BL. |
| 96 | |
| 97 | ### 6. Update safety |
| 98 | |
| 99 | - Write to scratch sector, verify CRC, then swap metadata pointer (A/B) |
| 100 | - Never erase the only valid image without recovery path |
| 101 | - Reset watchdog only after verified commit |
| 102 | |
| 103 | ### 7. Agent usage |
| 104 | |
| 105 | ``` |
| 106 | /bootloaders-embedded Write STM32F4 jump-to-app at 0x08010000 with VTOR setup |
| 107 | ``` |
| 108 | |
| 109 | ## Common Problems |
| 110 | |
| 111 | | Symptom | Cause | Fix | |
| 112 | |---------|-------|-----| |
| 113 | | HardFault after jump | SP invalid or Thumb bit missing | Validate vectors; ensure `reset \| 1` | |
| 114 | | IRQs hit bootloader handlers | VTOR not relocated | Set `SCB->VTOR` before enabling IRQs | |
| 115 | | App OK standalone, fails via BL | Linker still at `0x08000000` | Relink app with correct ORIGIN | |
| 116 | | UART garbage after jump | BL left UART running | De-init or reset peripherals | |
| 117 | | Brick after OTA | Power loss mid-erase | Dual-bank or metadata rollback | |
| 118 | |
| 119 | ## Related Skills |
| 120 | |
| 121 | - `skills/baremetal/baremetal-startup` — vector table and Reset_Handler |
| 122 | - `skills/baremetal/stm32-baremetal` — flash map and CMSIS |
| 123 | - `skills/baremetal/interrupts-and-exceptions-baremetal` — NVIC disable pattern |
| 124 | - `skills/embedded/linker-scripts` — VMA/LMA for split images |
| 125 | - `skills/embedded/openocd-jtag` — recover bricked flash |