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);
