eBoot Developer Documentation v0.1.0

Secure A/B bootloader with CRC-protected boot control, slot management, image verification, rollback support, structured boot logging, and recovery mode. Headers: <eos_bootctl.h>, <eos_slot_manager.h>, <eos_boot_log.h>

📜 Structures & Types

Core data structures used throughout eBoot. Stored in flash, survive resets.

eos_bootctl_t

Persistent boot control block stored in a dedicated flash sector with CRC protection and dual-copy redundancy. All fields are 32-bit aligned for direct flash read/write.

magicuint32_tMust equal EOS_BOOTCTL_MAGIC (0x45425443).
versionuint32_tFormat version. Current: EOS_BOOTCTL_VERSION (1).
active_slotuint32_tCurrently active slot. Cast to eos_slot_t: EOS_SLOT_A(0) or EOS_SLOT_B(1).
pending_slotuint32_tSlot marked for test boot. EOS_SLOT_NONE(0xFF) if none.
confirmed_slotuint32_tLast confirmed-good slot after app calls eos_bootctl_confirm().
boot_attemptsuint32_tConsecutive boot attempts since last confirmation.
max_attemptsuint32_tMax allowed attempts before rollback. Default: 3.
last_reset_reasonuint32_tLast reset cause (eos_reset_reason_t).
flagsuint32_tEOS_FLAG_TEST_BOOT(0x01), RECOVERY(0x02), FACTORY_RESET(0x04), SECURE_BOOT(0x08).
img_a_versionuint32_tFirmware version in Slot A.
img_b_versionuint32_tFirmware version in Slot B.
img_a_crcuint32_tCRC32 of Slot A image.
img_b_crcuint32_tCRC32 of Slot B image.
log_headuint32_tRing buffer write pointer for boot log.
boot_countuint32_tTotal boot count since factory (never reset).
reserved[7]uint32_t[7]Reserved. Must be zero.
crc32uint32_tCRC32 of all preceding fields.

📋 Enums & Constants

eos_slot_t

EOS_SLOT_A0Primary firmware slot.
EOS_SLOT_B1Secondary firmware slot (OTA target).
EOS_SLOT_NONE0xFFNo slot selected.

eos_slot_state_t

EOS_SLOT_EMPTY0Erased, no firmware.
EOS_SLOT_VALID1Verified firmware image.
EOS_SLOT_INVALID2Corrupt or incomplete image.
EOS_SLOT_UPDATING3Being written during OTA.

eos_reset_reason_t

EOS_RESET_POWER_ON0Cold power-on.
EOS_RESET_WATCHDOG1Watchdog expiration.
EOS_RESET_SOFTWARE2Software reset.
EOS_RESET_PIN3External reset pin.
EOS_RESET_BROWNOUT4Brownout detection.

Return Codes

EOS_OK0Success.
EOS_ERR_CRC-1CRC validation failed.
EOS_ERR_FLASH-2Flash operation failed.
EOS_ERR_NO_IMAGE-3No valid image in slot.
EOS_ERR_INVALID-4Invalid parameter.

Boot Log Events

EOS_LOG_BOOT_START0Boot cycle started.
EOS_LOG_SLOT_SELECT1Slot selected for boot.
EOS_LOG_IMG_VERIFY2Image CRC verification passed.
EOS_LOG_IMG_FAIL3Image verification failed.
EOS_LOG_ROLLBACK4Rollback to confirmed slot.
EOS_LOG_RECOVERY5Entered recovery mode.
EOS_LOG_OTA_START6OTA update started.
EOS_LOG_OTA_DONE7OTA update completed.
EOS_LOG_CONFIRM8Slot confirmed as good.

🔒 Boot Control API

Persistent boot state management. Header: <eos_bootctl.h>. All functions operate on eos_bootctl_t stored in flash with CRC protection and dual-copy redundancy.

int eos_bootctl_load(eos_bootctl_t *bctl)

Load the boot control block from flash. Reads the primary copy first; if CRC is invalid, falls back to the backup copy. Typically the first eBoot function called during boot.

Parameters
bctleos_bootctl_t *Pointer to structure to populate. Must not be NULL. All fields overwritten on success.
Returns

EOS_OK (0) on success. EOS_ERR_CRC (-1) if both copies are corrupt. EOS_ERR_FLASH (-2) on flash read failure.

Example
eos_bootctl_t bctl;
int rc = eos_bootctl_load(&bctl);
if (rc == EOS_ERR_CRC) {
    eos_bootctl_init_defaults(&bctl);
    eos_bootctl_save(&bctl);
} else if (rc != EOS_OK) {
    enter_recovery_mode();
}
printf("Active: slot %s, boots: %u\n",
       bctl.active_slot == EOS_SLOT_A ? "A" : "B", bctl.boot_count);
int eos_bootctl_save(eos_bootctl_t *bctl)

Save the boot control block to flash. Computes CRC32, then writes both primary and backup copies. Write order: erase primary, write primary, erase backup, write backup. Power-loss safe: at least one copy survives.

Parameters
bctleos_bootctl_t *Pointer to structure to save. crc32 field is computed by this function.
Returns

EOS_OK (0) on success. EOS_ERR_FLASH (-2) on write/erase failure.

Example
eos_bootctl_t bctl;
eos_bootctl_load(&bctl);
bctl.active_slot = EOS_SLOT_B;
bctl.boot_count++;
if (eos_bootctl_save(&bctl) != EOS_OK) {
    eos_boot_log_append(EOS_LOG_IMG_FAIL, EOS_SLOT_NONE, -2);
}
void eos_bootctl_init_defaults(eos_bootctl_t *bctl)

Initialize with factory defaults: magic=EOS_BOOTCTL_MAGIC, version=1, active_slot=A, max_attempts=3, all counters=0, flags cleared. Does NOT write to flash.

Parameters
bctleos_bootctl_t *Pointer to structure. All fields overwritten.
Returns

None (void).

Example
eos_bootctl_t bctl;
eos_bootctl_init_defaults(&bctl);
eos_bootctl_save(&bctl);   // Persist to flash
eos_boot_log_clear();       // Clear old log
NVIC_SystemReset();         // Reboot fresh
bool eos_bootctl_validate(const eos_bootctl_t *bctl)

Validate CRC of a boot control block in memory. Computes CRC32 over all fields except crc32 and compares. Useful for verifying flash-loaded copies before modifying.

Parameters
bctlconst eos_bootctl_t *Pointer to structure to validate. Not modified.
Returns

true if CRC matches. false if mismatch or NULL.

int eos_bootctl_increment_attempts(eos_bootctl_t *bctl)

Increment boot_attempts by one and save. Called early in boot before jumping to application. If boot_attempts exceeds max_attempts, bootloader should rollback.

Parameters
bctleos_bootctl_t *Pointer to boot control block. boot_attempts incremented and saved.
Returns

EOS_OK on success. EOS_ERR_FLASH on save failure.

Example
eos_bootctl_t bctl;
eos_bootctl_load(&bctl);
eos_bootctl_increment_attempts(&bctl);
if (bctl.boot_attempts > bctl.max_attempts) {
    eos_boot_log_append(EOS_LOG_ROLLBACK, bctl.active_slot, bctl.boot_attempts);
    bctl.active_slot = bctl.confirmed_slot;
    bctl.flags &= ~EOS_FLAG_TEST_BOOT;
    eos_bootctl_reset_attempts(&bctl);
    eos_bootctl_save(&bctl);
}
jump_to_slot(bctl.active_slot);
int eos_bootctl_reset_attempts(eos_bootctl_t *bctl)

Reset boot_attempts to zero and save. Called after successful rollback or slot confirmation.

Parameters
bctleos_bootctl_t *Pointer to boot control block.
Returns

EOS_OK on success. EOS_ERR_FLASH on failure.

int eos_bootctl_set_pending(eos_bootctl_t *bctl, eos_slot_t slot)

Mark a slot for test boot after OTA. On next reset, bootloader boots the pending slot with EOS_FLAG_TEST_BOOT. App must call eos_bootctl_confirm() within max_attempts boots or rollback occurs.

Parameters
bctleos_bootctl_t *Pointer to boot control block.
sloteos_slot_tTarget slot: EOS_SLOT_A (0) or EOS_SLOT_B (1).
Returns

EOS_OK on success. EOS_ERR_INVALID if slot invalid.

Example
// After OTA completes
eos_bootctl_t bctl;
eos_bootctl_load(&bctl);
eos_slot_t target = eos_bootctl_other_slot(bctl.active_slot);
eos_bootctl_set_pending(&bctl, target);
eos_bootctl_save(&bctl);
eos_boot_log_append(EOS_LOG_OTA_DONE, target, new_version);
NVIC_SystemReset();
int eos_bootctl_clear_pending(eos_bootctl_t *bctl)

Clear pending slot after failed verification. Sets pending_slot to EOS_SLOT_NONE and clears test boot flag.

Parameters
bctleos_bootctl_t *Pointer to boot control block.
Returns

EOS_OK on success.

int eos_bootctl_confirm(eos_bootctl_t *bctl)

Confirm active slot as known-good. Application firmware MUST call this after successful self-test to prevent rollback. Clears EOS_FLAG_TEST_BOOT, sets confirmed_slot, resets boot_attempts.

Parameters
bctleos_bootctl_t *Pointer to boot control block.
Returns

EOS_OK on success. EOS_ERR_FLASH on failure.

Example
void app_main(void) {
    eos_bootctl_t bctl;
    eos_bootctl_load(&bctl);
    if (self_test_passed()) {
        eos_bootctl_confirm(&bctl);
        EOS_INFO("Boot confirmed on slot %s",
                 bctl.active_slot == EOS_SLOT_A ? "A" : "B");
    } else {
        EOS_ERROR("Self-test failed - rollback on next reboot");
    }
}
int eos_bootctl_request_recovery(eos_bootctl_t *bctl)

Set EOS_FLAG_RECOVERY. Next boot enters recovery mode (serial/USB reflash interface).

Parameters
bctleos_bootctl_t *Pointer to boot control block.
Returns

EOS_OK on success.

int eos_bootctl_request_factory_reset(eos_bootctl_t *bctl)

Set EOS_FLAG_FACTORY_RESET. Next boot erases user data, reinitializes defaults, and boots Slot A.

Parameters
bctleos_bootctl_t *Pointer to boot control block.
Returns

EOS_OK on success.

eos_slot_t eos_bootctl_other_slot(eos_slot_t slot)

Get the alternate slot. Returns B if given A, A if given B, NONE if invalid.

Parameters
sloteos_slot_tCurrent slot: EOS_SLOT_A(0) or EOS_SLOT_B(1).
Returns

The other slot, or EOS_SLOT_NONE (0xFF) if invalid input.

🗃 Slot Manager API

Firmware slot inspection and management. Header: <eos_slot_manager.h>

int eos_slot_scan_all(void)

Scan all firmware slots, read image headers, validate CRCs, and cache slot states. Must be called before any other slot manager function. Re-scanning is safe and updates cached state.

Returns

Number of valid slots found (0-2), or negative error code on flash failure.

Example
int valid = eos_slot_scan_all();
printf("Found %d valid firmware slots\n", valid);
if (valid == 0) {
    enter_recovery_mode();  // No bootable image
}
bool eos_slot_is_valid(eos_slot_t slot)

Check if a slot contains a valid firmware image (header magic OK, CRC matches). Requires prior eos_slot_scan_all().

Parameters
sloteos_slot_tSlot to check: EOS_SLOT_A or EOS_SLOT_B.
Returns

true if slot has a valid image. false if empty, corrupt, or invalid slot ID.

uint32_t eos_slot_get_version(eos_slot_t slot)

Get firmware version number from a slot's image header. Returns 0 if the slot is empty or invalid.

Parameters
sloteos_slot_tSlot to query.
Returns

Version number (e.g., 0x010200 for v1.2.0), or 0 if invalid.

int eos_slot_get_header(eos_slot_t slot, eos_image_header_t *out)

Read the full image header from a slot. Contains version, size, CRC, build timestamp, and hardware compatibility info.

Parameters
sloteos_slot_tSlot to read from.
outeos_image_header_t *Output buffer for image header.
Returns

EOS_OK on success. EOS_ERR_NO_IMAGE if slot is empty.

Example
eos_image_header_t hdr;
if (eos_slot_get_header(EOS_SLOT_A, &hdr) == EOS_OK) {
    printf("Slot A: v%u.%u.%u, size=%u bytes\n",
           (hdr.version >> 16) & 0xFF,
           (hdr.version >> 8) & 0xFF,
           hdr.version & 0xFF, hdr.image_size);
}
eos_slot_state_t eos_slot_get_state(eos_slot_t slot)

Get the current state of a firmware slot: EMPTY, VALID, INVALID, or UPDATING.

Parameters
sloteos_slot_tSlot to query.
Returns

eos_slot_state_t enum value.

int eos_slot_erase(eos_slot_t slot)

Erase a firmware slot. Sets all bytes to 0xFF. Used before writing a new OTA image.

Parameters
sloteos_slot_tSlot to erase.
Returns

EOS_OK on success. EOS_ERR_FLASH on failure.

📖 Boot Log API

Persistent ring-buffer boot log in flash. Header: <eos_boot_log.h>. Survives resets. Readable by application firmware.

int eos_boot_log_init(void)

Initialize boot log subsystem. Reads log region from flash, validates header, locates write position. Formats with fresh header if corrupt or uninitialized.

Returns

EOS_OK on success. EOS_ERR_FLASH on read failure.

void eos_boot_log_append(uint32_t event, uint32_t slot, uint32_t detail)

Append a timestamped entry to the boot log ring buffer. When full, oldest entry is overwritten. Flushed to flash immediately.

Parameters
eventuint32_tEvent code: EOS_LOG_BOOT_START(0)..EOS_LOG_CONFIRM(8).
slotuint32_tAssociated slot (EOS_SLOT_A, EOS_SLOT_B, or EOS_SLOT_NONE).
detailuint32_tEvent-specific: version number, error code, CRC, etc.
Example
eos_boot_log_init();
eos_boot_log_append(EOS_LOG_BOOT_START, EOS_SLOT_NONE, 0);
eos_boot_log_append(EOS_LOG_SLOT_SELECT, EOS_SLOT_A, 0x010200);
eos_boot_log_append(EOS_LOG_IMG_VERIFY, EOS_SLOT_A, computed_crc);
int eos_boot_log_read(eos_boot_log_entry_t *entries, uint32_t max_count)

Read all boot log entries in chronological order (oldest first).

Parameters
entrieseos_boot_log_entry_t *Output buffer.
max_countuint32_tMax entries buffer can hold.
Returns

Number of entries read, or negative error code.

Example
eos_boot_log_entry_t log[64];
int n = eos_boot_log_read(log, 64);
for (int i = 0; i < n; i++) {
    printf("[%u ms] %s slot=%u detail=0x%08X\n",
           log[i].timestamp,
           eos_boot_log_event_name(log[i].event),
           log[i].slot, log[i].detail);
}
uint32_t eos_boot_log_count(void)

Get number of entries currently stored in the boot log.

Returns

Entry count (0 to EOS_BOOT_LOG_MAX).

int eos_boot_log_clear(void)

Clear all boot log entries. Erases log flash sector and writes fresh header.

Returns

EOS_OK on success. EOS_ERR_FLASH on erase failure.

int eos_boot_log_flush(void)

Flush buffered entries to flash. Call before jumping to application to ensure persistence.

Returns

EOS_OK on success.

int eos_boot_log_get_latest(eos_boot_log_entry_t *entry)

Get the most recent log entry.

Parameters
entryeos_boot_log_entry_t *Output buffer.
Returns

EOS_OK on success. EOS_ERR_NO_IMAGE if log is empty.

const char * eos_boot_log_event_name(uint32_t event)

Convert event code to human-readable string (e.g., "BOOT_START", "ROLLBACK").

Parameters
eventuint32_tEvent code (EOS_LOG_*).
Returns

Static string describing the event, or "UNKNOWN".

🔄 Boot Flow

RESET
  |
  v
eos_boot_log_init()
eos_bootctl_load(&bctl)  --- CRC fail ---> eos_bootctl_init_defaults() ---> save
  |
  v
Check flags: FACTORY_RESET? ---> erase all, init defaults, reboot
             RECOVERY?      ---> enter_recovery_mode()
  |
  v
eos_bootctl_increment_attempts(&bctl)
  |
  v
boot_attempts > max_attempts? ---> ROLLBACK to confirmed_slot
  |                                 log EOS_LOG_ROLLBACK
  v                                 reset attempts, reboot
pending_slot != NONE?
  YES: active_slot = pending_slot, set TEST_BOOT flag
  NO:  use current active_slot
  |
  v
eos_slot_scan_all()
eos_slot_is_valid(active_slot)? ---> NO: try other slot, or recovery
  |
  v
log EOS_LOG_SLOT_SELECT, EOS_LOG_IMG_VERIFY
eos_boot_log_flush()
jump_to_slot(active_slot)
  |
  v
APPLICATION FIRMWARE
  |
  v
self_test_passed()? ---> eos_bootctl_confirm()  // CRITICAL!
                         log EOS_LOG_CONFIRM

🔧 Board Porting Guide

Flash Layout Requirements

eBoot requires the following flash regions configured in your linker script:

RegionSizeDescription
BOOTLOADER32-64 KBeBoot code (starts at 0x08000000 on STM32).
BOOTCTL_PRIMARY4 KB (1 sector)Primary boot control block.
BOOTCTL_BACKUP4 KB (1 sector)Backup boot control block.
BOOT_LOG4 KB (1 sector)Boot log ring buffer (EOS_BOOT_LOG_SECTOR_SIZE).
SLOT_AVariablePrimary firmware image (typically 256KB-1MB).
SLOT_BVariableSecondary firmware image (same size as SLOT_A).

Porting Steps

  1. Define flash addresses in board_config.h: BOOTCTL_FLASH_ADDR, BOOTCTL_BACKUP_ADDR, BOOT_LOG_FLASH_ADDR, SLOT_A_ADDR, SLOT_B_ADDR, SLOT_SIZE.
  2. Implement flash HAL: eos_flash_read(), eos_flash_write(), eos_flash_erase_sector().
  3. Implement jump_to_slot(eos_slot_t slot) — set VTOR, load SP from slot start, jump to reset vector.
  4. Add watchdog initialization before jumping (to catch hung firmware).
  5. Call eos_bootctl_confirm() from your application after self-test passes.
Example: board_config.h
#define BOOTCTL_FLASH_ADDR     0x08010000
#define BOOTCTL_BACKUP_ADDR    0x08011000
#define BOOT_LOG_FLASH_ADDR    0x08012000
#define SLOT_A_ADDR            0x08020000
#define SLOT_B_ADDR            0x08060000
#define SLOT_SIZE              0x00040000  // 256KB per slot

Thread Safety: Boot control functions are NOT thread-safe. All calls should be made from the bootloader main context before the RTOS starts, or from a single application task with appropriate locking.