Last updated: 2026-04-03
Purpose: Document the design decisions in eBootloader relative to mcuboot — what was adopted, what was intentionally not adopted, and why.
mcuboot is the de facto open-source secure bootloader for microcontrollers, originally developed for Apache Mynewt and Zephyr. eBootloader draws inspiration from mcuboot's proven security patterns while making architectural decisions tailored to multi-architecture support, operational recovery, and staged boot.
mcuboot pattern: Image metadata (hash, signature, key ID, dependencies) is stored as Type-Length-Value (TLV) entries appended after the image payload.
eBootloader adoption: The image header (eos_image_header_t) uses a fixed-layout header with reserved fields for future TLV extension. The sign_image.py tool supports TLV generation for key hash and dependency information.
| TLV Type | mcuboot | eBootloader |
|---|---|---|
| SHA-256 hash | IMAGE_TLV_SHA256 (0x10) |
hash[32] field in fixed header |
| Key hash | IMAGE_TLV_KEYHASH (0x01) |
Planned — TLV extension after header |
| Ed25519 signature | IMAGE_TLV_ED25519 (0x24) |
signature[64] field in fixed header |
| Security counter | IMAGE_TLV_SEC_CNT (0x50) |
image_version field + dedicated counter |
Rationale: A fixed-layout header with known offsets simplifies stage-0 parsing (minimal code) and enables constant-time field access. TLV is used for optional/extensible metadata only.
mcuboot pattern: First compute the hash of the image payload, then verify the signature over the hash. This separates integrity from authenticity.
eBootloader adoption: Identical approach:
Step 1: eos_image_verify_integrity()
→ Compute SHA-256 over payload
→ Compare against header hash field
→ FAIL → reject image (integrity violation)
Step 2: eos_image_verify_signature()
→ Verify Ed25519 signature over header hash
→ FAIL → reject image (authenticity violation)
Rationale: This is a well-established pattern that enables:
- Early rejection of corrupted images without expensive signature verification
- Separate integrity and authenticity error reporting
- Compatibility with CRC-only mode (Phase 1) where signature verification is absent
mcuboot pattern: Instead of embedding the full public key in the image, mcuboot stores a SHA-256 hash of the public key in the TLV. The bootloader holds the actual public key and compares hashes to select which key to use for verification.
eBootloader adoption: Planned for Phase 2. The image will carry a key hash identifying which key slot was used for signing. The bootloader compares this hash against its embedded public key hashes to select the correct verification key.
Benefit: Enables efficient multi-key support — the bootloader can determine which of its key slots to use without attempting verification with each key.
mcuboot pattern: A monotonic security counter is stored in the image TLV (IMAGE_TLV_SEC_CNT). The bootloader compares it against a device-side counter and rejects images with a lower counter.
eBootloader adoption: Monotonic counter stored in OTP/eFuse or dedicated flash sector. See Key Lifecycle — §6.
mcuboot approach: By default, mcuboot uses a swap-based upgrade strategy. When a new image is placed in the secondary slot, mcuboot swaps the primary and secondary slot contents sector-by-sector. This allows automatic revert if the new image fails to boot.
eBootloader decision: Not adopted.
Reasons:
| Factor | Swap-Based (mcuboot) | Direct-Write A/B (eBootloader) |
|---|---|---|
| Flash wear | 3× write amplification (read A, read B, write A, write B, write scratch) | 1× write (write to inactive slot) |
| Boot time | Slow first boot after upgrade (swap takes seconds) | Instant — no data movement at boot |
| Scratch area | Requires dedicated scratch partition | Not needed |
| Code complexity | Complex swap state machine with resume-on-interrupt | Simple: select slot, verify, jump |
| Anti-tearing | Swap status tracked in dedicated area | Only boot control block needs anti-tearing |
| Flash size | Requires scratch area (~1 sector minimum) | No overhead |
| Recovery | Automatic revert via swap-back | Rollback by switching active_slot pointer |
eBootloader approach: Direct-write with A/B slot selection. The inactive slot is written by the application. On reboot, stage-1 verifies the candidate image and either boots it (test mode) or falls back to the confirmed slot. No data is moved between slots at boot time.
mcuboot approach: An alternative mode where the secondary slot image is copied to the primary slot, destroying the previous primary image.
eBootloader decision: Not adopted.
Reason: Overwrite-only eliminates rollback capability. eBootloader always maintains two slot copies for resilience.
mcuboot approach: Both slots contain fully position-independent images. mcuboot selects the slot to execute from without copying.
eBootloader decision: Partially adopted. eBootloader's A/B slots use fixed load addresses, but images are linked for their specific slot address. The boot manager selects which slot to jump to — similar to direct-XIP but with fixed addressing rather than position-independent code.
| Aspect | mcuboot | eBootloader |
|---|---|---|
| Flash interface | flash_area API (flash_area_open, flash_area_read, etc.) |
eos_board_ops_t vtable with function pointers |
| Registration | Compile-time via flash_map configuration |
Runtime via eos_hal_init(&board_ops) |
| Multi-board | Board selection via Kconfig/CMake | Board selection via board_get_ops() at runtime |
| Watchdog | Not managed by mcuboot | eos_board_ops_t.watchdog_init/feed |
| UART | Not part of mcuboot HAL | eos_board_ops_t.uart_init/send/recv for recovery |
| Reset control | Not part of mcuboot HAL | eos_board_ops_t.system_reset, get_reset_reason |
| Jump/handoff | Direct function call to app entry | eos_board_ops_t.jump with MSP setup via eos_hal_set_msp() |
Rationale: The vtable approach allows a single eBootloader binary to support multiple boards via runtime board detection (useful for SoC families with board variants). It also integrates recovery, watchdog, and UART into the same HAL — mcuboot delegates these to the underlying RTOS.
| Architecture | mcuboot | eBootloader |
|---|---|---|
| ARM Cortex-M (M0, M3, M4, M7, M33) | ✅ Primary target | ✅ Supported |
| ARM Cortex-A (A53, A72) | ❌ | ✅ Supported |
| ARM Cortex-R (R5F) | ❌ | ✅ Supported |
| RISC-V (RV32, RV64) | ✅ (limited) | ✅ Supported |
| x86 / x86_64 | ❌ | ✅ Supported |
| Xtensa | ❌ | ✅ Supported |
| PowerPC | ❌ | ✅ Platform enum defined |
| SPARC, M68K, SH4, MIPS, etc. | ❌ | ✅ Platform enum defined |
eBootloader's eos_platform_t enum in eos_hal.h defines 24 platform targets. The eos_hal_set_msp() inline function already includes architecture-specific assembly for ARM Cortex-M, Cortex-A/R, AArch64, and RISC-V.
| Aspect | mcuboot | eBootloader |
|---|---|---|
| Stage count | 2 (ROM → mcuboot → app) | 3 (ROM → stage-0 → stage-1 → app) |
| First-stage size | ~32–64 KB typical | 16 KB (stage-0) + 48 KB (stage-1) |
| Stage-0 purpose | N/A | Minimal init, recovery check, stage-1 verification |
| Recovery | External tooling | Built-in UART recovery with 9-command protocol |
Rationale: The two-stage split (stage-0 + stage-1) keeps the root-of-trust code surface minimal (16 KB). Stage-0 can be locked in flash and rarely updated, while stage-1 can be updated more frequently with new boot policies. mcuboot is a single monolithic bootloader.
| Aspect | mcuboot | eBootloader |
|---|---|---|
| Built-in recovery | ❌ No | ✅ UART command protocol (9 commands) |
| Recovery trigger | N/A | Hardware pin (recovery_pin_asserted) or boot control flag |
| Remote recovery | Depends on app/RTOS | UART: PING, INFO, ERASE, WRITE, VERIFY, BOOT, LOG, RESET, FACTORY |
| Brick recovery | External programmer (JTAG/SWD) | UART recovery before JTAG is needed |
| Security Feature | mcuboot | eBootloader | Notes |
|---|---|---|---|
| Image integrity | SHA-256 in TLV | SHA-256 in header (Phase 2), CRC32 (Phase 1) | Same algorithm, different metadata format |
| Signature algorithms | RSA-2048/3072, ECDSA-P256, Ed25519 | Ed25519 (primary), ECDSA (planned) | eBootloader focuses on Ed25519 for size/speed |
| Key storage | Compiled-in public key hash | Compiled-in, OTP/eFuse, or secure element | eBootloader supports more storage options |
| Multi-key support | Up to N keys via key hash TLV | Dual-slot (primary + backup) | Sufficient for rotation; simpler implementation |
| Anti-rollback | Security counter in TLV | Monotonic counter (OTP/flash/SE) | Same concept, different storage |
| Payload encryption | AES-256-CTR, AES-256-CBC, ECIES-P256+AES | AES-256-GCM (Phase 3) | eBootloader uses authenticated encryption |
| Swap protection | Swap status tracking | Not applicable (no swap) | eBootloader avoids swap complexity |
| Debug lock | Not managed | Stage-0 applies debug lock in production | Integrated into boot chain |
| Recovery auth | Not applicable | Challenge-response (Phase 3) | eBootloader authenticates recovery sessions |
| Boot logging | Minimal | Dedicated boot log region (8 KB) | Full audit trail of boot decisions |
| Hardware dependencies | Requires RTOS flash driver | Bare-metal HAL vtable | eBootloader has no RTOS dependency |
| Scenario | Recommended | Reason |
|---|---|---|
| Zephyr/Mynewt project on Cortex-M | mcuboot | Native integration, community support |
| Multi-arch project (ARM + RISC-V + x86) | eBootloader | Broader platform support |
| Bare-metal with no RTOS | eBootloader | No RTOS dependency; self-contained HAL |
| Need built-in UART recovery | eBootloader | 9-command recovery protocol included |
| Need swap-based upgrade with scratch | mcuboot | Mature swap implementation |
| Need minimal flash overhead | eBootloader | No scratch partition; A/B direct-write |
| Need boot audit logging | eBootloader | Dedicated boot log region |
| Existing mcuboot deployment | mcuboot | Migration cost outweighs benefits |