mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-13 17:00:19 +00:00
Merge pull request #3718 from cesanta/cr
Move crash handler to src/health.c
This commit is contained in:
13 files changed
+176
-264
No files matched your search
+72
-26
@@ -3485,6 +3485,73 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
|||||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||||
|
|
||||||
|
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||||
|
#if MG_ENABLE_HEALTH && defined(STM32H723xx_H)
|
||||||
|
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||||
|
// blocking calls. Copies the unwind registers and raw stack 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 *frame,
|
||||||
|
uint32_t exc_return) {
|
||||||
|
extern uint32_t _estack;
|
||||||
|
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
|
||||||
|
uint32_t *r = mg_health_record.data;
|
||||||
|
|
||||||
|
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
|
||||||
|
r[0] = (uint32_t) (uintptr_t) stack; // SP
|
||||||
|
r[1] = frame[5]; // LR
|
||||||
|
r[2] = frame[6]; // PC
|
||||||
|
for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - 3; i++) { // Raw stack
|
||||||
|
r[3 + i] = stack + i < &_estack ? stack[i] : 0;
|
||||||
|
}
|
||||||
|
mg_health_record.cpuid = SCB->CPUID;
|
||||||
|
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||||
|
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(
|
||||||
|
"mov r1, lr\n\t"
|
||||||
|
"tst lr, #4\n\t"
|
||||||
|
"ite eq\n\t"
|
||||||
|
"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")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
|
size_t n = va_arg(*ap, size_t);
|
||||||
|
uint32_t *p = va_arg(*ap, uint32_t *);
|
||||||
|
size_t len = 0;
|
||||||
|
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
|
size_t len = 0;
|
||||||
|
if (mg_health_valid() &&
|
||||||
|
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
||||||
|
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||||
|
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||||
|
MG_ESC("version"), mg_health_record.magic[3], //
|
||||||
|
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||||
|
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||||
|
} else {
|
||||||
|
len += mg_xprintf(fn, arg, "{}");
|
||||||
|
}
|
||||||
|
(void) ap;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MG_ENABLE_LINES
|
#ifdef MG_ENABLE_LINES
|
||||||
#line 1 "src/http.c"
|
#line 1 "src/http.c"
|
||||||
#endif
|
#endif
|
||||||
@@ -7031,35 +7098,14 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(void) c;
|
(void) c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|
||||||
size_t n = va_arg(*ap, size_t);
|
|
||||||
uint32_t *p = va_arg(*ap, uint32_t *);
|
|
||||||
size_t len = 0;
|
|
||||||
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|
||||||
size_t len = 0;
|
|
||||||
if (mg_health_valid()) {
|
|
||||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
|
||||||
len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"),
|
|
||||||
MG_ESC("version"), mg_health_record.magic[3], //
|
|
||||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
|
||||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
|
||||||
}
|
|
||||||
(void) ap;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||||
const char *reset_reason = mg_health_reason_str(mg_health_reason());
|
const char *reset_reason = mg_health_reason_str(mg_health_reason());
|
||||||
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\"%M}", //
|
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", //
|
||||||
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
|
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
|
||||||
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||||
MG_ESC("arch"), MG_ARCH, //
|
MG_ESC("arch"), MG_ARCH, //
|
||||||
print_crash);
|
MG_ESC("crash"), mg_print_crash_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||||
|
|||||||
+6
-42
@@ -1490,6 +1490,10 @@ struct timeval {
|
|||||||
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_HEALTH
|
||||||
|
#define MG_ENABLE_HEALTH 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -4914,30 +4918,7 @@ void mg_ota_device_id(char *buf, size_t len);
|
|||||||
// directly.
|
// directly.
|
||||||
void mg_ota_poll(struct mg_mgr *);
|
void mg_ota_poll(struct mg_mgr *);
|
||||||
// Device health monitoring
|
// Device health monitoring
|
||||||
//
|
|
||||||
// Keeps a small record in a RAM region that survives a warm reset, so a device
|
|
||||||
// can tell why it restarted. The record holds a boot counter, the uptime this
|
|
||||||
// boot has reached, and the reset reason reported by the hardware. A crash
|
|
||||||
// loop, a hang loop and a marginal power supply each leave a different mark.
|
|
||||||
//
|
|
||||||
// Put MG_HEALTH_INIT() in main(), right after clock and memory init:
|
|
||||||
//
|
|
||||||
// ```c
|
|
||||||
// int main(void) {
|
|
||||||
// hal_clock_init();
|
|
||||||
// MG_HEALTH_INIT();
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// The record must live in RAM that startup code neither copies nor zeroes.
|
|
||||||
// See tutorials/stm32/nucleo-h723zg/minimal/link.ld for the .mg_health region,
|
|
||||||
// and tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h for the
|
|
||||||
// reset-reason hooks.
|
|
||||||
//
|
|
||||||
// On Cortex-M, a fault handler can record the crash: it sets reset_reason to
|
|
||||||
// MG_HEALTH_RESET_FAULT and walks the stack into backtrace[]. See
|
|
||||||
// tutorials/stm32/nucleo-h723zg/minimal/main.c for a sample HardFault_Handler.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -5016,27 +4997,10 @@ static inline void mg_health_init(void) {
|
|||||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||||
}
|
}
|
||||||
// h->prev_uptime = h->uptime;
|
|
||||||
// h->prev_reason = h->reason;
|
|
||||||
// h->counter++;
|
|
||||||
// h->uptime = 0;
|
|
||||||
// h->reason = mg_health_decode_reason();
|
|
||||||
// MG_HEALTH_RESET_CLEAR();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Record that the main loop is still alive. Called from mg_mgr_poll(), so a
|
|
||||||
// // hung application leaves uptime frozen at the hang
|
|
||||||
// extern uint64_t mg_health_next_ms; // Defined in health.c
|
|
||||||
|
|
||||||
// static inline void mg_health_uptime(uint64_t now_ms) {
|
|
||||||
// struct mg_health *h = mg_health_get();
|
|
||||||
// if (now_ms >= mg_health_next_ms && mg_health_valid()) {
|
|
||||||
// mg_health_next_ms = now_ms + 1000;
|
|
||||||
// h->uptime++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
|
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||||
// Cloud connector API to https://mdash.net - device management cloud
|
// Cloud connector API to https://mdash.net - device management cloud
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -226,3 +226,7 @@
|
|||||||
#ifndef MG_ENABLE_CHACHA20
|
#ifndef MG_ENABLE_CHACHA20
|
||||||
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_HEALTH
|
||||||
|
#define MG_ENABLE_HEALTH 0
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,73 @@
|
|||||||
|
#include "config.h"
|
||||||
#include "health.h"
|
#include "health.h"
|
||||||
|
|
||||||
// The one health record. Lives in RAM that survives a warm reset, see
|
// The one health record. Lives in RAM that survives a warm reset, see
|
||||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||||
|
|
||||||
|
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||||
|
#if MG_ENABLE_HEALTH && defined(STM32H723xx_H)
|
||||||
|
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||||
|
// blocking calls. Copies the unwind registers and raw stack 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 *frame,
|
||||||
|
uint32_t exc_return) {
|
||||||
|
extern uint32_t _estack;
|
||||||
|
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
|
||||||
|
uint32_t *r = mg_health_record.data;
|
||||||
|
|
||||||
|
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
|
||||||
|
r[0] = (uint32_t) (uintptr_t) stack; // SP
|
||||||
|
r[1] = frame[5]; // LR
|
||||||
|
r[2] = frame[6]; // PC
|
||||||
|
for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - 3; i++) { // Raw stack
|
||||||
|
r[3 + i] = stack + i < &_estack ? stack[i] : 0;
|
||||||
|
}
|
||||||
|
mg_health_record.cpuid = SCB->CPUID;
|
||||||
|
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||||
|
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(
|
||||||
|
"mov r1, lr\n\t"
|
||||||
|
"tst lr, #4\n\t"
|
||||||
|
"ite eq\n\t"
|
||||||
|
"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")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
|
size_t n = va_arg(*ap, size_t);
|
||||||
|
uint32_t *p = va_arg(*ap, uint32_t *);
|
||||||
|
size_t len = 0;
|
||||||
|
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
|
size_t len = 0;
|
||||||
|
if (mg_health_valid() &&
|
||||||
|
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
||||||
|
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||||
|
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||||
|
MG_ESC("version"), mg_health_record.magic[3], //
|
||||||
|
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||||
|
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||||
|
} else {
|
||||||
|
len += mg_xprintf(fn, arg, "{}");
|
||||||
|
}
|
||||||
|
(void) ap;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
+2
-42
@@ -1,32 +1,9 @@
|
|||||||
// Device health monitoring
|
// Device health monitoring
|
||||||
//
|
|
||||||
// Keeps a small record in a RAM region that survives a warm reset, so a device
|
|
||||||
// can tell why it restarted. The record holds a boot counter, the uptime this
|
|
||||||
// boot has reached, and the reset reason reported by the hardware. A crash
|
|
||||||
// loop, a hang loop and a marginal power supply each leave a different mark.
|
|
||||||
//
|
|
||||||
// Put MG_HEALTH_INIT() in main(), right after clock and memory init:
|
|
||||||
//
|
|
||||||
// ```c
|
|
||||||
// int main(void) {
|
|
||||||
// hal_clock_init();
|
|
||||||
// MG_HEALTH_INIT();
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// The record must live in RAM that startup code neither copies nor zeroes.
|
|
||||||
// See tutorials/stm32/nucleo-h723zg/minimal/link.ld for the .mg_health region,
|
|
||||||
// and tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h for the
|
|
||||||
// reset-reason hooks.
|
|
||||||
//
|
|
||||||
// On Cortex-M, a fault handler can record the crash: it sets reset_reason to
|
|
||||||
// MG_HEALTH_RESET_FAULT and walks the stack into backtrace[]. See
|
|
||||||
// tutorials/stm32/nucleo-h723zg/minimal/main.c for a sample HardFault_Handler.
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
#ifndef MG_HEALTH_MAGIC
|
#ifndef MG_HEALTH_MAGIC
|
||||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
@@ -101,24 +78,7 @@ static inline void mg_health_init(void) {
|
|||||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||||
}
|
}
|
||||||
// h->prev_uptime = h->uptime;
|
|
||||||
// h->prev_reason = h->reason;
|
|
||||||
// h->counter++;
|
|
||||||
// h->uptime = 0;
|
|
||||||
// h->reason = mg_health_decode_reason();
|
|
||||||
// MG_HEALTH_RESET_CLEAR();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Record that the main loop is still alive. Called from mg_mgr_poll(), so a
|
|
||||||
// // hung application leaves uptime frozen at the hang
|
|
||||||
// extern uint64_t mg_health_next_ms; // Defined in health.c
|
|
||||||
|
|
||||||
// static inline void mg_health_uptime(uint64_t now_ms) {
|
|
||||||
// struct mg_health *h = mg_health_get();
|
|
||||||
// if (now_ms >= mg_health_next_ms && mg_health_valid()) {
|
|
||||||
// mg_health_next_ms = now_ms + 1000;
|
|
||||||
// h->uptime++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
|
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||||
+5
-26
@@ -95,35 +95,14 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(void) c;
|
(void) c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|
||||||
size_t n = va_arg(*ap, size_t);
|
|
||||||
uint32_t *p = va_arg(*ap, uint32_t *);
|
|
||||||
size_t len = 0;
|
|
||||||
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|
||||||
size_t len = 0;
|
|
||||||
if (mg_health_valid()) {
|
|
||||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
|
||||||
len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"),
|
|
||||||
MG_ESC("version"), mg_health_record.magic[3], //
|
|
||||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
|
||||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
|
||||||
}
|
|
||||||
(void) ap;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||||
const char *reset_reason = mg_health_reason_str(mg_health_reason());
|
const char *reset_reason = mg_health_reason_str(mg_health_reason());
|
||||||
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\"%M}", //
|
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", //
|
||||||
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
|
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
|
||||||
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||||
MG_ESC("arch"), MG_ARCH, //
|
MG_ESC("arch"), MG_ARCH, //
|
||||||
print_crash);
|
MG_ESC("crash"), mg_print_crash_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ extern "C" {
|
|||||||
|
|
||||||
/* Exported functions prototypes ---------------------------------------------*/
|
/* Exported functions prototypes ---------------------------------------------*/
|
||||||
void NMI_Handler(void);
|
void NMI_Handler(void);
|
||||||
void HardFault_Handler(void);
|
|
||||||
void MemManage_Handler(void);
|
|
||||||
void BusFault_Handler(void);
|
|
||||||
void UsageFault_Handler(void);
|
|
||||||
void SVC_Handler(void);
|
void SVC_Handler(void);
|
||||||
void DebugMon_Handler(void);
|
void DebugMon_Handler(void);
|
||||||
void PendSV_Handler(void);
|
void PendSV_Handler(void);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
/* USER CODE END Header */
|
/* USER CODE END Header */
|
||||||
/* Includes ------------------------------------------------------------------*/
|
/* Includes ------------------------------------------------------------------*/
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "stm32h7xx_hal.h"
|
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
/* Private includes ----------------------------------------------------------*/
|
||||||
@@ -94,6 +93,9 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick());
|
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick());
|
||||||
|
} else if (mg_match(hm->uri, mg_str("/api/kill"), NULL)) {
|
||||||
|
SCB->SHCSR &= ~SCB_SHCSR_USGFAULTENA_Msk;
|
||||||
|
__asm volatile("udf #0");
|
||||||
} else {
|
} else {
|
||||||
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %lu\n", HAL_GetTick());
|
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %lu\n", HAL_GetTick());
|
||||||
}
|
}
|
||||||
@@ -109,7 +111,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
|
|
||||||
/* USER CODE BEGIN 1 */
|
/* USER CODE BEGIN 1 */
|
||||||
|
MG_HEALTH_INIT();
|
||||||
/* USER CODE END 1 */
|
/* USER CODE END 1 */
|
||||||
|
|
||||||
/* MPU Configuration--------------------------------------------------------*/
|
/* MPU Configuration--------------------------------------------------------*/
|
||||||
|
|||||||
@@ -78,66 +78,6 @@ void NMI_Handler(void)
|
|||||||
/* USER CODE END NonMaskableInt_IRQn 1 */
|
/* USER CODE END NonMaskableInt_IRQn 1 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Hard fault interrupt.
|
|
||||||
*/
|
|
||||||
void HardFault_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN HardFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END HardFault_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
|
|
||||||
/* USER CODE END W1_HardFault_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Memory management fault.
|
|
||||||
*/
|
|
||||||
void MemManage_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END MemoryManagement_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
|
|
||||||
/* USER CODE END W1_MemoryManagement_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Pre-fetch fault, memory access fault.
|
|
||||||
*/
|
|
||||||
void BusFault_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN BusFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END BusFault_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
|
|
||||||
/* USER CODE END W1_BusFault_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Undefined instruction or illegal state.
|
|
||||||
*/
|
|
||||||
void UsageFault_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN UsageFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END UsageFault_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
|
|
||||||
/* USER CODE END W1_UsageFault_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function handles System service call via SWI instruction.
|
* @brief This function handles System service call via SWI instruction.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -42,23 +42,23 @@ Mcu.Pin6=PB0
|
|||||||
Mcu.Pin7=PB13
|
Mcu.Pin7=PB13
|
||||||
Mcu.Pin8=PB14
|
Mcu.Pin8=PB14
|
||||||
Mcu.Pin9=PD8
|
Mcu.Pin9=PD8
|
||||||
Mcu.PinsNb=19
|
Mcu.PinsNb=17
|
||||||
Mcu.ThirdPartyNb=0
|
Mcu.ThirdPartyNb=0
|
||||||
Mcu.UserConstants=
|
Mcu.UserConstants=
|
||||||
Mcu.UserName=STM32H723ZGTx
|
Mcu.UserName=STM32H723ZGTx
|
||||||
MxCube.Version=6.17.0
|
MxCube.Version=6.17.0
|
||||||
MxDb.Version=DB.6.0.170
|
MxDb.Version=DB.6.0.170
|
||||||
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false
|
||||||
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||||
NVIC.ForceEnableDMAVector=true
|
NVIC.ForceEnableDMAVector=true
|
||||||
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false
|
||||||
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false
|
||||||
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||||
NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||||
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
|
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
|
||||||
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||||
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false
|
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false
|
||||||
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false
|
||||||
PA1.Mode=RMII
|
PA1.Mode=RMII
|
||||||
PA1.Signal=ETH_REF_CLK
|
PA1.Signal=ETH_REF_CLK
|
||||||
PA2.Mode=RMII
|
PA2.Mode=RMII
|
||||||
@@ -114,8 +114,8 @@ ProjectManager.MainLocation=Core/Src
|
|||||||
ProjectManager.NoMain=false
|
ProjectManager.NoMain=false
|
||||||
ProjectManager.PreviousToolchain=
|
ProjectManager.PreviousToolchain=
|
||||||
ProjectManager.ProjectBuild=false
|
ProjectManager.ProjectBuild=false
|
||||||
ProjectManager.ProjectFileName=h723.ioc
|
ProjectManager.ProjectFileName=nucleo-h723zg.ioc
|
||||||
ProjectManager.ProjectName=h723
|
ProjectManager.ProjectName=nucleo-h723zg
|
||||||
ProjectManager.ProjectStructure=
|
ProjectManager.ProjectStructure=
|
||||||
ProjectManager.RegisterCallBack=
|
ProjectManager.RegisterCallBack=
|
||||||
ProjectManager.StackSize=0x2000
|
ProjectManager.StackSize=0x2000
|
||||||
|
|||||||
@@ -28,5 +28,5 @@ SECTIONS {
|
|||||||
_end = .;
|
_end = .;
|
||||||
|
|
||||||
/* Retained health record. Startup code must neither copy nor clear it. */
|
/* Retained health record. Startup code must neither copy nor clear it. */
|
||||||
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > bkpsram
|
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > ram_d3
|
||||||
}
|
}
|
||||||
@@ -17,19 +17,10 @@
|
|||||||
#define LED2 PIN('E', 1)
|
#define LED2 PIN('E', 1)
|
||||||
#define LED3 PIN('B', 14)
|
#define LED3 PIN('B', 14)
|
||||||
|
|
||||||
enum { CRASH_SP, CRASH_LR, CRASH_PC, CRASH_STACK };
|
|
||||||
|
|
||||||
static void log_fn(char ch, void *param) {
|
static void log_fn(char ch, void *param) {
|
||||||
hal_uart_write_buf(param, &ch, 1);
|
hal_uart_write_buf(param, &ch, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hal_storage_init(void) {
|
|
||||||
RCC->AHB4ENR |= RCC_AHB4ENR_BKPRAMEN;
|
|
||||||
(void) RCC->AHB4ENR;
|
|
||||||
PWR->CR2 |= PWR_CR2_BREN;
|
|
||||||
while ((PWR->CR2 & PWR_CR2_BRRDY) == 0) (void) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blink_task(void) {
|
static void blink_task(void) {
|
||||||
static uint64_t blink_timer = 0;
|
static uint64_t blink_timer = 0;
|
||||||
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
||||||
@@ -37,46 +28,6 @@ static void blink_task(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
|
||||||
// blocking calls. Copies the unwind registers and raw stack 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 *frame,
|
|
||||||
uint32_t exc_return) {
|
|
||||||
extern uint32_t _estack;
|
|
||||||
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
|
|
||||||
uint32_t *r = mg_health_record.data;
|
|
||||||
|
|
||||||
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
|
|
||||||
r[CRASH_SP] = (uint32_t) (uintptr_t) stack;
|
|
||||||
r[CRASH_LR] = frame[5];
|
|
||||||
r[CRASH_PC] = frame[6];
|
|
||||||
for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - CRASH_STACK; i++) {
|
|
||||||
r[CRASH_STACK + i] = stack + i < &_estack ? stack[i] : 0;
|
|
||||||
}
|
|
||||||
mg_health_record.cpuid = SCB->CPUID;
|
|
||||||
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
|
||||||
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(
|
|
||||||
"mov r1, lr\n\t"
|
|
||||||
"tst lr, #4\n\t"
|
|
||||||
"ite eq\n\t"
|
|
||||||
"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) {
|
uint64_t mg_millis(void) {
|
||||||
return hal_get_tick();
|
return hal_get_tick();
|
||||||
}
|
}
|
||||||
@@ -105,9 +56,9 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
MG_HEALTH_INIT();
|
||||||
|
|
||||||
hal_clock_init();
|
hal_clock_init();
|
||||||
hal_storage_init();
|
|
||||||
MG_HEALTH_INIT(); // Must be called after clock init
|
|
||||||
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
|
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
|
||||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||||
hal_rng_init();
|
hal_rng_init();
|
||||||
@@ -117,6 +68,7 @@ int main(void) {
|
|||||||
hal_gpio_output(LED3);
|
hal_gpio_output(LED3);
|
||||||
|
|
||||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||||
|
MG_INFO(("Crash record: %M", mg_print_crash_record));
|
||||||
|
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
|
|||||||
@@ -27,9 +27,10 @@
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// Crash report support. Health record lives in the .mg_health region, see link.ld
|
// Crash report support. Health record lives in the .mg_health region, see link.ld
|
||||||
|
#define MG_ENABLE_HEALTH 1
|
||||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||||
#define MG_HEALTH_DATA_SIZE 1000 // fill the 4KB backup SRAM
|
#define MG_HEALTH_DATA_SIZE 1000
|
||||||
|
|
||||||
// mdash.net device management service support
|
// mdash.net device management service support
|
||||||
#define MG_ENABLE_MDASH 1
|
#define MG_ENABLE_MDASH 0
|
||||||
#define MG_MDASH_KEY "u8eyWvBmkc6gw9991z1SQ8Lg" // Set this - mDash device token
|
#define MG_MDASH_KEY "..." // Set this - mDash device token
|
||||||
Reference in new issue
Block a user