mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-13 10:15:00 +00:00
Merge pull request #3718 from cesanta/cr
Move crash handler to src/health.c
This commit is contained in:
+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
|
||||
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
|
||||
#line 1 "src/http.c"
|
||||
#endif
|
||||
@@ -7031,35 +7098,14 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(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) {
|
||||
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("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||
MG_ESC("arch"), MG_ARCH, //
|
||||
print_crash);
|
||||
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||
MG_ESC("arch"), MG_ARCH, //
|
||||
MG_ESC("crash"), mg_print_crash_record);
|
||||
}
|
||||
|
||||
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
|
||||
#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.
|
||||
void mg_ota_poll(struct mg_mgr *);
|
||||
// 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));
|
||||
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()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
// Cloud connector API to https://mdash.net - device management cloud
|
||||
|
||||
|
||||
|
||||
@@ -226,3 +226,7 @@
|
||||
#ifndef MG_ENABLE_CHACHA20
|
||||
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_HEALTH
|
||||
#define MG_ENABLE_HEALTH 0
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,73 @@
|
||||
#include "config.h"
|
||||
#include "health.h"
|
||||
|
||||
// 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
|
||||
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
|
||||
//
|
||||
// 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
|
||||
|
||||
#include "arch.h"
|
||||
#include "printf.h"
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#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));
|
||||
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()
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||
MG_ESC("arch"), MG_ARCH, //
|
||||
print_crash);
|
||||
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||
MG_ESC("arch"), MG_ARCH, //
|
||||
MG_ESC("crash"), mg_print_crash_record);
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||
|
||||
@@ -47,10 +47,6 @@ extern "C" {
|
||||
|
||||
/* Exported functions prototypes ---------------------------------------------*/
|
||||
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 DebugMon_Handler(void);
|
||||
void PendSV_Handler(void);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
/* USER CODE END Header */
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "main.h"
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "string.h"
|
||||
|
||||
/* 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;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
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 {
|
||||
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %lu\n", HAL_GetTick());
|
||||
}
|
||||
@@ -109,7 +111,7 @@ int main(void)
|
||||
{
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
MG_HEALTH_INIT();
|
||||
/* USER CODE END 1 */
|
||||
|
||||
/* MPU Configuration--------------------------------------------------------*/
|
||||
|
||||
@@ -78,66 +78,6 @@ void NMI_Handler(void)
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
@@ -154,9 +154,9 @@ SECTIONS
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* mongoose Ethernet driver */
|
||||
.eth_ram : { *(.eth_ram .eth_ram*) } > RAM_D2 AT > FLASH
|
||||
|
||||
/* mongoose Ethernet driver */
|
||||
.eth_ram : { *(.eth_ram .eth_ram*) } > RAM_D2 AT > FLASH
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
|
||||
@@ -42,23 +42,23 @@ Mcu.Pin6=PB0
|
||||
Mcu.Pin7=PB13
|
||||
Mcu.Pin8=PB14
|
||||
Mcu.Pin9=PD8
|
||||
Mcu.PinsNb=19
|
||||
Mcu.PinsNb=17
|
||||
Mcu.ThirdPartyNb=0
|
||||
Mcu.UserConstants=
|
||||
Mcu.UserName=STM32H723ZGTx
|
||||
MxCube.Version=6.17.0
|
||||
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.ForceEnableDMAVector=true
|
||||
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
|
||||
NVIC.MemoryManagement_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\:false\: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.PriorityGroup=NVIC_PRIORITYGROUP_4
|
||||
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.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.Signal=ETH_REF_CLK
|
||||
PA2.Mode=RMII
|
||||
@@ -114,8 +114,8 @@ ProjectManager.MainLocation=Core/Src
|
||||
ProjectManager.NoMain=false
|
||||
ProjectManager.PreviousToolchain=
|
||||
ProjectManager.ProjectBuild=false
|
||||
ProjectManager.ProjectFileName=h723.ioc
|
||||
ProjectManager.ProjectName=h723
|
||||
ProjectManager.ProjectFileName=nucleo-h723zg.ioc
|
||||
ProjectManager.ProjectName=nucleo-h723zg
|
||||
ProjectManager.ProjectStructure=
|
||||
ProjectManager.RegisterCallBack=
|
||||
ProjectManager.StackSize=0x2000
|
||||
|
||||
@@ -28,5 +28,5 @@ SECTIONS {
|
||||
_end = .;
|
||||
|
||||
/* 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 LED3 PIN('B', 14)
|
||||
|
||||
enum { CRASH_SP, CRASH_LR, CRASH_PC, CRASH_STACK };
|
||||
|
||||
static void log_fn(char ch, void *param) {
|
||||
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 uint64_t blink_timer = 0;
|
||||
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) {
|
||||
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) {
|
||||
MG_HEALTH_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);
|
||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||
hal_rng_init();
|
||||
@@ -117,6 +68,7 @@ int main(void) {
|
||||
hal_gpio_output(LED3);
|
||||
|
||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||
MG_INFO(("Crash record: %M", mg_print_crash_record));
|
||||
|
||||
struct mg_mgr mgr;
|
||||
mg_mgr_init(&mgr);
|
||||
|
||||
@@ -27,9 +27,10 @@
|
||||
} while (0)
|
||||
|
||||
// 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_DATA_SIZE 1000 // fill the 4KB backup SRAM
|
||||
#define MG_HEALTH_DATA_SIZE 1000
|
||||
|
||||
// mdash.net device management service support
|
||||
#define MG_ENABLE_MDASH 1
|
||||
#define MG_MDASH_KEY "u8eyWvBmkc6gw9991z1SQ8Lg" // Set this - mDash device token
|
||||
#define MG_ENABLE_MDASH 0
|
||||
#define MG_MDASH_KEY "..." // Set this - mDash device token
|
||||
|
||||
Reference in New Issue
Block a user