This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit df782bd1e587d4d85389235c267080768796bbc6
Author: Marco Casaroli <[email protected]>
AuthorDate: Tue Jul 28 23:57:28 2026 +0200

    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 <[email protected]>
---
 arch/xtensa/src/esp32s3/esp32s3_pagefault.c | 59 +++++++++++++++++++++++++++++
 arch/xtensa/src/esp32s3/esp32s3_pagefault.h | 11 ++++++
 arch/xtensa/src/esp32s3/esp32s3_userfault.c | 14 +++++++
 3 files changed, 84 insertions(+)

diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c 
b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c
index 4d029d7a585..af3b9176f5f 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c
@@ -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;
+}
diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.h 
b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h
index 13ed1dbbcf3..3b268d9ef00 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_pagefault.h
+++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h
@@ -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 */
diff --git a/arch/xtensa/src/esp32s3/esp32s3_userfault.c 
b/arch/xtensa/src/esp32s3/esp32s3_userfault.c
index becff2125b5..e0f6ca343a8 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_userfault.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_userfault.c
@@ -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]);

Reply via email to