mirror of
https://github.com/meshtastic/firmware.git
synced 2026-09-20 21:39:01 -04:00
fix(stm32wl): reset instead of hanging on faults (#11420)
* fix(stm32wl): reset instead of hanging on faults Reset instead of hanging forever on three unrecoverable faults, each of which previously required a manual power cycle to recover: - HardFault_Handler_C: blinked SOS forever with no debugger attached; now resets once the fault registers are printed. - __wrap___assert_func: silently hung on an assert failure; now prints file/line/func/expr via debug_printf, then resets. - earlyBootCheck: silently hung if the jump into the bootloader ROM failed to take; now calls the bare NVIC_SystemReset(), not the HAL wrapper, since it runs pre-HAL_Init() and MSP/VTOR are already repointed at the bootloader by this point, so a return would unwind through a corrupted stack frame. Assisted-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Andrew Yong <me@ndoo.sg> * refactor(stm32wl): group fault-handling code Group the fault-handling code together and drop incidental cruft: - Move __wrap___assert_func next to HardFault_Handler_C and the other fault-reporting helpers. - Add banner comments separating linker-hack wrappers from fault-handling/recovery code, matching the existing Bootloader redirect banner. - Drop the forward declaration for debug_printf, no longer needed now that __wrap___assert_func sits below its definition. - Trim the Bootloader redirect banner comment to 1-3 lines. No behavior change. Assisted-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Andrew Yong <me@ndoo.sg> --------- Signed-off-by: Andrew Yong <me@ndoo.sg>
This commit is contained in:
1 parent
ea1e6b88e0
commit
f960c84f5b
1 file changed
+16
-64
@@ -18,20 +18,9 @@ static bool stm32wlRtcValid = false;
|
||||
#endif
|
||||
|
||||
// ─── Bootloader redirect ──────────────────────────────────────────────────────
|
||||
//
|
||||
// Why .noinit + constructor instead of TAMP backup registers:
|
||||
//
|
||||
// The STM32duino startup sequence initialises clocks which may call
|
||||
// __HAL_RCC_BACKUPRESET_FORCE/RELEASE when configuring the LSE oscillator,
|
||||
// wiping the entire backup domain (including TAMP->BKP0R) before setup()
|
||||
// ever runs. The backup-register approach therefore cannot reliably survive
|
||||
// a soft reset in this toolchain.
|
||||
//
|
||||
// Solution: store the magic in a .noinit SRAM variable.
|
||||
// - NVIC_SystemReset() does NOT clear SRAM.
|
||||
// - The linker script skips zero-init for .noinit sections.
|
||||
// - __attribute__((constructor)) fires before main()/HAL_Init(), so we can
|
||||
// intercept and jump before anything disturbs peripheral state.
|
||||
// Uses .noinit SRAM instead of TAMP backup registers: STM32duino's clock init can wipe the
|
||||
// backup domain via __HAL_RCC_BACKUPRESET_FORCE/RELEASE before setup() runs, but .noinit
|
||||
// survives NVIC_SystemReset() and this constructor fires before HAL_Init() touches anything.
|
||||
|
||||
#define BOOTLOADER_MAGIC 0xD00DB007UL
|
||||
#define SYS_MEM_BASE 0x1FFF0000UL
|
||||
@@ -58,8 +47,10 @@ __attribute__((constructor(101), used)) static void earlyBootCheck(void)
|
||||
SCB->VTOR = SYS_MEM_BASE;
|
||||
__set_MSP(*(volatile uint32_t *)SYS_MEM_BASE);
|
||||
((void (*)(void))(*(volatile uint32_t *)(SYS_MEM_BASE + 4)))();
|
||||
while (1)
|
||||
;
|
||||
// Should never be reached: the bootloader ROM does not return. A bare reset
|
||||
// (rather than returning normally) avoids unwinding through this function's
|
||||
// epilogue, which would restore registers relative to the now-repointed MSP.
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void enterDfuMode()
|
||||
@@ -169,15 +160,7 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Hacks to force more code and data out.
|
||||
|
||||
// By default __assert_func uses fiprintf which pulls in stdio.
|
||||
extern "C" void __wrap___assert_func(const char *, int, const char *, const char *)
|
||||
{
|
||||
while (true)
|
||||
;
|
||||
return;
|
||||
}
|
||||
// ─── Linker hacks to reduce code size ─────────────────────────────────────────
|
||||
|
||||
// By default strerror has a lot of strings we probably don't use. Make it return an empty string instead.
|
||||
char empty = 0;
|
||||
@@ -197,6 +180,8 @@ extern "C" void __wrap__tzset_unlocked_r(struct _reent *reent_ptr)
|
||||
}
|
||||
#endif
|
||||
|
||||
// ─── Fault handling & recovery ────────────────────────────────────────────────
|
||||
|
||||
// Taken from https://interrupt.memfault.com/blog/cortex-m-hardfault-debug
|
||||
typedef struct __attribute__((packed)) ContextStateFrame {
|
||||
uint32_t r0;
|
||||
@@ -233,32 +218,11 @@ static void debug_printf(const char *format, ...)
|
||||
uart_debug_write((uint8_t *)hardfault_message_buffer, min((unsigned int)length, sizeof(hardfault_message_buffer) - 1));
|
||||
}
|
||||
|
||||
// N picked by guessing
|
||||
#define DOT_TIME 1200000
|
||||
static void dot()
|
||||
// By default __assert_func uses fiprintf which pulls in stdio.
|
||||
extern "C" void __wrap___assert_func(const char *file, int line, const char *func, const char *failedexpr)
|
||||
{
|
||||
digitalWrite(LED_POWER, LED_STATE_ON);
|
||||
for (volatile int i = 0; i < DOT_TIME; i++) { /* busy wait */
|
||||
}
|
||||
digitalWrite(LED_POWER, LED_STATE_OFF);
|
||||
for (volatile int i = 0; i < DOT_TIME; i++) { /* busy wait */
|
||||
}
|
||||
}
|
||||
|
||||
static void dash()
|
||||
{
|
||||
digitalWrite(LED_POWER, LED_STATE_ON);
|
||||
for (volatile int i = 0; i < (DOT_TIME * 3); i++) { /* busy wait */
|
||||
}
|
||||
digitalWrite(LED_POWER, LED_STATE_OFF);
|
||||
for (volatile int i = 0; i < DOT_TIME; i++) { /* busy wait */
|
||||
}
|
||||
}
|
||||
|
||||
static void space()
|
||||
{
|
||||
for (volatile int i = 0; i < (DOT_TIME * 3); i++) { /* busy wait */
|
||||
}
|
||||
debug_printf("assert: %s:%d in %s: %s\r\n", file, line, func, failedexpr);
|
||||
HAL_NVIC_SystemReset();
|
||||
}
|
||||
|
||||
// Disable optimizations for this function so "frame" argument
|
||||
@@ -277,17 +241,5 @@ extern "C" __attribute__((optimize("O0"))) void HardFault_Handler_C(sContextStat
|
||||
|
||||
HALT_IF_DEBUGGING();
|
||||
|
||||
// blink SOS forever
|
||||
while (1) {
|
||||
dot();
|
||||
dot();
|
||||
dot();
|
||||
dash();
|
||||
dash();
|
||||
dash();
|
||||
dot();
|
||||
dot();
|
||||
dot();
|
||||
space();
|
||||
}
|
||||
}
|
||||
HAL_NVIC_SystemReset();
|
||||
}
|
||||
Reference in new issue
Block a user