mirror of
https://github.com/apache/nuttx.git
synced 2026-10-08 14:55:18 +00:00
xtensa/esp32s3: Stop an unreportable cache fault from livelocking.
Reporting a fault can itself fault. syslog reaches memory the fault being reported may have made unreachable, so esp32s3_pagefault_dispatch() is re-entered from inside its own _alert() and never returns, and the console fills with the same half-printed line forever. Found under Espressif's QEMU, where PSRAM never initialises and the kernel build needs it; the board's PSRAM works, so hardware does not take this path. A fault repeating at the same address and PC is not helped by reporting it again, so the dispatcher tries three times and then halts with interrupts off. esp32s3_userfault_abort() clears the count through esp32s3_pagefault_clear_repeat(): reaching it means the fault was contained, so only unbroken recursion stops the machine, and three probes at one address do not halt a healthy system. Verified under QEMU: 12,958,521 bytes of output in 60 s before, four reports and a halt after. On an ESP32-S3 DevKitC, esp32s3-devkit:kernel_oct, three identical sandbox probes in one boot are all contained. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
This commit is contained in:
parent
802ccef86f
commit
df782bd1e5
3 changed files with 84 additions and 0 deletions
|
|
@ -66,6 +66,18 @@
|
|||
static volatile int g_pf_selftest_hits;
|
||||
#endif
|
||||
|
||||
/* How many times to let the same fault be reported before giving up on
|
||||
* reporting it. Note the report may not survive even once -- see the
|
||||
* comment in the dispatcher -- so this bounds the damage rather than
|
||||
* guaranteeing a legible message.
|
||||
*/
|
||||
|
||||
#define PF_REPEAT_LIMIT 3
|
||||
|
||||
static uintptr_t g_pf_last_vaddr;
|
||||
static uintptr_t g_pf_last_pc;
|
||||
static int g_pf_repeats;
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
|
@ -125,6 +137,36 @@ int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Reporting a fault can itself fault. syslog reaches memory that the
|
||||
* very fault being reported may have made unreachable -- PSRAM that never
|
||||
* initialised, say -- and then this handler is re-entered from inside its
|
||||
* own _alert(). The console fills with the same line severed part-way
|
||||
* through EXCVADDR, forever, and nothing legible ever reaches it.
|
||||
*
|
||||
* A fault repeating at the same address and PC is not going to be helped
|
||||
* by reporting it again. Try a few times, then stop and halt. The lines
|
||||
* may still be truncated -- the print is what faults, so it cannot be made
|
||||
* to complete from here -- but a handful of severed lines followed by
|
||||
* silence is diagnosable, and an endless stream of them is not.
|
||||
*/
|
||||
|
||||
if (vaddr == g_pf_last_vaddr && pc == g_pf_last_pc)
|
||||
{
|
||||
if (++g_pf_repeats >= PF_REPEAT_LIMIT)
|
||||
{
|
||||
up_irq_save();
|
||||
for (; ; )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pf_last_vaddr = vaddr;
|
||||
g_pf_last_pc = pc;
|
||||
g_pf_repeats = 0;
|
||||
}
|
||||
|
||||
/* Report the precise fault (with its tracking EXCVADDR) and decline to
|
||||
* service it, so the caller falls through to the panic / abort path.
|
||||
*/
|
||||
|
|
@ -135,3 +177,20 @@ int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs)
|
|||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp32s3_pagefault_clear_repeat
|
||||
*
|
||||
* Description:
|
||||
* Forget the last serviced fault. Called once a fault has been contained
|
||||
* some other way -- the task terminated -- so that later, unrelated faults
|
||||
* at the same address are not counted as runaway recursion.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void esp32s3_pagefault_clear_repeat(void)
|
||||
{
|
||||
g_pf_last_vaddr = 0;
|
||||
g_pf_last_pc = 0;
|
||||
g_pf_repeats = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,4 +59,15 @@
|
|||
|
||||
int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp32s3_pagefault_clear_repeat
|
||||
*
|
||||
* Description:
|
||||
* Forget the last serviced fault, so that later faults at the same address
|
||||
* are not mistaken for runaway recursion.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void esp32s3_pagefault_clear_repeat(void);
|
||||
|
||||
#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H */
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@
|
|||
#include "signal/signal.h"
|
||||
|
||||
#include "esp32s3_userfault.h"
|
||||
#ifdef CONFIG_ESP32S3_PAGEFAULT
|
||||
#include "esp32s3_pagefault.h"
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
|
|
@ -82,6 +85,17 @@ uint32_t *esp32s3_userfault_abort(int exccause, uint32_t *regs)
|
|||
struct tcb_s *tcb = this_task();
|
||||
siginfo_t info;
|
||||
|
||||
#ifdef CONFIG_ESP32S3_PAGEFAULT
|
||||
/* Reaching here means the fault was contained and the system carried on,
|
||||
* so the dispatcher's repeat counter has served its purpose. Clear it, or
|
||||
* a probe run three times at one address would trip that guard and halt a
|
||||
* perfectly healthy system. Only *unbroken* recursion -- a report that
|
||||
* faults before the abort can happen -- should stop the machine.
|
||||
*/
|
||||
|
||||
esp32s3_pagefault_clear_repeat();
|
||||
#endif
|
||||
|
||||
_alert("SIGSEGV task %s: EXCCAUSE=%d EXCVADDR=%08x PC=%08x\n",
|
||||
get_task_name(tcb), exccause, (unsigned)regs[REG_EXCVADDR],
|
||||
(unsigned)regs[REG_PC]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue