Sync betaflight to Gitea
This commit is contained in:
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* Betaflight N6 OpenBootloader — entry, boot decision, and S→NS
|
||||
* hand-off trampoline.
|
||||
*
|
||||
* Loaded by the boot ROM into AXISRAM2 secure at 0x34180400 from XSPI
|
||||
* nor0 0x0 on cold boot, or via USB DFU @FSBL alt during recovery.
|
||||
* The two cases are distinguished by checking nor0 0x0 for the signed
|
||||
* FSBL header magic.
|
||||
*
|
||||
* Boot decision:
|
||||
* - nor0 0x0 has no valid OBL header → recovery DFU (writes nor0 0x0)
|
||||
* - RCC->RSR has IWDGRSTF/LCKRSTF/
|
||||
* WWDGRSTF set → previous BF crashed → DFU
|
||||
* - BF vector table at 0x70100000
|
||||
* invalid → DFU
|
||||
* - otherwise → arm IWDG, BXNS to BF in NS
|
||||
*
|
||||
* The DFU loop itself is supplied by the OBL middleware (CubeN6
|
||||
* OpenBootloader, pulled in via Makefile VPATH and re-entered via
|
||||
* obl_app.c::OBL_run_dfu_*). This file owns the boot decision, IWDG
|
||||
* arming, and the BF jump trampoline. OBL is built with -mcmse so the
|
||||
* CMSIS peripheral pointers (RCC, RIFSC, GPIOx, DBGMCU, RISAF*, …)
|
||||
* resolve to their secure-alias bases automatically.
|
||||
*/
|
||||
|
||||
#include <arm_cmse.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "flash_iface.h"
|
||||
|
||||
/* ---------- Layout constants ---------- */
|
||||
#define XSPI_NOR_BASE 0x70000000U
|
||||
#define BF_VECTOR_BASE 0x70100000U
|
||||
#define BF_SLOT_OFFSET 0x00100000U /* 1 MiB OBL reserve before BF */
|
||||
|
||||
/* "STM2" little-endian — the signed FSBL header magic at offset 0. */
|
||||
#define STM2_MAGIC_LE 0x324D5453U
|
||||
|
||||
/* IWDG window: ~10 s @ LSI 32 kHz / (256 × 1250). BF must refresh from
|
||||
* its scheduler-driven watchdog task before this expires. */
|
||||
#define OBL_IWDG_PRESCALER IWDG_PRESCALER_256
|
||||
#define OBL_IWDG_RELOAD 1250U
|
||||
#define OBL_IWDG_WINDOW 1250U /* window disabled (= reload) */
|
||||
|
||||
IWDG_HandleTypeDef hiwdg;
|
||||
|
||||
/* Forward-declared from obl_app.c. Both run the OBL middleware DFU
|
||||
* loop and never return — they end via NVIC_SystemReset. */
|
||||
__attribute__((noreturn)) void OBL_run_dfu_normal(void);
|
||||
__attribute__((noreturn)) void OBL_run_dfu_recovery(void);
|
||||
|
||||
static void SystemClock_Config(void);
|
||||
extern void HAL_MspInit(void);
|
||||
|
||||
/* ---------- Boot decision helpers ---------- */
|
||||
|
||||
static bool nor0_has_valid_obl(void)
|
||||
{
|
||||
if (!flash_memmap_on()) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t magic = *(volatile uint32_t *)(XSPI_NOR_BASE);
|
||||
return magic == STM2_MAGIC_LE;
|
||||
}
|
||||
|
||||
static bool bf_vector_table_valid(void)
|
||||
{
|
||||
if (!flash_memmap_on()) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t bf_sp = *(volatile uint32_t *)(BF_VECTOR_BASE);
|
||||
const uint32_t bf_pc = *(volatile uint32_t *)(BF_VECTOR_BASE + 4U);
|
||||
|
||||
/* SP must point into AXISRAM (top nibble 0x2). */
|
||||
if ((bf_sp & 0xF8000000U) != 0x20000000U) {
|
||||
return false;
|
||||
}
|
||||
/* Reset_Handler must live in the BF XIP slot (top 12 bits == 0x701)
|
||||
* with the thumb bit set. */
|
||||
if ((bf_pc & 0xFFF00000U) != BF_VECTOR_BASE) {
|
||||
return false;
|
||||
}
|
||||
if ((bf_pc & 1U) == 0U) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- IWDG ---------- */
|
||||
|
||||
static void iwdg_start(void)
|
||||
{
|
||||
hiwdg.Instance = IWDG;
|
||||
hiwdg.Init.Prescaler = OBL_IWDG_PRESCALER;
|
||||
hiwdg.Init.Reload = OBL_IWDG_RELOAD;
|
||||
hiwdg.Init.Window = OBL_IWDG_WINDOW;
|
||||
if (HAL_IWDG_Init(&hiwdg) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
/* IWDG cannot be disabled until system reset. BF must call
|
||||
* HAL_IWDG_Refresh() from its scheduler, or the timeout fires NRST
|
||||
* and OBL routes the next boot to DFU via RSR.IWDGRSTF. */
|
||||
}
|
||||
|
||||
/* ---------- Jump trampoline ---------- */
|
||||
|
||||
/* All-8-CIDs read + write enable for RISAF region CIDCFGR. Equivalent to
|
||||
* RDENC0_Msk | RDENC1_Msk | ... | RDENC7_Msk | WRENC0_Msk | ... | WRENC7_Msk
|
||||
* but kept as a single hex literal because the OR'd form is noisy. */
|
||||
#define RISAF_CIDCFGR_ALL_CIDS_RW 0x00FF00FFUL
|
||||
|
||||
/* Configure one base region of a RISAF instance. `region` is 0-indexed
|
||||
* (the CMSIS RISAF_TypeDef exposes the per-region structs as REG[0..14]).
|
||||
* Per RM §7.5: write STARTR/ENDR/CIDCFGR before CFGR so BREN gates the
|
||||
* region atomically. CIDCFGR opens RDENC[0..7] and WRENC[0..7]. */
|
||||
static inline void risaf_region_config(RISAF_TypeDef *risaf, uint32_t region,
|
||||
uint32_t start, uint32_t end,
|
||||
bool sec)
|
||||
{
|
||||
risaf->REG[region].STARTR = start;
|
||||
risaf->REG[region].ENDR = end;
|
||||
risaf->REG[region].CIDCFGR = RISAF_CIDCFGR_ALL_CIDS_RW;
|
||||
risaf->REG[region].CFGR = (sec ? RISAF_REGx_CFGR_SEC_Msk : 0UL)
|
||||
| RISAF_REGx_CFGR_BREN_Msk;
|
||||
}
|
||||
|
||||
static __attribute__((noreturn)) void jump_to_bf(void)
|
||||
{
|
||||
const uint32_t bf_sp = *(volatile uint32_t *)(BF_VECTOR_BASE);
|
||||
const uint32_t bf_pc = *(volatile uint32_t *)(BF_VECTOR_BASE + 4U);
|
||||
|
||||
HAL_SuspendTick();
|
||||
__disable_irq();
|
||||
|
||||
/* Leave XSPI memory-mapped — BF runs XIP from 0x70100000. */
|
||||
SCB_DisableICache();
|
||||
SCB_DisableDCache();
|
||||
|
||||
/* === Hand off to BF in NS state ===========================
|
||||
*
|
||||
* BF runs as a Non-Secure application; OBL is the only S-state
|
||||
* code on the device. RISAFs must be programmed so the NS CPU
|
||||
* can reach BF's data (AXISRAM1) and code (XSPI memory-mapped)
|
||||
* before BXNS. Boot-ROM default leaves every RISAF region
|
||||
* disabled, which per RM §3.5.7 means "Locations outside any
|
||||
* enabled region belong to the secure OS" — so without explicit
|
||||
* opens, NS access is silently RAZ.
|
||||
*
|
||||
* Order matters: RISAFs are configured AFTER OBL's last XSPI
|
||||
* read (bf_sp/bf_pc above) so OBL's S-state reads still see the
|
||||
* boot-ROM permissive default. Once XSPI's RISAF12 region is
|
||||
* flipped to NS, OBL itself can no longer read XSPI; only BF
|
||||
* (NS) can. AXISRAM2 (where OBL lives) is left at the boot-ROM
|
||||
* default (secure-only) so OBL keeps executing through the BXNS.
|
||||
*
|
||||
* RISAF6 is deliberately not programmed — ST's
|
||||
* Template_Isolation_XIP reference for the equivalent topology
|
||||
* (FSBL → AppliSecure → AppliNS in XSPI) leaves it alone too. */
|
||||
|
||||
/* RISAF peripheral clock — boot ROM leaves AHB3ENR.RISAFEN=0,
|
||||
* which makes every access to RISAF register space stall the bus. */
|
||||
RCC->AHB3ENSR = RCC_AHB3ENSR_RISAFENS;
|
||||
(void)RCC->AHB3ENR;
|
||||
|
||||
/* AXISRAM1 + AXISRAM3..6 clocks — boot ROM only clocks AXISRAM2
|
||||
* (where OBL itself runs). The NS BF lives in AXISRAM1 (data+stack)
|
||||
* and AXISRAM3..6 (D2_RAM); without these clocks enabled, any NS
|
||||
* access to 0x24010000+ or 0x24200000+ bus-faults. Idempotent with
|
||||
* the SystemInit set. */
|
||||
RCC->MEMENSR = RCC_MEMENSR_AXISRAM1ENS | RCC_MEMENSR_AXISRAM2ENS
|
||||
| RCC_MEMENSR_AXISRAM3ENS | RCC_MEMENSR_AXISRAM4ENS
|
||||
| RCC_MEMENSR_AXISRAM5ENS | RCC_MEMENSR_AXISRAM6ENS;
|
||||
(void)RCC->MEMENR;
|
||||
|
||||
/* Slave-side RISAFs — open the resources BF (NS) needs.
|
||||
*
|
||||
* Boot-ROM default: every RISAF region disabled. RM §3.5.7 says a
|
||||
* memory location covered by a RISAF but outside any enabled region
|
||||
* "belongs to the secure OS", i.e., NS access bus-faults. So every
|
||||
* slave-RISAF-protected bank BF touches needs an explicit NS region.
|
||||
*
|
||||
* RISAF3 (AXISRAM2, 1 MiB): region offset 0x91000..0xFFFFF NS
|
||||
* (= 0x24191000..0x241FFFFF, 444 KiB)
|
||||
* — BF's data/bss/stack/.ram_code. The
|
||||
* low half stays uncovered → S-only →
|
||||
* OBL's RAM at 0x34180400+ is protected.
|
||||
* RISAF12 (XSPI2, 256 MiB): region covers offsets 0x100000..end
|
||||
* NS, leaving the OBL slot at offsets
|
||||
* 0x00000..0xFFFFF uncovered. Per RM
|
||||
* §3.5.7 an uncovered range defaults
|
||||
* to S-only, so accidental NS deref of
|
||||
* 0x70000000..0x700FFFFF bus-faults
|
||||
* instead of returning OBL image bytes.
|
||||
*
|
||||
* Intentionally NOT programmed:
|
||||
* - RISAF2 (AXISRAM1, 1 MiB): NS access to this bank silently RAZ's
|
||||
* despite a correctly-programmed RISAF2 region 0 — root cause
|
||||
* unknown after exhaustive diagnostic (boot-ROM RISAF2 left at
|
||||
* default, RIFSC SEC bits clear, IASR clean, CIDCFGR opens CID 1
|
||||
* which the M55 presents). Empirical workaround: BF moved to
|
||||
* AXISRAM2 high half (above OBL's 64 KiB block) via RISAF3, which
|
||||
* matches the ST template pattern of "S app in AXISRAM1, NS app
|
||||
* in AXISRAM2".
|
||||
* - AXISRAM3..6 (NPURAM0..3, 0x24200000..0x243BFFFF, 1792 KiB):
|
||||
* no slave RISAF exists for this range; bank security is gated by
|
||||
* RIFSC->RISC_SECCFGRx[5] bits 17..20, which system_stm32n6xx_obl.c
|
||||
* clears in its RIFSC open loop. NS access works once enabled. */
|
||||
/* RISAF3 region 0 (AXISRAM2 NS slice for BF's data/bss/stack/.ram_code).
|
||||
* BF lives in AXISRAM2 above the 64 KiB OBL block — RAM origin at
|
||||
* 0x24191000, length 444 KiB. RISAF3 granularity is 4 KiB so the
|
||||
* region is offset 0x91000..0xFFFFF (relative to AXISRAM2 base
|
||||
* 0x24100000). The lower half of AXISRAM2 (offsets 0x00000..0x90FFF,
|
||||
* which includes OBL's secure RAM) stays outside any enabled region
|
||||
* and defaults to S-only per RM §3.5.7 — protecting OBL from any
|
||||
* stray BF NS reach into its address space.
|
||||
*
|
||||
* RISAF2 (AXISRAM1) and RISAF6 are intentionally not programmed —
|
||||
* BF doesn't use AXISRAM1 in this layout, and RISAF6 matches the
|
||||
* ST Template_Isolation_XIP reference. */
|
||||
risaf_region_config(RISAF3, 0U, 0x00091000UL, 0x000FFFFFUL, false);
|
||||
risaf_region_config(RISAF12, 0U, 0x00100000UL, 0x0FFFFFFFUL, false);
|
||||
|
||||
/* === Per-CPU TrustZone setup for the NS application ====
|
||||
*
|
||||
* NSACR (S-only): grant NS code access to FPU coprocessor banks
|
||||
* (CP10 + CP11). Without this, BF's first FPU instruction in NS
|
||||
* state takes a NOCP UsageFault.
|
||||
*
|
||||
* CPACR_NS: NS-bank CPACR is at reset value (FPU access disabled
|
||||
* for NS). BF's __libc_init_array runs before its own systemInit
|
||||
* and may emit FPU instructions (newlib helpers, struct passing).
|
||||
* Enable CP10/CP11 in the NS bank now so the early NS code can run.
|
||||
*
|
||||
* AIRCR.BFHFNMINS: set to 1 so NS HardFault / BusFault / NMI route to
|
||||
* NS handlers. Required for BF to diagnose its own faults via NS
|
||||
* HardFault_Handler. Without this NS faults route to S where OBL has
|
||||
* only Default_Handler (infinite loop) — chip wedges silently. */
|
||||
SCB->NSACR = SCB_NSACR_CP10_Msk | SCB_NSACR_CP11_Msk;
|
||||
SCB_NS->CPACR = (3UL << 20) | (3UL << 22); /* CP10/CP11 full access in NS bank */
|
||||
SCB->AIRCR = (SCB->AIRCR & ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_BFHFNMINS_Msk))
|
||||
| (0x05FAUL << SCB_AIRCR_VECTKEY_Pos)
|
||||
| SCB_AIRCR_BFHFNMINS_Msk;
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* SAU (Security Attribution Unit) — REQUIRED for BXNS to NS code.
|
||||
* Per RM0486 §3.5.1: "at reset, the SAU unilaterally determines
|
||||
* that the entire memory is secure." Without SAU regions defining
|
||||
* NS, BXNS to 0x7010xxxx resolves the destination as S → SecureFault.
|
||||
*
|
||||
* Per-region NS instead of ALLNS=1: with explicit regions, addresses
|
||||
* outside any region stay S-default — which is what OBL needs for
|
||||
* its S-state accesses at 0x34xxxxxx, 0x50xxxxxx, 0x54xxxxxx etc.
|
||||
*
|
||||
* Region 0: AXISRAM1 (0x24000000..0x240FFFFF) NS — BF main RAM
|
||||
* Region 1: AHB/APB NS-alias peripherals (0x40000000..0x4FFFFFFF)
|
||||
* Region 2: XSPI memory-mapped (0x70100000..0x7FFFFFFF) NS — skip
|
||||
* the OBL slot at 0x70000000..0x700FFFFF so a stray NS
|
||||
* deref of an OBL address is rejected by the SAU before
|
||||
* it reaches the bus (matches the RISAF12 region split).
|
||||
* Region 3: AXISRAM3..6 (0x24200000..0x243BFFFF) NS — D2_RAM /
|
||||
* LCD framebuffer. AXISRAM2 NS (0x24100000..0x241FFFFF)
|
||||
* is intentionally NOT covered: nothing in BF uses it,
|
||||
* and leaving it outside any SAU region means a stray
|
||||
* NS access bus-faults instead of reaching the bank.
|
||||
*
|
||||
* RBAR/RLAR are 32-byte granularity (low 5 bits ignored).
|
||||
*
|
||||
* Per UM3234 the boot ROM also configures SAU. Disable the unit and
|
||||
* clear every region (RLAR.ENABLE=0) before programming ours,
|
||||
* otherwise leftover boot-ROM regions ≥ 4 keep their classification
|
||||
* and can shadow what we configure. */
|
||||
{
|
||||
SAU->CTRL = 0;
|
||||
for (uint32_t i = 0; i < 8U; i++) {
|
||||
SAU->RNR = i;
|
||||
SAU->RBAR = 0;
|
||||
SAU->RLAR = 0;
|
||||
}
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
const struct { uint32_t base; uint32_t limit; } regs[] = {
|
||||
/* BF RAM lives in AXISRAM2 NS above OBL's 64 KiB block.
|
||||
* AXISRAM2 lower half + OBL's secure region stays outside
|
||||
* any SAU region → SAU-S by default, blocking any stray
|
||||
* NS deref of OBL's address space. */
|
||||
{ 0x24191000UL, 0x241FFFE0UL }, /* AXISRAM2 high (BF) */
|
||||
{ 0x40000000UL, 0x4FFFFFE0UL }, /* NS peripherals */
|
||||
{ 0x70100000UL, 0x7FFFFFE0UL }, /* XSPI (excl. OBL) */
|
||||
{ 0x24200000UL, 0x243BFFE0UL }, /* AXISRAM3..6 / D2RAM */
|
||||
};
|
||||
for (uint32_t i = 0; i < (uint32_t)(sizeof(regs) / sizeof(regs[0])); i++) {
|
||||
SAU->RNR = i;
|
||||
SAU->RBAR = regs[i].base;
|
||||
SAU->RLAR = regs[i].limit | SAU_RLAR_ENABLE_Msk;
|
||||
}
|
||||
SAU->CTRL = SAU_CTRL_ENABLE_Msk;
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
/* NS VTOR — point BF at its vector table at the XIP slot base. */
|
||||
SCB_NS->VTOR = BF_VECTOR_BASE;
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* Clear every NVIC IRQ enable + pending bit before the hand-off so
|
||||
* no S-side leftover interrupt fires the instant the NS app does
|
||||
* `__enable_irq()`. BF's own startup clears these again; doing it
|
||||
* here protects the transition window. */
|
||||
for (uint32_t i = 0; i < (uint32_t)(sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0])); i++) {
|
||||
NVIC->ICER[i] = 0xFFFFFFFFUL;
|
||||
NVIC->ICPR[i] = 0xFFFFFFFFUL;
|
||||
}
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* Re-target every external IRQ to NS so BF can enable/handle them
|
||||
* through the NS NVIC bank. Reset default is ITNS = 0 (all IRQs
|
||||
* Secure-targeted), which would route every peripheral IRQ (USB,
|
||||
* GPDMA, EXTI, TIMx, …) into S handlers OBL doesn't implement —
|
||||
* typically a tight Default_Handler loop. With NS-targeted IRQs the
|
||||
* NVIC banks the configuration registers to the NS bank so BF's
|
||||
* NVIC_EnableIRQ / SetPriority / SetTargetState calls land where
|
||||
* the IRQ actually fires. */
|
||||
for (uint32_t i = 0; i < (uint32_t)(sizeof(NVIC->ITNS) / sizeof(NVIC->ITNS[0])); i++) {
|
||||
NVIC->ITNS[i] = 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
/* SysTick is a system exception, not an external IRQ — NVIC->ITNS
|
||||
* doesn't cover it. Its NS/S target lives in ICSR.STTNS (bit 24,
|
||||
* Secure-only writable). Without this, BF's NS SysTick fires the
|
||||
* Secure-side SysTick exception, which OBL doesn't implement, so the
|
||||
* exception pends in S forever (NS-side ICSR shows PENDSTSET=1 +
|
||||
* VECTPENDING=15 with VECTACTIVE=0) and BF's NS handler never runs —
|
||||
* uwTick / sysTickUptime stay at 0 and the first HAL_Delay /
|
||||
* delay() call after the BF init that doesn't poll hardware directly
|
||||
* hangs forever. */
|
||||
SCB->ICSR |= SCB_ICSR_STTNS_Msk;
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* CMSIS-compliant ARMv8-M S→NS hand-off.
|
||||
*
|
||||
* `cmse_nonsecure_call` on the function-pointer type makes the
|
||||
* compiler emit the proper BXNS-style branch *and* the AAPCS-CMSE
|
||||
* register-clearing prologue (zero R0-R3 / R12 / FPSCR caller-saved
|
||||
* state, so no Secure data leaks into the NS app). The function-
|
||||
* pointer cast also implicitly clears the LSB of the target so the
|
||||
* branch always transitions S → NS (with LSB=1 BXNS stays in S and
|
||||
* would SecureFault on the NS-attributed XSPI fetch).
|
||||
* __TZ_set_MSP_NS installs BF's stack pointer in the NS bank. */
|
||||
__TZ_set_MSP_NS(bf_sp);
|
||||
|
||||
/* Clear S PRIMASK / FAULTMASK before BXNS. The __disable_irq() above
|
||||
* leaves PRIMASK_S=1; empirically the Cortex-M55 keeps NS exceptions
|
||||
* (SysTick, PendSV, peripheral IRQs targeted NS via ITNS) pending in
|
||||
* the NS-bank ICSR but never delivers them while PRIMASK_S is set —
|
||||
* even though Armv8-M masks are nominally banked per Security state.
|
||||
* Re-enable S-side IRQs so NS exception delivery works post-BXNS. */
|
||||
__enable_irq();
|
||||
__set_FAULTMASK(0);
|
||||
__set_BASEPRI(0);
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
typedef void __attribute__((cmse_nonsecure_call)) (*ns_reset_fn)(void);
|
||||
const ns_reset_fn bf_reset = (ns_reset_fn)(bf_pc & ~1UL);
|
||||
bf_reset();
|
||||
|
||||
while (1) {
|
||||
__NOP();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Entry ---------- */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* RCC->RSR must be sampled before HAL_Init / SystemClock_Config touch
|
||||
* RCC — HAL_RCC_DeInit (called from System_DeInit during
|
||||
* SystemClock_Config) writes RMVF and erases the IWDGRSTF / LCKRSTF /
|
||||
* WWDGRSTF flags the boot decision below depends on. Snapshot the
|
||||
* value here and clear so subsequent boots see a stable state. */
|
||||
const uint32_t saved_rsr = RCC->RSR;
|
||||
RCC->RSR = RCC_RSR_RMVF;
|
||||
(void)RCC->RSR;
|
||||
|
||||
HAL_Init();
|
||||
SystemClock_Config();
|
||||
|
||||
if (!flash_init()) {
|
||||
Error_Handler();
|
||||
}
|
||||
if (!flash_memmap_on()) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
#ifdef OBL_FORCE_RECOVERY
|
||||
/* Bring-up only: SWD-loaded OBL used to refresh nor0 0x0. Skip the
|
||||
* magic check so we always enter Recovery, exposing nor0 from 0x0
|
||||
* for OBL+BF reinstall. The committed signed OBL has this flag
|
||||
* undefined. */
|
||||
(void)nor0_has_valid_obl;
|
||||
OBL_run_dfu_recovery();
|
||||
/* unreachable */
|
||||
#endif
|
||||
|
||||
if (!nor0_has_valid_obl()) {
|
||||
OBL_run_dfu_recovery();
|
||||
/* unreachable */
|
||||
}
|
||||
|
||||
/* Reset-cause boot decision. RCC->RSR is the only on-chip primitive
|
||||
* that survives any reset short of POR/BOR — its flags are cleared
|
||||
* only by RMVF (handled above) or POR, and persist across NRST /
|
||||
* SRST / SYSRESETREQ. IWDGRSTF / LCKRSTF / WWDGRSTF set means the
|
||||
* previous boot ended with a crash (watchdog timeout, CPU lockup,
|
||||
* or window WD); route to DFU instead of re-launching the broken
|
||||
* BF in a loop. */
|
||||
const uint32_t crash_flags = RCC_RSR_IWDGRSTF | RCC_RSR_LCKRSTF | RCC_RSR_WWDGRSTF;
|
||||
const bool was_crash_reset = (saved_rsr & crash_flags) != 0U;
|
||||
|
||||
#ifdef OBL_FORCE_DFU
|
||||
/* Bring-up override: SWD-load OBL with this flag set when we want
|
||||
* DFU mode regardless of nor0 contents — used to flash a fresh BF
|
||||
* via dfu-util on top of an existing valid (but stale) BF. */
|
||||
OBL_run_dfu_normal();
|
||||
/* unreachable */
|
||||
#endif
|
||||
|
||||
if (was_crash_reset || !bf_vector_table_valid()) {
|
||||
OBL_run_dfu_normal();
|
||||
/* unreachable */
|
||||
}
|
||||
|
||||
iwdg_start();
|
||||
jump_to_bf();
|
||||
}
|
||||
|
||||
/* ---------- Plumbing ---------- */
|
||||
|
||||
void Error_Handler(void)
|
||||
{
|
||||
__disable_irq();
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Tear the clock tree back to reset defaults before SystemClock_Config_Impl
|
||||
* runs OscConfig. Boot ROM's FSBL-load path leaves PLL1 active and the
|
||||
* CPU sourced from it, which makes HAL_RCC_OscConfig fail. Also resolves
|
||||
* the OpenBootloader_DeInit reference in CubeN6's app_openbootloader.c. */
|
||||
void System_DeInit(void)
|
||||
{
|
||||
HAL_RCC_DeInit();
|
||||
}
|
||||
|
||||
extern void SystemClock_Config_Impl(void);
|
||||
static void SystemClock_Config(void)
|
||||
{
|
||||
SystemClock_Config_Impl();
|
||||
|
||||
/* Re-assert DBGMCU.CR after the clock-tree reconfig — the brief
|
||||
* debug-clock outage during the CPU-clock switch can drop the
|
||||
* bits. Same value as in SystemInit. */
|
||||
DBGMCU->CR = DBGMCU_CR_DBGCLKEN
|
||||
| DBGMCU_CR_DBG_SLEEP
|
||||
| DBGMCU_CR_DBG_STOP
|
||||
| DBGMCU_CR_DBG_STANDBY;
|
||||
(void)DBGMCU->CR;
|
||||
}
|
||||
Reference in New Issue
Block a user