Skip to content

Commit 02a44b2

Browse files
jplexerclaude
andcommitted
fw/kernel/core_dump: dump LCPU RAM last and only when its domain is up
Capturing LCPU RAM (the SF32LB52 BLE coprocessor RAM in the LPSYS domain at 0x20400000) was added as the second memory region, ahead of the extra-register and thread chunks. On real crashes the dump consistently aborted right after that region: reads into the LPSYS domain hang/fault when it is powered down, and because the region sat before the registers and threads, every dump lost the data needed to symbolize the crash. readcore.py then produced a register-less ELF and GDB reported "Couldn't find general-purpose registers in core file". Move the LCPU RAM dump to the very end, after the thread walk, mirroring the existing "threads last in case FreeRTOS structures are corrupt" design so a faulting read can no longer cost us the RAM, registers, and threads. Also gate it on the HPSYS AON ISSR LP_ACTIVE bit, which the HAL already uses to decide whether LPSYS registers are reachable, so we skip the region entirely when the domain is off instead of hanging on it. The Memfault upload path is unaffected: it only caches addresses within the main SRAM dump range, so the lcpu_ram extra region was already dropped there regardless. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Joshua Jun <lets@throw.rocks>
1 parent d437284 commit 02a44b2

1 file changed

Lines changed: 24 additions & 3 deletions

File tree

src/fw/kernel/core_dump.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,19 @@ typedef struct {
106106
static const MemoryRegion MEMORY_REGIONS_DUMP[] = {
107107
#if CONFIG_SOC_NRF52 || CONFIG_SOC_SF32LB52 || CONFIG_QEMU
108108
{ .start = (void *)0x20000000, .length = COREDUMP_RAM_SIZE },
109-
#endif
110-
#if defined(CONFIG_SOC_SF32LB52)
111-
{ .start = (void *)COREDUMP_LCPU_RAM_START, .length = COREDUMP_LCPU_RAM_SIZE },
112109
#endif
113110
{ .start = (void *)&NVIC->ISER, .length = sizeof(NVIC->ISER) }, // Enabled interrupts
114111
{ .start = (void *)&NVIC->ISPR, .length = sizeof(NVIC->ISPR) }, // Pending interrupts
115112
{ .start = (void *)&NVIC->IABR, .length = sizeof(NVIC->IABR) }, // Active interrupts
116113
};
117114

115+
#if defined(CONFIG_SOC_SF32LB52)
116+
// LCPU RAM is dumped last and only when its domain is up; see prv_dump_lcpu_ram().
117+
static const MemoryRegion LCPU_MEMORY_REGION = {
118+
.start = (void *)COREDUMP_LCPU_RAM_START, .length = COREDUMP_LCPU_RAM_SIZE,
119+
};
120+
#endif
121+
118122
// -------------------------------------------------------------------------------------------------
119123
// Flash driver dual-API.
120124
static bool s_use_cd_flash_driver = true;
@@ -442,6 +446,18 @@ static void prv_write_memory_regions(const MemoryRegion *regions, unsigned int c
442446
}
443447
}
444448

449+
#if defined(CONFIG_SOC_SF32LB52)
450+
// LCPU RAM lives in the LPSYS domain; reading it while that domain is powered
451+
// down hangs/faults. LP_ACTIVE reports whether it is reachable.
452+
static void prv_dump_lcpu_ram(uint32_t flash_base) {
453+
if (hwp_hpsys_aon->ISSR & HPSYS_AON_ISSR_LP_ACTIVE) {
454+
prv_write_memory_regions(&LCPU_MEMORY_REGION, 1, flash_base);
455+
} else {
456+
prv_debug_str("CD: LCPU domain off, skipping LCPU RAM");
457+
}
458+
}
459+
#endif
460+
445461
// Write the Core Dump Image Header
446462
// Returns number of bytes written @ flash_addr
447463
static uint32_t prv_write_image_header(uint32_t flash_addr, uint8_t core_number,
@@ -659,6 +675,11 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) {
659675
prvTaskInfoCallback(&task_info, NULL);
660676
}
661677

678+
#if defined(CONFIG_SOC_SF32LB52)
679+
// Last: its read can hang/fault, so do it after the essential chunks are saved.
680+
prv_dump_lcpu_ram(flash_base);
681+
#endif
682+
662683
// Write out chunk terminator
663684
chunk_hdr.key = CORE_DUMP_CHUNK_KEY_TERMINATOR;
664685
chunk_hdr.size = 0;

0 commit comments

Comments
 (0)