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 54fcbe888b2ed27dd7f8371142231abc11bac9d6
Author: Marco Casaroli <[email protected]>
AuthorDate: Thu Jul 30 14:16:01 2026 +0200

    xtensa/esp32s3: add recoverable cache-attribute fault dispatcher (Unit B)
    
    Route the precise cache-attribute permission faults -- Load/Store/InstrFetch
    Prohibited (EXCCAUSE 28/29/20) -- from xtensa_user() to a new dispatcher,
    esp32s3_pagefault_dispatch().  On a serviced fault the register frame is
    returned so the exception vector's RFE re-executes the faulting instruction;
    otherwise it declines to the existing panic path.  Gated by
    CONFIG_ESP32S3_PAGEFAULT (default n, depends on BUILD_PROTECTED); the build
    is unchanged when the option is off.
    
    This is the recoverable-fault primitive the address-environment / 
demand-paging
    work builds on.  Proven on the ESP32-S3-DevKitC WROOM-2:
    
    - A precise LoadProhibited carries a tracking EXCVADDR (the exact faulting
      address), and RFE cleanly re-executes the faulted load on return -- 
verified
      with CONFIG_ESP32S3_PAGEFAULT_SELFTEST (the identical instruction restarts
      three times, then steps past, and the task resumes with the shell alive).
    - ESP32-S3 PMS (World Controller) permission violations are NOT delivered as
      these precise causes; they raise the asynchronous DRAM0/IRAM0 PMS-monitor
      interrupt, so PMS is an isolation (kill) mechanism, not a restartable one.
    
    No regression: esp32s3-devkit:knsh (WROOM-2) boots to nsh and ostest passes
    with the option enabled.
    
    Assisted-by: Claude Opus 4.8 (1M context) <[email protected]>
    Signed-off-by: Marco Casaroli <[email protected]>
---
 arch/xtensa/src/esp32s3/Kconfig                    |  28 +++++
 arch/xtensa/src/esp32s3/Make.defs                  |   4 +
 arch/xtensa/src/esp32s3/esp32s3_pagefault.c        | 136 +++++++++++++++++++++
 .../{esp32s3_user.c => esp32s3_pagefault.h}        |  63 ++++------
 arch/xtensa/src/esp32s3/esp32s3_user.c             |  28 +++++
 5 files changed, 217 insertions(+), 42 deletions(-)

diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig
index 4d189c7baa5..bb16be86b60 100644
--- a/arch/xtensa/src/esp32s3/Kconfig
+++ b/arch/xtensa/src/esp32s3/Kconfig
@@ -925,6 +925,34 @@ config ESP32S3_WCL
        select ARCH_USE_MPU
        select XTENSA_HAVE_GENERAL_EXCEPTION_HOOKS if BUILD_PROTECTED
 
+config ESP32S3_PAGEFAULT
+       bool "Recoverable PMS permission faults"
+       default n
+       depends on BUILD_PROTECTED
+       ---help---
+               Route the precise Load/Store/InstrFetch Prohibited exceptions 
raised
+               by PMS (memory-protection) permission violations through a
+               recoverable-fault dispatcher instead of panicking 
unconditionally.
+               This is the foundation for guard pages, lazy stack/heap growth 
and,
+               ultimately, demand paging / copy-on-write on the ESP32-S3.
+
+if ESP32S3_PAGEFAULT
+
+config ESP32S3_PAGEFAULT_SELFTEST
+       bool "Recoverable-fault self-test"
+       default n
+       ---help---
+               Prove the recoverable-fault primitive on silicon.  A load from
+               0x80000000 raises a precise LoadProhibited (EXCCAUSE 28, "cache
+               attribute does not allow load"); the fault dispatcher lets the 
RFE
+               re-execute the identical faulting instruction several times 
(proving
+               a faulted precise access restarts cleanly) before stepping past 
it,
+               reporting the result over the console.  Trigger it from a user 
task,
+               e.g. with examples/pffault:  nsh> pffault r 0x80000000
+               For bring-up and evaluation only.
+
+endif # ESP32S3_PAGEFAULT
+
 config ESP32S3_LCD
        bool "LCD"
        default n
diff --git a/arch/xtensa/src/esp32s3/Make.defs 
b/arch/xtensa/src/esp32s3/Make.defs
index b306e15c46c..e80bf885ba6 100644
--- a/arch/xtensa/src/esp32s3/Make.defs
+++ b/arch/xtensa/src/esp32s3/Make.defs
@@ -43,6 +43,10 @@ ifeq ($(CONFIG_BUILD_PROTECTED),y)
 CHIP_CSRCS += esp32s3_userspace.c
 endif
 
+ifeq ($(CONFIG_ESP32S3_PAGEFAULT),y)
+CHIP_CSRCS += esp32s3_pagefault.c
+endif
+
 ifeq ($(CONFIG_SMP),y)
 CHIP_CSRCS += esp32s3_cpuidlestack.c esp32s3_cpustart.c 
esp32s3_intercpu_interrupt.c
 endif
diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c 
b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c
new file mode 100644
index 00000000000..69b356bc2e6
--- /dev/null
+++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c
@@ -0,0 +1,136 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s3/esp32s3_pagefault.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/sched.h>
+#include <arch/irq.h>
+#include <arch/xtensa/xtensa_corebits.h>
+
+#include "xtensa.h"
+#include "sched/sched.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP32S3_PAGEFAULT_SELFTEST
+/* Runtime self-test: proof of the recoverable-fault primitive on silicon.
+ * A load of this out-of-cache-region address raises a precise LoadProhibited
+ * (EXCCAUSE 28, "cache attribute does not allow load") whose EXCVADDR tracks
+ * the address exactly.  The dispatcher returns "serviced" WITHOUT making the
+ * address accessible for the first PF_SELFTEST_REPEATS re-executions, so the
+ * exception vector's RFE re-runs the identical faulting instruction.
+ * Being re-entered that many times for one instruction proves that RFE
+ * cleanly restarts a faulted precise access (the write-buffer / prefetch
+ * corner case that gates recoverable page faults); it then steps the saved
+ * PC past the 2-byte l32i.n so the faulting task resumes.  Trigger it from a
+ * user task, e.g. examples/pffault:  nsh> pffault r 0x80000000
+ */
+
+#  define PF_SELFTEST_VADDR    0x80000000ul
+#  define PF_SELFTEST_REPEATS  3
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static volatile int g_pf_selftest_hits;
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32s3_pagefault_dispatch
+ *
+ * Description:
+ *   Offered the precise, restartable exceptions raised by a cache-attribute
+ *   permission violation (EXCCAUSE Load/Store/InstrFetch Prohibited).  The
+ *   faulting virtual address is in regs[REG_EXCVADDR] and the faulting PC in
+ *   regs[REG_PC]; both are populated by the common Xtensa user exception
+ *   handler (frame layout: NuttX REG_*).
+ *
+ *   This unit establishes the recoverable-fault primitive.  The full
+ *   servicing (map a page / restore a cache attribute, then RFE-restart) is
+ *   built on top in the addrenv / demand-paging units; here the dispatcher
+ *   reports the fault (with its tracking EXCVADDR) and declines to service
+ *   it, except under the self-test which proves the RFE-restart on silicon.
+ *
+ * Returned Value:
+ *   OK if the faulting instruction may be (re-)executed via RFE; a negated
+ *   errno otherwise (the caller then panics / aborts the faulting task).
+ *
+ ****************************************************************************/
+
+int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs)
+{
+  uintptr_t vaddr = (uintptr_t)regs[REG_EXCVADDR];
+  uintptr_t pc    = (uintptr_t)regs[REG_PC];
+
+#ifdef CONFIG_ESP32S3_PAGEFAULT_SELFTEST
+  if (exccause == EXCCAUSE_LOAD_PROHIBITED && vaddr == PF_SELFTEST_VADDR)
+    {
+      g_pf_selftest_hits++;
+
+      _alert("PAGEFAULT SELFTEST: restart #%d precise LoadProhibited "
+             "EXCVADDR=%08" PRIxPTR " PC=%08" PRIxPTR "\n",
+             g_pf_selftest_hits, vaddr, pc);
+
+      if (g_pf_selftest_hits < PF_SELFTEST_REPEATS)
+        {
+          /* Return serviced without changing anything: the RFE must
+           * re-execute the identical faulting load and land back here.
+           */
+
+          return OK;
+        }
+
+      /* Proof complete: step past the 2-byte l32i.n so the task resumes. */
+
+      regs[REG_PC]       = pc + 2;
+      g_pf_selftest_hits = 0;
+      _alert("PAGEFAULT SELFTEST: RFE cleanly restarted the load %d times; "
+             "resuming\n", PF_SELFTEST_REPEATS);
+      return OK;
+    }
+#endif
+
+  /* Report the precise fault (with its tracking EXCVADDR) and decline to
+   * service it, so the caller falls through to the panic / abort path.
+   */
+
+  _alert("cache fault: EXCCAUSE=%d EXCVADDR=%08" PRIxPTR " PC=%08" PRIxPTR
+         " task=%s\n", exccause, vaddr, pc,
+         get_task_name(this_task()));
+
+  return -EFAULT;
+}
diff --git a/arch/xtensa/src/esp32s3/esp32s3_user.c 
b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h
similarity index 55%
copy from arch/xtensa/src/esp32s3/esp32s3_user.c
copy to arch/xtensa/src/esp32s3/esp32s3_pagefault.h
index cb1c43e9949..13ed1dbbcf3 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_user.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/xtensa/src/esp32s3/esp32s3_user.c
+ * arch/xtensa/src/esp32s3/esp32s3_pagefault.h
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,6 +18,9 @@
  *
  ****************************************************************************/
 
+#ifndef __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H
+#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
@@ -26,58 +29,34 @@
 
 #include <stdint.h>
 
-#include "xtensa.h"
-#ifdef CONFIG_ESPRESSIF_SPIFLASH
-#include "esp_private/cache_utils.h"
-#endif
-
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
-/****************************************************************************
- * Private Data
- ****************************************************************************/
-
 /****************************************************************************
- * Private Functions
+ * Public Function Prototypes
  ****************************************************************************/
 
 /****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: xtensa_user
+ * Name: esp32s3_pagefault_dispatch
  *
  * Description:
- *   ESP32-S3-specific user exception handler.
+ *   Service a precise PMS permission fault (EXCCAUSE Load/Store/InstrFetch
+ *   Prohibited).  This is the recoverable-fault entry point invoked from the
+ *   Xtensa user exception handler (xtensa_user()).
+ *
+ *   The faulting data address is read from the register save area
+ *   (regs[REG_EXCVADDR]).  If the fault is serviced, the handler leaves the
+ *   saved PC (regs[REG_PC] == EPC1) unchanged so that, upon return, the RFE
+ *   in the exception vector re-executes the faulting instruction.
  *
  * Input Parameters:
- *   exccause - Identifies the EXCCAUSE of the user exception.
- *   regs     - The register save are at the time of the interrupt.
+ *   exccause - The EXCCAUSE value (20/28/29).
+ *   regs     - Pointer to the register save area.
  *
  * Returned Value:
- *   Does not return.
+ *   OK if the fault was serviced and the instruction should be retried via
+ *   RFE; a negated errno value if the fault is not recoverable (the caller
+ *   then panics / terminates the faulting task).
  *
  ****************************************************************************/
 
-uint32_t *xtensa_user(int exccause, uint32_t *regs)
-{
-#ifdef CONFIG_ESPRESSIF_SPIFLASH
-  int cpu = this_cpu();
-
-  if (!spi_flash_cache_enabled())
-    {
-      spi_flash_restore_cache(cpu, 0);
-    }
-#endif /* CONFIG_ESPRESSIF_SPIFLASH */
-
-  /* xtensa_user_panic never returns. */
-
-  xtensa_user_panic(exccause, regs);
+int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs);
 
-  while (1)
-    {
-    }
-}
+#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H */
diff --git a/arch/xtensa/src/esp32s3/esp32s3_user.c 
b/arch/xtensa/src/esp32s3/esp32s3_user.c
index cb1c43e9949..88050c5eb89 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_user.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_user.c
@@ -27,6 +27,10 @@
 #include <stdint.h>
 
 #include "xtensa.h"
+#include <arch/xtensa/xtensa_corebits.h>
+#ifdef CONFIG_ESP32S3_PAGEFAULT
+#include "esp32s3_pagefault.h"
+#endif
 #ifdef CONFIG_ESPRESSIF_SPIFLASH
 #include "esp_private/cache_utils.h"
 #endif
@@ -73,6 +77,30 @@ uint32_t *xtensa_user(int exccause, uint32_t *regs)
     }
 #endif /* CONFIG_ESPRESSIF_SPIFLASH */
 
+#ifdef CONFIG_ESP32S3_PAGEFAULT
+  /* A cache-attribute permission violation raises a precise, restartable
+   * exception: Load/Store/InstrFetch Prohibited (EXCCAUSE 28/29/20), with
+   * EXCVADDR holding the exact faulting address.  This is proven on silicon
+   * (see esp32s3_pagefault.c) and is the recoverable-fault primitive.  Offer
+   * these to the dispatcher; if serviced, return the register frame so that
+   * the RFE in the exception vector re-executes the faulting instruction.
+   *
+   * Note: ESP32-S3 PMS (World Controller) memory-protection violations are
+   * NOT delivered as these precise causes; they raise the asynchronous
+   * DRAM0/IRAM0 PMS-monitor interrupt instead (handled elsewhere).
+   */
+
+  if (exccause == EXCCAUSE_LOAD_PROHIBITED  ||
+      exccause == EXCCAUSE_STORE_PROHIBITED ||
+      exccause == EXCCAUSE_INSTR_PROHIBITED)
+    {
+      if (esp32s3_pagefault_dispatch(exccause, regs) == OK)
+        {
+          return regs;
+        }
+    }
+#endif
+
   /* xtensa_user_panic never returns. */
 
   xtensa_user_panic(exccause, regs);

Reply via email to