109 lines
2.9 KiB
C
109 lines
2.9 KiB
C
/**
|
|
* @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 <stddef.h>
|
|
#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 (;;)
|
|
{
|
|
}
|
|
}
|