/** * @file main.c * @brief CAIIC minimal bootloader: bootsetting arbitration, image CRC32 * check, vector remap and jump to the active application bank. * * No interrupts, no peripherals: flash is read through the memory map only. * If nothing valid is found the CPU stays here forever (recover via SWD). */ #include #include "n32wb03x.h" #include "dfu_layout.h" #include "boot_crc32.h" /** * @brief Sanity check of an app vector table at @p base. * @return 1 if MSP points into SRAM and the reset vector into the bank. */ static int bank_vector_ok(uint32_t base) { uint32_t msp = *(volatile uint32_t*)base; uint32_t reset = *(volatile uint32_t*)(base + 4) & ~1u; return ((msp & 0xFFFF0000u) == 0x20000000u) && (reset >= base) && (reset < base + CAIIC_APP_BANK_SIZE); } /** * @brief Check a bank image against its bootsetting CRC32 record. */ static int bank_crc_ok(const caiic_bank_t* bank, uint32_t base) { if (bank->size == 0 || bank->size > CAIIC_APP_BANK_SIZE) { return 0; } return bank->crc32 == caiic_crc32((const uint8_t*)base, bank->size); } /** * @brief Remap the vector table to the app and jump to it. Never returns. */ static void jump_to_app(uint32_t base) { typedef void (*app_entry_t)(void); __disable_irq(); PWR->VTOR_REG = CAIIC_VTOR_EN | base; /* Cortex-M0 has no SCB->VTOR */ __set_MSP(*(volatile uint32_t*)base); ((app_entry_t)(*(volatile uint32_t*)(base + 4)))(); for (;;) { } } /** * @brief Main program. */ int main(void) { const caiic_bootsetting_t* bs = (const caiic_bootsetting_t*)CAIIC_BOOTSETTING_ADDR; uint32_t target = 0; if (bs->magic == CAIIC_BOOT_MAGIC && bs->crc32 == caiic_crc32((const uint8_t*)bs, offsetof(caiic_bootsetting_t, crc32))) { /* Bootsetting valid: jump to the active bank if its image checks out */ if (bs->active_bank == CAIIC_ACTIVE_BANK1 && bank_crc_ok(&bs->bank1, CAIIC_APP1_BASE)) { target = CAIIC_APP1_BASE; } else if (bs->active_bank == CAIIC_ACTIVE_BANK2 && bank_crc_ok(&bs->bank2, CAIIC_APP2_BASE)) { target = CAIIC_APP2_BASE; } /* Active bank corrupted: fall back to any bank with a valid image */ else if (bank_crc_ok(&bs->bank1, CAIIC_APP1_BASE)) { target = CAIIC_APP1_BASE; } else if (bank_crc_ok(&bs->bank2, CAIIC_APP2_BASE)) { target = CAIIC_APP2_BASE; } } else { /* Blank/invalid bootsetting (first flash): vector sanity check only */ if (bank_vector_ok(CAIIC_APP1_BASE)) { target = CAIIC_APP1_BASE; } else if (bank_vector_ok(CAIIC_APP2_BASE)) { target = CAIIC_APP2_BASE; } } if (target != 0) { jump_to_app(target); } for (;;) { } }