/** * @file main.c * @brief CAIIC minimal bootloader: bootsetting arbitration, vector sanity * check, vector remap and jump to the active application bank. * * No interrupts, no peripherals: flash is read through the memory map only. * * Policy: the application image CRC is verified during the OTA process * (app_ota.c) BEFORE bootsetting is switched - boot itself never validates * image contents, it only trusts the bootsetting sector integrity * (magic + own CRC32) to pick the active bank, then jumps straight to it. * If the chosen vector table looks dead 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 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 = CAIIC_APP1_BASE; /* default bank */ /* Pick the active bank. Only the bootsetting sector integrity is * checked (magic + its own CRC32); the app image itself is NOT * verified here - it was already verified at OTA time before this * record was written. */ if (bs->magic == CAIIC_BOOT_MAGIC && bs->crc32 == caiic_crc32((const uint8_t*)bs, offsetof(caiic_bootsetting_t, crc32)) && bs->active_bank == CAIIC_ACTIVE_BANK2) { target = CAIIC_APP2_BASE; } /* Last-resort sanity: never jump into a blank/garbage vector table */ if (bank_vector_ok(target)) { jump_to_app(target); } for (;;) { } }