diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index 8b941c160a2..58c185b5081 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -956,6 +956,27 @@ config ESP32S3_PAGEFAULT This is the foundation for guard pages, lazy stack/heap growth and, ultimately, demand paging / copy-on-write on the ESP32-S3. +config ESP32S3_USERFAULT_ABORT + bool "Abort a faulting user task instead of panicking" + default y + depends on !BUILD_FLAT + select SIG_DEFAULT + select SIG_SIGKILL_ACTION + ---help--- + When an unprivileged task takes a fault that cannot be serviced, + deliver SIGSEGV to it so its default action terminates only that task + and the rest of the system keeps running, instead of a whole-system + panic. Kernel-mode faults still panic: a kernel thread, a fault + inside a system call, and a fault while handling an interrupt all run + with PS.UM clear and there is no safe task to kill. + + This is deliberately independent of ESP32S3_PAGEFAULT. Servicing a + fault so the instruction can be re-executed and refusing to let a + rogue task halt the machine are separate capabilities, and the second + is one you always want: a user task can raise an unaligned load, a + divide by zero, a privileged instruction or a corrupt opcode, none of + which any dispatcher can service. + if ESP32S3_PAGEFAULT config ESP32S3_PAGEFAULT_SELFTEST diff --git a/arch/xtensa/src/esp32s3/Make.defs b/arch/xtensa/src/esp32s3/Make.defs index 574a99a599f..f299c2c6f54 100644 --- a/arch/xtensa/src/esp32s3/Make.defs +++ b/arch/xtensa/src/esp32s3/Make.defs @@ -50,6 +50,14 @@ ifneq ($(CONFIG_BUILD_FLAT),y) CHIP_CSRCS += esp32s3_isolation.c endif +# Terminating a faulting user task rather than panicking is independent of +# the recoverable-fault dispatcher, so it is built whenever there is an +# unprivileged world at all. + +ifeq ($(CONFIG_ESP32S3_USERFAULT_ABORT),y) +CHIP_CSRCS += esp32s3_userfault.c +endif + ifeq ($(CONFIG_BUILD_KERNEL),y) CHIP_ASRCS += esp32s3_world1_vectors.S endif diff --git a/arch/xtensa/src/esp32s3/esp32s3_isolation.c b/arch/xtensa/src/esp32s3/esp32s3_isolation.c index 9521ac2f42f..abd421de6f0 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_isolation.c +++ b/arch/xtensa/src/esp32s3/esp32s3_isolation.c @@ -37,7 +37,7 @@ #include #include -#ifdef CONFIG_ESP32S3_PAGEFAULT_ABORT +#ifdef CONFIG_ESP32S3_USERFAULT_ABORT #include #include #include @@ -61,7 +61,7 @@ #include "soc/extmem_reg.h" -#ifdef CONFIG_ESP32S3_PAGEFAULT_ABORT +#ifdef CONFIG_ESP32S3_USERFAULT_ABORT #include "sched/sched.h" #include "signal/signal.h" #endif @@ -107,7 +107,7 @@ extern void _xtensa_level3_vector(void); * Private Functions ****************************************************************************/ -#ifdef CONFIG_ESP32S3_PAGEFAULT_ABORT +#ifdef CONFIG_ESP32S3_USERFAULT_ABORT /**************************************************************************** * Name: pms_clear_violations @@ -169,7 +169,7 @@ static void IRAM_ATTR pms_clear_violations(void) static int IRAM_ATTR pms_violation_isr(int cpuint, void *context, void *arg) { -#ifdef CONFIG_ESP32S3_PAGEFAULT_ABORT +#ifdef CONFIG_ESP32S3_USERFAULT_ABORT uint32_t *regs = (uint32_t *)context; /* Acknowledge and re-arm the monitors first so the level-triggered diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c index 69b356bc2e6..4d029d7a585 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c +++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include diff --git a/arch/xtensa/src/esp32s3/esp32s3_user.c b/arch/xtensa/src/esp32s3/esp32s3_user.c index 88050c5eb89..4a4183feb89 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_user.c +++ b/arch/xtensa/src/esp32s3/esp32s3_user.c @@ -31,6 +31,7 @@ #ifdef CONFIG_ESP32S3_PAGEFAULT #include "esp32s3_pagefault.h" #endif +#include "esp32s3_userfault.h" #ifdef CONFIG_ESPRESSIF_SPIFLASH #include "esp_private/cache_utils.h" #endif @@ -101,6 +102,39 @@ uint32_t *xtensa_user(int exccause, uint32_t *regs) } #endif +#ifdef CONFIG_ESP32S3_USERFAULT_ABORT + /* Nothing serviced it, so this fault is not going away by re-executing. + * If the interruptee was unprivileged, terminate just that task. + * + * The test is on the interruptee's User Mode bit and NOT on the cause, and + * that is the whole point. A user task has many ways to raise a + * synchronous exception that no dispatcher can service -- an unaligned + * load (EXCCAUSE 9), a divide by zero (6), a privileged instruction (8), a + * load/store error (3), a corrupt opcode (0) -- and gating on a list of + * causes means every cause left off the list is a way for an unprivileged + * task to stop the machine. Whichever way it arose, a user task running + * garbage must not take the system down with it. + * + * PS.UM also excludes the cases where there is no safe task to kill: a + * kernel thread, a fault inside a system call made on the user's behalf, + * and a fault while handling an interrupt all run with it clear, and fall + * through to the panic below. + * + * One cause deserves its own note. A *denied* external-memory access does + * not trap on this chip. TRM v1.8 p.699: an access without permission is + * "responded with 0 (for internal memory) or 0xdeadbeaf (for external + * memory)". So an unprivileged branch into kernel text in flash or PSRAM + * is refused silently, the CPU executes the dummy word it was handed + * instead, and the refusal surfaces here as EXCCAUSE_ILLEGAL at the + * address that was branched to -- never as EXCCAUSE_INSTR_PROHIBITED. + */ + + if ((regs[REG_PS] & PS_UM) != 0) + { + return esp32s3_userfault_abort(exccause, regs); + } +#endif + /* xtensa_user_panic never returns. */ xtensa_user_panic(exccause, regs); diff --git a/arch/xtensa/src/esp32s3/esp32s3_userfault.c b/arch/xtensa/src/esp32s3/esp32s3_userfault.c new file mode 100644 index 00000000000..becff2125b5 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_userfault.c @@ -0,0 +1,103 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_userfault.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "xtensa.h" +#include "sched/sched.h" +#include "signal/signal.h" + +#include "esp32s3_userfault.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_userfault_abort + * + * Description: + * Terminate just the faulting unprivileged task by delivering a fatal + * SIGSEGV, instead of panicking the whole system. + * + * This is deliberately independent of what the fault was. A user task + * must not be able to stop the machine, and it has many ways to raise a + * synchronous exception that no dispatcher can service: an unaligned + * load, a divide by zero, a privileged instruction, a corrupt opcode. All + * of them arrive here through the same vector with the same frame, so all + * of them get the same answer. Only the caller decides who is eligible -- + * see xtensa_user(), which gates on the interruptee's PS.UM. + * + * Mirrors the interrupt-dispatch handshake: record the exception frame as + * the task context, dispatch the signal -- which redirects the task to the + * signal trampoline via up_schedule_sigaction() -- then return the + * redirected frame so the vector's RFE resumes the task in the trampoline, + * whose SIGSEGV default action (_exit) tears the task down and resumes. + * + * Input Parameters: + * exccause - The EXCCAUSE of the user exception, for reporting + * regs - The register save area at the time of the exception + * + * Returned Value: + * The register frame to resume (the signal trampoline for the faulting + * task). + * + ****************************************************************************/ + +uint32_t *esp32s3_userfault_abort(int exccause, uint32_t *regs) +{ + struct tcb_s *tcb = this_task(); + siginfo_t info; + + _alert("SIGSEGV task %s: EXCCAUSE=%d EXCVADDR=%08x PC=%08x\n", + get_task_name(tcb), exccause, (unsigned)regs[REG_EXCVADDR], + (unsigned)regs[REG_PC]); + + up_set_interrupt_context(true); + tcb->xcp.regs = regs; + + info.si_signo = SIGSEGV; + info.si_code = SI_USER; + info.si_errno = 0; + info.si_value.sival_ptr = (FAR void *)regs[REG_EXCVADDR]; + + nxsig_tcbdispatch(tcb, &info, false); + + regs = tcb->xcp.regs; + tcb->xcp.regs = NULL; + up_set_interrupt_context(false); + return regs; +} diff --git a/arch/xtensa/src/esp32s3/esp32s3_userfault.h b/arch/xtensa/src/esp32s3/esp32s3_userfault.h new file mode 100644 index 00000000000..c3083e53e12 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_userfault.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_userfault.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_USERFAULT_H +#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_USERFAULT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#ifdef CONFIG_ESP32S3_USERFAULT_ABORT + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_userfault_abort + * + * Description: + * Terminate the faulting unprivileged task with SIGSEGV rather than + * panicking the system. Cause-agnostic; the caller decides eligibility. + * + * Input Parameters: + * exccause - The EXCCAUSE of the user exception, for reporting + * regs - The register save area at the time of the exception + * + * Returned Value: + * The register frame to resume. + * + ****************************************************************************/ + +uint32_t *esp32s3_userfault_abort(int exccause, uint32_t *regs); + +#endif /* CONFIG_ESP32S3_USERFAULT_ABORT */ +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_USERFAULT_H */ diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/ksta_softap/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/ksta_softap/defconfig index 0602d0f9b95..259f82d008b 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/configs/ksta_softap/defconfig +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/ksta_softap/defconfig @@ -88,7 +88,6 @@ CONFIG_SCHED_CHILD_STATUS=y CONFIG_SCHED_HAVE_PARENT=y CONFIG_SCHED_LPWORK=y CONFIG_SCHED_WAITPID=y -CONFIG_SIG_DEFAULT=y CONFIG_START_DAY=6 CONFIG_START_MONTH=12 CONFIG_START_YEAR=2011