health record support

This commit is contained in:
cpq committed 2026-09-11 14:09:55 +01:00
1 parent 6814b44b03
commit 1e99974b83
17 files changed
+748 -17

No files matched your search

@@ -133,7 +133,7 @@ int main(void)
/* Initialize all configured peripherals */
MX_GPIO_Init();
(void) MX_ETH_Init; // Don't use that stuff, Mongoose handle Ethernet
MX_ETH_Init();
MX_RNG_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
@@ -1,24 +1,22 @@
# Copyright (c) 2026 Cesanta Software Limited
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
MGDIR = ../../../..
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include -Imongoose
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include
CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c hal.c
SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s
SOURCES += mongoose/mongoose.c
SOURCES += $(MGDIR)/mongoose.c
all build example: firmware.bin
mongoose/mongoose.c mongoose/mongoose.h:
cp ../../../../mongoose.[ch] mongoose/
firmware.elf: cmsis_core cmsis_h7 hal.h link.ld Makefile $(SOURCES) mongoose/mongoose.h
firmware.elf: cmsis_core cmsis_h7 hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
firmware.bin: firmware.elf
@@ -36,7 +34,7 @@ cmsis_h7:
git clone -q -c advice.detachedHead=false --depth 1 -b v1.10.6 https://github.com/STMicroelectronics/cmsis_device_h7 $@
clean:
rm -rf firmware.* cmsis_* mongoose/mongoose.*
rm -rf firmware.* cmsis_*
# Automated remote test. Requires env variable VCON_API_KEY set. See https://vcon.io/automated-firmware-tests/
@@ -23,6 +23,10 @@ SECTIONS {
.eth_ram : { *(.eth_ram .eth_ram*) } > ram_d1 AT > flash
/* Health record. NOLOAD and in unused DTCM, so startup code neither copies
nor zeroes it and the contents survive a warm reset */
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > dtcmram
. = ALIGN(8);
_end = .;
}
+65 -3
View File
@@ -24,10 +24,54 @@ static void log_fn(char ch, void *param) {
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED1);
hal_gpio_toggle(LED2);
}
}
// Fault handler body. Runs in exception context: no printf, no malloc, no
// blocking calls. Records the crash reason and a backtrace into the health
// record, then resets.
// "used" keeps the linker from garbage-collecting this section: the only
// reference is the "b fault_c" branch in the naked handler below
__attribute__((used, noinline)) static void fault_c(uint32_t *sp) {
extern uint8_t _estack; // End of the main RAM region, defined in link.ld
size_t n = 0;
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
// Frame 0 is the faulting PC, frame 1 the caller's LR. Deeper frames come
// from a heuristic stack walk: BL pushes an odd return address that lives
// in flash. A stack word that merely looks like an address shows up as a
// bogus frame when symbolised, which is easy to filter by eye
mg_health_record.backtrace[n++] = sp[6] & ~1U; // Stacked PC
mg_health_record.backtrace[n++] = sp[5] & ~1U; // Stacked LR
for (uint32_t *p = sp + 8;
n < MG_HEALTH_BACKTRACE &&
(uintptr_t) p < (uintptr_t) sp + 4096U && // Bound the scan
(uintptr_t) p < (uintptr_t) &_estack; // Stay in RAM
p++) {
uint32_t v = *p;
if ((v & 1U) && v >= 0x08000000U && v < 0x08000000U + 1024U * 1024U) {
mg_health_record.backtrace[n++] = v & ~1U;
}
}
NVIC_SystemReset();
}
// Common fault entry. EXC_RETURN bit 2 tells which stack was in use:
// 0 = MSP, 1 = PSP. Load the faulting SP into r0 and hand it to fault_c()
__attribute__((naked)) void HardFault_Handler(void) {
__asm volatile(
"tst lr, #4\n\t" // Test EXC_RETURN bit 2
"ite eq\n\t" // If zero, use MSP; else PSP
"mrseq r0, msp\n\t"
"mrsne r0, psp\n\t"
"b fault_c\n\t");
}
// Route the other fault types through the same entry point
void MemManage_Handler(void) __attribute__((alias("HardFault_Handler")));
void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
uint64_t mg_millis(void) {
return hal_get_tick();
}
@@ -70,6 +114,15 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
int main(void) {
hal_clock_init();
// Enable MemManage, BusFault and UsageFault so faults are precise and
// routed to their own handlers instead of escalating to HardFault
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk |
SCB_SHCSR_USGFAULTENA_Msk;
MG_OTA_BOOT_CHECK(); // Must be called after clock init
MG_HEALTH_INIT(); // Must be called after clock init
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
hal_rng_init();
@@ -79,8 +132,17 @@ int main(void) {
hal_gpio_output(LED3);
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
MG_OTA_BOOT_CHECK(); // Must be called after clock init
// Report the previous boot's crash backtrace, if any
if (mg_health_reason() == MG_HEALTH_RESET_FAULT) {
MG_INFO(("Previous boot faulted. Backtrace:"));
for (int i = 0; i < MG_HEALTH_BACKTRACE; i++) {
if (mg_health_record.backtrace[i] == 0) break;
MG_INFO((" #%d 0x%08lx", i,
(unsigned long) mg_health_record.backtrace[i]));
}
// * (volatile uint32_t *) 0 = 123; // Deliberate fault: write to unmapped memory
}
// for (;;) (void) 0;
struct mg_mgr mgr;
mg_mgr_init(&mgr);
@@ -44,6 +44,16 @@
#define MG_OTA_STATE_GET() (RTC->BKP0R)
#define MG_OTA_STATE_SET(v) (RTC->BKP0R = (uint32_t) (v))
// Health record lives in the .mg_health region, see link.ld
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
// mdash device token
#define MG_ENABLE_MDASH 1
#ifndef MG_MDASH_KEY
#define MG_MDASH_KEY "" // Set this
#endif
// Construct MAC address from the MCU unique ID
#define MGUID ((uint32_t *) UID_BASE)
#define MG_SET_MAC_ADDRESS(mac) \