diff --git a/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst b/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst index dae236dbfada8..77b01e97d026c 100644 --- a/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst +++ b/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst @@ -372,6 +372,63 @@ After successfully built and flashed, run on the boards's terminal:: The corresponding output should show related debug information. +kernel_n8r2 +----------- + +A KERNEL build for a module with 2 MB of PSRAM, such as the +ESP32-S3-WROOM-1-N8R2. The flash is quad and runs in DIO mode. + +The page pool is smaller than in ``kernel_oct``, and each of the text, data +and heap regions of a process is 2 pages of 64 KiB. A process therefore takes +384 KiB, and a ``fork()`` needs 768 KiB while the parent and the child both +exist. + +This is tight on purpose. ``ostest`` has 115 KiB of text against a 128 KiB +text region. A larger program needs a module with more PSRAM, not a larger +page pool. + +kernel_oct +---------- + +A KERNEL build for a module with octal flash and 8 MB of PSRAM, such as the +ESP32-S3-WROOM-2-N32R8V. + +A KERNEL build gives each process its own address environment. This is the +only build mode in which ``fork()`` is available on this chip: the child +receives its own copy of the memory of the parent, at the same virtual +addresses. ``vfork()`` is available in every build mode. + +The page pool holds the pages of every process. It is carved out of the +PSRAM, and it is not kept mapped into the kernel address space. The kernel +reaches a page of the pool through a small scratch region, mapped for one +operation and invalidated afterwards. + +Build the kernel first, then the applications, then the boot ROMFS, and then +link the kernel again. The ROMFS is linked into the kernel image, so a change +to an application needs the whole chain:: + + make -j + make export + cd ../apps + ./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz + make import -j + ./tools/mkromfsimg.sh ../nuttx/arch/xtensa/src/board/board/romfs_boot.c + cd ../nuttx + make -j + +Confirm that the ROMFS is in the image. ``romfs_img`` must be a strong symbol +of a few hundred kilobytes:: + + xtensa-esp32s3-elf-nm -S nuttx | grep " romfs_img$" + +A one byte symbol means that the linker took the stub. Delete +``boards/xtensa/esp32s3/esp32s3-devkit/src/romfs_stub.o`` and ``libboard.a``, +then link again. + +The shell needs the full path of a program in this build mode:: + + nsh> /system/bin/ostest + knsh ---- diff --git a/arch/Kconfig b/arch/Kconfig index 60039f1991e4e..c04a428a52c11 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -158,6 +158,8 @@ config ARCH_X86_64 config ARCH_XTENSA bool "Xtensa" select ARCH_HAVE_BACKTRACE + select ARCH_HAVE_VFORK + select ARCH_HAVE_FORK if BUILD_KERNEL && ARCH_ADDRENV select ARCH_HAVE_CPUINFO select ARCH_HAVE_INTERRUPTSTACK select ARCH_HAVE_STACKCHECK @@ -1034,8 +1036,6 @@ config ARCH_PGPOOL_MAPPING Otherwise, a temporary mapping will have to be established each time it is necessary to modify the contents of a page. -if ARCH_PGPOOL_MAPPING - config ARCH_PGPOOL_PBASE hex "Page pool physical address" default 0x0 @@ -1045,14 +1045,9 @@ config ARCH_PGPOOL_PBASE but is required again in order to modularize the common address environment logic. -config ARCH_PGPOOL_VBASE - hex "Page pool virtual address" - default 0x0 - ---help--- - The virtual address of the start of the page pool memory. This - setting is probably equivalent to other platform specific definitions - but is required again in order to modularize the common address - environment logic. + This applies whether or not the pool is statically mapped: the page + allocator hands out physical addresses either way. Only + ARCH_PGPOOL_VBASE depends on ARCH_PGPOOL_MAPPING. config ARCH_PGPOOL_SIZE int "Page pool size (bytes)" @@ -1062,6 +1057,17 @@ config ARCH_PGPOOL_SIZE equivalent to other platform specific definitions but is required again in order to modularize the common address environment logic. +if ARCH_PGPOOL_MAPPING + +config ARCH_PGPOOL_VBASE + hex "Page pool virtual address" + default 0x0 + ---help--- + The virtual address of the start of the page pool memory. This + setting is probably equivalent to other platform specific definitions + but is required again in order to modularize the common address + environment logic. + endif # ARCH_PGPOOL_MAPPING endif # ARCH_ADDRENV && ARCH_NEED_ADDRENV_MAPPING diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 5f25569d86687..ed5a5fe4f323f 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -85,6 +85,10 @@ config ARCH_CHIP_ESP32S3 select ARCH_HAVE_DEBUG select ARCH_HAVE_FPU select ARCH_HAVE_MPU + select ARCH_HAVE_MMU + select ARCH_HAVE_ADDRENV + select ARCH_NEED_ADDRENV_MAPPING + select ARCH_HAVE_ELF_EXECUTABLE select ARCH_HAVE_MULTICPU select ARCH_HAVE_RESET select ARCH_HAVE_TEXT_HEAP diff --git a/arch/xtensa/include/arch.h b/arch/xtensa/include/arch.h index cb68d0807098b..5d700dbe478a8 100644 --- a/arch/xtensa/include/arch.h +++ b/arch/xtensa/include/arch.h @@ -45,6 +45,56 @@ * Public Types ****************************************************************************/ +#ifdef CONFIG_ARCH_ADDRENV +#ifndef __ASSEMBLY__ + +/* The ESP32-S3 has no general paging MMU and no per-process page-table root. + * External memory (PSRAM) is reached through a single global cache-MMU remap + * table with separate instruction-bus (.text) and data-bus (.data/.bss/heap) + * address windows, at 64 KB page granularity. An address environment is + * therefore described not by a page-table root (no satp/TTBR equivalent) but + * by the set of physical PSRAM pages that back the process plus the fixed + * virtual window bases. up_addrenv_select() makes an environment active by + * reprogramming the cache-MMU window entries to point at these pages, + * invalidating the cache, and reprogramming the PMS split lines that gate + * WORLD1 (user) access -- with a fast path when the incoming environment is + * already the active one (thread<->thread, ISR, syscall). + */ + +struct arch_addrenv_s +{ + /* Virtual bases and heap size. Returned by up_addrenv_vtext/vdata/vheap + * and up_addrenv_heapsize. .text maps through the instruction-bus window, + * .data/.bss and the heap through the data-bus window. + */ + + uintptr_t textvbase; + uintptr_t datavbase; + uintptr_t heapvbase; + size_t heapsize; + + /* Physical PSRAM page addresses backing each region -- one 64 KB page per + * entry, allocated from the PSRAM page pool by up_addrenv_create(). + * Index i of a region backs virtual page i counted from that region's + * vbase. + */ + + uintptr_t textpages[CONFIG_ARCH_TEXT_NPAGES]; + uintptr_t datapages[CONFIG_ARCH_DATA_NPAGES]; + uintptr_t heappages[CONFIG_ARCH_HEAP_NPAGES]; + + /* Number of pages actually allocated in each region */ + + uint16_t ntext; + uint16_t ndata; + uint16_t nheap; +}; + +typedef struct arch_addrenv_s arch_addrenv_t; + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_ARCH_ADDRENV */ + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/arch/xtensa/include/irq.h b/arch/xtensa/include/irq.h index 537262ebae3ae..d2005dcbcad92 100644 --- a/arch/xtensa/include/irq.h +++ b/arch/xtensa/include/irq.h @@ -216,7 +216,34 @@ struct xcptcontext uint32_t *regs; +#ifdef CONFIG_ARCH_KERNEL_STACK + /* In a kernel build the kernel cannot run on the stack of the process it + * is working for. That stack lives in a cache-MMU window which + * up_addrenv_select() reprograms, so it would move out from under the + * kernel the moment it touched another process's address environment -- + * taking the exception frame and every spilled register window with it. + * Each thread therefore gets a small stack of its own in kernel memory, + * which no address environment change can disturb. + */ + + uint32_t *kstack; /* Allocated base of the kernel stack */ + uint32_t *ktopstk; /* Top of the kernel stack (initial stack pointer) */ + uint32_t *ustkptr; /* Saved user stack pointer, while in a system call */ + uint32_t *kstkptr; /* Saved kernel stack pointer, while a user signal + * handler runs on the user stack */ +#endif + #ifdef CONFIG_LIB_SYSCALL + /* The exception frame of the system call currently in progress, i.e. the + * caller's register context as the vector saved it. A system call body + * runs as ordinary C code long after the exception has been dispatched, so + * this is the only way for one to reach the registers of the thread that + * made the call -- vfork() needs the caller's stack pointer and return + * address to give the child a copy. + */ + + uint32_t *sregs; + /* The following array holds the return address and the exc_return value * needed to return from each nested system call. */ diff --git a/arch/xtensa/src/Makefile b/arch/xtensa/src/Makefile index 31425e38fa3bc..522e622be0cf3 100644 --- a/arch/xtensa/src/Makefile +++ b/arch/xtensa/src/Makefile @@ -199,10 +199,20 @@ ifneq ($(CONFIG_WINDOWS_NATIVE),y) endif # This is part of the top-level export target +# +# A kernel build links its user programs outside this tree, against the +# export package, so crt0 has to travel with it: apps/import expects to +# find it as startup/crt0.o. + +ifeq ($(CONFIG_BUILD_KERNEL),y) +EXPORT_STARTUP_OBJS = $(STARTUP_OBJS) $(STARTUP_ELF_OBJS) +else +EXPORT_STARTUP_OBJS = $(STARTUP_OBJS) +endif -export_startup: $(STARTUP_OBJS) +export_startup: $(EXPORT_STARTUP_OBJS) $(Q) if [ -d "$(EXPORT_DIR)/startup" ]; then \ - cp -f $(STARTUP_OBJS) "$(EXPORT_DIR)/startup"; \ + cp -f $(EXPORT_STARTUP_OBJS) "$(EXPORT_DIR)/startup"; \ else \ echo "$(EXPORT_DIR)/startup does not exist"; \ exit 1; \ diff --git a/arch/xtensa/src/common/Make.defs b/arch/xtensa/src/common/Make.defs index 3e1ea13a78582..82be3de9de413 100644 --- a/arch/xtensa/src/common/Make.defs +++ b/arch/xtensa/src/common/Make.defs @@ -39,6 +39,11 @@ CMN_CSRCS += xtensa_modifyreg8.c xtensa_modifyreg16.c xtensa_modifyreg32.c CMN_CSRCS += xtensa_mpu.c xtensa_nputs.c xtensa_oneshot.c xtensa_perf.c CMN_CSRCS += xtensa_releasestack.c xtensa_registerdump.c CMN_CSRCS += xtensa_swint.c xtensa_stackframe.c + +ifneq ($(CONFIG_ARCH_HAVE_FORK)$(CONFIG_ARCH_HAVE_VFORK),) +CMN_CSRCS += xtensa_fork.c +endif + CMN_CSRCS += xtensa_saveusercontext.c CMN_CSRCS += xtensa_usestack.c xtensa_tcbinfo.c @@ -81,7 +86,11 @@ ifeq ($(CONFIG_XTENSA_SEMIHOSTING_HOSTFS),y) CMN_CSRCS += xtensa_hostfs.c endif -ifeq ($(CONFIG_BUILD_PROTECTED),y) +ifeq ($(CONFIG_ARCH_KERNEL_STACK),y) + CMN_CSRCS += xtensa_addrenv_kstack.c +endif + +ifneq ($(CONFIG_BUILD_FLAT),y) CMN_UASRCS += xtensa_signal_handler.S CMN_ASRCS += xtensa_dispatch_syscall.S CMN_CSRCS += xtensa_task_start.c xtensa_pthread_start.c diff --git a/arch/xtensa/src/common/crt0.c b/arch/xtensa/src/common/crt0.c index 89b21b78db07e..5dd1458e9822a 100644 --- a/arch/xtensa/src/common/crt0.c +++ b/arch/xtensa/src/common/crt0.c @@ -25,6 +25,7 @@ ****************************************************************************/ #include +#include #include #include @@ -55,6 +56,53 @@ int main(int argc, char *argv[]); * Private Functions ****************************************************************************/ +#ifdef CONFIG_BUILD_KERNEL + +/**************************************************************************** + * Name: sig_trampoline + * + * Description: + * The user-space signal handler trampoline. A kernel build cannot reach + * the one in xtensa_signal_handler.S -- that lives in libarch, which user + * programs do not link -- so it is carried here in crt0 instead, and + * _start() publishes it to the kernel through ARCH_DATA_RESERVE. The + * kernel enters it from the SYS_signal_handler case of xtensa_swint(). + * + * Written as file-scope assembly rather than as a naked function because + * GCC does not implement the naked attribute on Xtensa: it would emit a + * window-rotating prologue and quietly invalidate the register assignments + * below. + * + * Input Parameters: + * a2 = sighand, the user-space signal handling function + * a3, a4, a5 = signo, info and ucontext, its arguments + * + * Returned Value: + * None. This function does not return in the normal sense; it returns + * via the SYS_signal_handler_return syscall. + * + ****************************************************************************/ + +__asm__ +( + " .text\n" + " .global sig_trampoline\n" + " .type sig_trampoline, @function\n" + " .align 4\n" + "sig_trampoline:\n" + " mov a6, a3\n" /* Move signo into the callee's a2 */ + " mov a7, a4\n" /* Move info into the callee's a3 */ + " mov a8, a5\n" /* Move ucontext into the callee's a4 */ + " callx4 a2\n" /* Call the signal handler */ + " movi a2, " STRINGIFY(SYS_signal_handler_return) "\n" + " syscall\n" /* Will not return */ + " .size sig_trampoline, .-sig_trampoline\n" +); + +void sig_trampoline(void); + +#endif /* CONFIG_BUILD_KERNEL */ + #ifdef CONFIG_HAVE_CXXINITIALIZE /**************************************************************************** diff --git a/arch/xtensa/src/common/xtensa_addrenv_kstack.c b/arch/xtensa/src/common/xtensa_addrenv_kstack.c new file mode 100644 index 0000000000000..57bb749588062 --- /dev/null +++ b/arch/xtensa/src/common/xtensa_addrenv_kstack.c @@ -0,0 +1,129 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_addrenv_kstack.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 "xtensa.h" + +#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_ARCH_KERNEL_STACK) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The Xtensa windowed ABI requires 16-byte stack alignment */ + +#define KSTACK_ALIGNMENT 16 +#define KSTACK_ALIGN_DOWN(a) ((a) & ~(KSTACK_ALIGNMENT - 1)) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_addrenv_kstackalloc + * + * Description: + * This function is called when a new thread is created to allocate the + * new thread's kernel stack. This function may be called for certain + * terminating threads which have no kernel stack. It must be tolerant of + * that case. + * + * The stack comes from the kernel heap, which lives in internal SRAM and + * is mapped identically no matter which address environment is selected. + * That is the whole point of it: the kernel needs somewhere to keep the + * exception frame and its spilled register windows that does not move when + * up_addrenv_select() reprograms the user cache-MMU windows. + * + * Input Parameters: + * tcb - The TCB of the thread that requires the kernel stack. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_kstackalloc(struct tcb_s *tcb) +{ + DEBUGASSERT(tcb && tcb->xcp.kstack == NULL); + + tcb->xcp.kstack = kmm_memalign(KSTACK_ALIGNMENT, ARCH_KERNEL_STACKSIZE); + if (tcb->xcp.kstack == NULL) + { + berr("ERROR: Failed to allocate the kernel stack\n"); + return -ENOMEM; + } + + /* Xtensa stacks grow down and must stay aligned, so the usable top is the + * far end of the allocation. + */ + + tcb->xcp.ktopstk = (uint32_t *) + KSTACK_ALIGN_DOWN((uintptr_t)tcb->xcp.kstack + ARCH_KERNEL_STACKSIZE); + + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_kstackfree + * + * Description: + * This function is called when any thread exits. This function frees + * the kernel stack. + * + * Input Parameters: + * tcb - The TCB of the thread that no longer requires the kernel stack. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_kstackfree(struct tcb_s *tcb) +{ + DEBUGASSERT(tcb); + + /* Does the exiting thread have a kernel stack? */ + + if (tcb->xcp.kstack != NULL) + { + kmm_free(tcb->xcp.kstack); + tcb->xcp.kstack = NULL; + tcb->xcp.ktopstk = NULL; + } + + return OK; +} + +#endif /* CONFIG_ARCH_ADDRENV && CONFIG_ARCH_KERNEL_STACK */ diff --git a/arch/xtensa/src/common/xtensa_fork.c b/arch/xtensa/src/common/xtensa_fork.c new file mode 100644 index 0000000000000..e45ed5ba8c079 --- /dev/null +++ b/arch/xtensa/src/common/xtensa_fork.c @@ -0,0 +1,455 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_fork.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 +#include + +#include "sched/sched.h" +#include "xtensa.h" +#include "chip_macros.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The base save area: the 16 bytes below a frame's stack pointer, holding + * the spilled a0-a3 of that frame's caller. Window overflow writes them and + * underflow reads them back, so it is the link that makes the frame chain + * walkable, and the reason a copy starting at the stack pointer is missing + * its first link. + */ + +#define BASE_SAVE_AREA 16 +#define BASE_SAVE_A1 1 /* a0, a1, a2, a3 -- a1 is the caller's SP */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Everything a child is built from: the register context of the thread that + * called, and where that thread was. There are two ways to come by it, and + * they differ in more than provenance -- see xtensa_fork_direct() below. + */ + +struct fork_snapshot_s +{ + FAR const uint32_t *regs; /* The caller's full register context */ + uintptr_t usp; /* The caller's user stack pointer */ + uintptr_t pc; /* Where the child resumes */ + uint32_t a2; /* What the child sees returned in A2 */ +#ifndef CONFIG_BUILD_FLAT + uintptr_t ctx; /* The caller's privilege, from its syscall */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xtensa_fork_rebase + * + * Description: + * Rebase the frame chain of a relocated stack copy. + * + * A windowed ABI stores each frame's caller stack pointer absolutely, in + * the base save area at [sp - 16), so a copy taken at a different address + * still names the parent and the child's first retw would underflow onto + * the parent's stack. Each link gets the relocation offset added. + * + * Spilled a4-a15 and any other stack addresses in the copy are left alone. + * Those are data; the chain is what the child needs to return at all. + * + * Input Parameters: + * newsp - The child's stack pointer + * usp - The parent's stack pointer, which newsp is a relocation of + * stacktop - The top of the parent's stack; the walk ends there + * offset - newsp - usp, the amount every link moves by + * + ****************************************************************************/ + +static void xtensa_fork_rebase(uintptr_t newsp, uintptr_t usp, + uintptr_t stacktop, intptr_t offset) +{ + uintptr_t csp = newsp; /* The frame being fixed, in the child's copy */ + uintptr_t psp = usp; /* The same frame, as the parent addresses it */ + + while (psp < stacktop) + { + FAR uint32_t *save = (FAR uint32_t *)(csp - BASE_SAVE_AREA); + uintptr_t caller = save[BASE_SAVE_A1]; + + /* The chain grows towards the top of the stack and ends there. Stop + * on anything else rather than following it: the outermost frame's + * save area was never written by an overflow, so what is in it is + * whatever the stack was coloured with. + */ + + if (caller <= psp || caller > stacktop) + { + break; + } + + save[BASE_SAVE_A1] = (uint32_t)(uintptr_t)((intptr_t)caller + offset); + + psp = caller; + csp = (uintptr_t)((intptr_t)caller + offset); + } +} + +/**************************************************************************** + * Name: xtensa_fork_stack + * + * Description: + * Give the child its stack pointer, copying the parent's frames if needed. + * + * A fork() child keeps the parent's stack addresses inside its own address + * environment, so it has nothing to copy. A vfork() child gets a stack of + * its own, which needs the copy and xtensa_fork_rebase() with it. + * + * The copy starts one base save area below the stack pointer: the frame + * the child resumes into keeps its caller's spilled a0-a3 there. + * + * Input Parameters: + * parent - The parent task's TCB + * child - The child task's TCB + * usp - The parent's stack pointer + * + * Returned Value: + * The child's stack pointer. + * + ****************************************************************************/ + +static uintptr_t xtensa_fork_stack(FAR struct tcb_s *parent, + FAR struct tcb_s *child, + uintptr_t usp) +{ + uintptr_t stacktop; + uintptr_t stackutil; + uintptr_t newtop; + uintptr_t newsp; + + stacktop = (uintptr_t)parent->stack_base_ptr + parent->adj_stack_size; + DEBUGASSERT(stacktop > usp); + + if (child->stack_base_ptr == parent->stack_base_ptr) + { + /* The child is running at the parent's stack addresses, inside its + * own duplicated address environment. There is nothing to relocate: + * every stack address the child inherits is still the address it + * names. + */ + + return usp; + } + + DEBUGASSERT(usp - BASE_SAVE_AREA >= (uintptr_t)parent->stack_base_ptr); + + stackutil = stacktop - (usp - BASE_SAVE_AREA); + newtop = (uintptr_t)child->stack_base_ptr + child->adj_stack_size; + + /* The copy has to fit, and the register save area goes below it when the + * child has no kernel stack to put it on -- see xtensa_fork(). + */ + + DEBUGASSERT(newtop - stackutil > + (uintptr_t)child->stack_base_ptr + XCPTCONTEXT_SIZE); + + newsp = newtop - stackutil + BASE_SAVE_AREA; + + memcpy((FAR void *)(newsp - BASE_SAVE_AREA), + (FAR const void *)(usp - BASE_SAVE_AREA), stackutil); + + xtensa_fork_rebase(newsp, usp, stacktop, (intptr_t)(newsp - usp)); + + return newsp; +} + +/**************************************************************************** + * Name: xtensa_fork + * + * Description: + * The common core of the two primitives. They differ in the flag handed + * to nxtask_setup_fork(), which is where the memory semantics are decided, + * and in where their snapshot of the caller comes from. + * + * Input Parameters: + * vfork - true for vfork(), false for fork() + * snap - The caller's context; see struct fork_snapshot_s + * + * Returned Value: + * The pid of the child, or ERROR on failure. + * + ****************************************************************************/ + +static pid_t xtensa_fork(bool vfork, FAR const struct fork_snapshot_s *snap) +{ + FAR struct tcb_s *parent = this_task(); + FAR struct tcb_s *child; + uintptr_t newsp; + uintptr_t regstop; + + DEBUGASSERT(snap->regs != NULL && snap->pc != 0); + + /* Allocate and initialise a TCB for the child. The start address is only + * bookkeeping here: what the child actually resumes with is the register + * context assembled below. + */ + + child = nxtask_setup_fork((start_t)snap->pc, vfork); + if (child == NULL) + { + sinfo("nxtask_setup_fork failed\n"); + return (pid_t)ERROR; + } + + newsp = xtensa_fork_stack(parent, child, snap->usp); + + /* Where the child's register context is restored from. */ + +#ifdef CONFIG_ARCH_KERNEL_STACK + if (child->xcp.kstack != NULL) + { + /* On the kernel stack: the child is resumed by the same path a system + * call returns through, which runs in kernel context. + */ + + regstop = (uintptr_t)child->xcp.ktopstk; + } + else +#endif + { + /* There is no kernel stack, so it goes on the child's own stack, below + * the base save area rather than at the stack pointer. Writing + * XCPTCONTEXT_SIZE bytes down from newsp would destroy the very words + * the child's first `retw' reads. It is dead memory once the context + * has been restored, so the child may then grow over it. + * + * This is only sound because a child without a kernel stack always has + * a stack of its own: writing here on a shared stack would land in + * the parent's frames. See xtensa_fork_stack(). + */ + + DEBUGASSERT(child->stack_base_ptr != parent->stack_base_ptr); + regstop = newsp - BASE_SAVE_AREA; + } + + child->xcp.regs = (FAR uint32_t *)(regstop - XCPTCONTEXT_SIZE); + + /* Start from the parent's context, then correct what must differ */ + + memcpy(child->xcp.regs, snap->regs, XCPTCONTEXT_SIZE); + + /* The child is not returning the way the parent will: it is being + * started. Give it directly what its resume path would have produced -- + * the address to resume at, its privilege, its own stack, and the value + * the call returns to it. + * + * The privilege matters most. The frame copied above carries the world + * the *exception* left in it, not the caller's; taking it would resume an + * unprivileged process privileged. + */ + + child->xcp.regs[REG_PC] = snap->pc; + child->xcp.regs[REG_A1] = newsp; + child->xcp.regs[REG_A2] = snap->a2; + +#ifndef CONFIG_BUILD_FLAT + xtensa_restoreprivilege(child->xcp.regs, snap->ctx); +#endif + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* The parent is inside a system call, so its saved user stack pointer is + * held aside in ustkptr and A1 names the kernel stack. The child is not + * inside that call and must not inherit it. + */ + + child->xcp.ustkptr = NULL; +#endif + + /* And start the child. On failure nxtask_start_fork() discards the TCB + * through nxtask_abort_fork(). + */ + + return nxtask_start_fork(child, vfork); +} + +#ifdef CONFIG_LIB_SYSCALL +/**************************************************************************** + * Name: xtensa_fork_syscall + * + * Description: + * Fork from the system call that brought the caller into the kernel. + * + * There is no assembly counterpart to this, and it does not need one. + * Other architectures enter through a stub that spills the caller's + * registers into a struct fork_s, because a C function cannot see its + * caller's callee-saved registers. Here the system call has already done + * better than that: _xtensa_context_save() runs SPILL_ALL_WINDOWS on + * every exception entry, so every live register window of the calling + * thread has been written to its stack and the whole context is in the + * exception frame. Copying the stack therefore copies a complete and + * self-consistent frame chain. xcp.sregs is that frame (see + * xtensa_swint()). + * + * The child resumes where the system call would have returned, and A2 is + * the value the call yields -- 0, as fork() and vfork() return to a child. + * + ****************************************************************************/ + +static pid_t xtensa_fork_syscall(bool vfork) +{ + FAR struct tcb_s *parent = this_task(); + struct fork_snapshot_s snap; + int index; + + /* This runs as the body of a system call, so the caller's own state is not + * simply what is in the exception frame. + */ + + DEBUGASSERT(parent->xcp.sregs != NULL); + index = (int)parent->xcp.nsyscalls - 1; + DEBUGASSERT(index >= 0); + + snap.regs = parent->xcp.sregs; + snap.pc = parent->xcp.syscall[index].sysreturn; + snap.a2 = 0; +#ifndef CONFIG_BUILD_FLAT + snap.ctx = parent->xcp.syscall[index].int_ctx; +#endif + + /* The stack pointer to work from is the *user* one. A kernel build moves + * the outermost system call onto the thread's kernel stack and holds the + * user stack pointer aside in ustkptr, so the A1 in the exception frame + * names the kernel stack from here on -- measuring the parent's user stack + * against it would produce a nonsense length. + */ + +#ifdef CONFIG_ARCH_KERNEL_STACK + snap.usp = parent->xcp.ustkptr != NULL ? + (uintptr_t)parent->xcp.ustkptr : snap.regs[REG_A1]; +#else + snap.usp = snap.regs[REG_A1]; +#endif + + return xtensa_fork(vfork, &snap); +} + +#else /* CONFIG_LIB_SYSCALL */ + +/**************************************************************************** + * Name: xtensa_fork_direct + * + * Description: + * Fork a caller that did not arrive through a system call, which in a flat + * build is every caller. + * + * SYS_save_context spills every window and copies out the exception frame, + * giving the same snapshot a system call would have left behind. + * + * It is issued inline rather than through up_saveusercontext() because the + * snapshot records the stack pointer of the frame that issues it, and the + * child resumes on a copy of the stack from that point up. Everything + * called from here runs below this frame, so the copied region stays + * valid; a helper's frame would be dead and reused by the time the child + * ran on it. + * + * The child resumes after the syscall rather than at a syscall return, so + * the PC comes from the frame, and A2 is 1 so the branch below can tell + * parent from child. + * + * Returned Value: + * The pid of the child to the parent, 0 to the child, ERROR on failure. + * + ****************************************************************************/ + +static pid_t xtensa_fork_direct(bool vfork) +{ + uint32_t regs[XCPTCONTEXT_REGS] aligned_data(16); + struct fork_snapshot_s snap; + + if (sys_call1(SYS_save_context, (uintptr_t)regs) != 0) + { + /* The child, resumed from the context captured just above with A2 set + * to 1. It has nothing to do but leave. + */ + + return 0; + } + + snap.regs = regs; + snap.usp = regs[REG_A1]; + snap.pc = regs[REG_PC]; + snap.a2 = 1; + + return xtensa_fork(vfork, &snap); +} +#endif /* CONFIG_LIB_SYSCALL */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_fork + * + * Description: + * The architecture half of fork() and vfork(). With vfork true the child + * shares the parent's memory and runs on a private copy of its stack, and + * the parent is suspended until the child leaves. With vfork false the + * child receives its own copy of the parent's memory at the same virtual + * addresses. + * + * Input Parameters: + * vfork - true for vfork(), false for fork() + * + * Returned Value: + * Upon successful completion, 0 is returned to the child and the pid of + * the child to the parent. Otherwise, -1 is returned to the parent, no + * child is created, and errno is set to indicate the error. + * + ****************************************************************************/ + +pid_t up_fork(bool vfork) +{ +#ifdef CONFIG_LIB_SYSCALL + return xtensa_fork_syscall(vfork); +#else + return xtensa_fork_direct(vfork); +#endif +} diff --git a/arch/xtensa/src/common/xtensa_initialstate.c b/arch/xtensa/src/common/xtensa_initialstate.c index 1a0a14579c2e6..b64989f0ba5cf 100644 --- a/arch/xtensa/src/common/xtensa_initialstate.c +++ b/arch/xtensa/src/common/xtensa_initialstate.c @@ -78,14 +78,27 @@ void up_initial_state(struct tcb_s *tcb) { struct xcptcontext *xcp = &tcb->xcp; #ifdef CONFIG_SCHED_THREAD_LOCAL - const uint32_t base = ALIGN_UP((uint32_t)&_rodata_reserved_align, + const uintptr_t base = ALIGN_UP((uintptr_t)&_rodata_reserved_align, TCB_SIZE); #endif +#ifdef CONFIG_ARCH_KERNEL_STACK + /* The kernel stack is allocated before the thread's initial state is set + * up, so hold on to it across the wipe below. + */ + + uint32_t *kstack = xcp->kstack; + uint32_t *ktopstk = xcp->ktopstk; +#endif /* Initialize the initial exception register context structure */ memset(xcp, 0, sizeof(struct xcptcontext)); +#ifdef CONFIG_ARCH_KERNEL_STACK + xcp->kstack = kstack; + xcp->ktopstk = ktopstk; +#endif + /* Initialize the idle thread stack */ if (tcb->pid == IDLE_PROCESS_ID) @@ -107,9 +120,24 @@ void up_initial_state(struct tcb_s *tcb) /* Initialize the context registers to stack top */ - xcp->regs = (void *)((uint32_t)tcb->stack_base_ptr + - tcb->adj_stack_size - - XCPTCONTEXT_SIZE); +#ifdef CONFIG_ARCH_KERNEL_STACK + if (xcp->kstack != NULL) + { + /* Put the frame on the thread's kernel stack rather than its user + * stack. It is restored in kernel context, and leaving it in user + * memory means the thread can scribble on the register set it is + * about to be started with. + */ + + xcp->regs = (void *)((uintptr_t)xcp->ktopstk - XCPTCONTEXT_SIZE); + } + else +#endif + { + xcp->regs = (void *)((uintptr_t)tcb->stack_base_ptr + + tcb->adj_stack_size - + XCPTCONTEXT_SIZE); + } /* Initialize the xcp registers */ @@ -117,9 +145,9 @@ void up_initial_state(struct tcb_s *tcb) /* Set initial values of registers */ - xcp->regs[REG_PC] = (uint32_t)tcb->start; /* Task entrypoint */ - xcp->regs[REG_A0] = 0; /* To terminate GDB backtrace */ - xcp->regs[REG_A1] = (uint32_t)tcb->stack_base_ptr + /* Physical top of stack frame */ + xcp->regs[REG_PC] = (uintptr_t)tcb->start; /* Task entrypoint */ + xcp->regs[REG_A0] = 0; /* To terminate GDB backtrace */ + xcp->regs[REG_A1] = (uintptr_t)tcb->stack_base_ptr + /* Physical top of stack frame */ tcb->adj_stack_size; /* Each task access the TLS variables using the THREADPTR register plus an @@ -228,8 +256,8 @@ void up_initial_state(struct tcb_s *tcb) #ifdef CONFIG_SCHED_THREAD_LOCAL xcp->regs[REG_THREADPTR] = (uintptr_t)tcb->stack_alloc_ptr + sizeof(struct tls_info_s) - - ((uint32_t)&_thread_local_start - - (uint32_t)&_rodata_reserved_start) - base; + ((uintptr_t)&_thread_local_start - + (uintptr_t)&_rodata_reserved_start) - base; #endif /* Set initial PS to int level 0, user mode. */ diff --git a/arch/xtensa/src/common/xtensa_swint.c b/arch/xtensa/src/common/xtensa_swint.c index 4b38987e17e38..08eeb5b9726f3 100644 --- a/arch/xtensa/src/common/xtensa_swint.c +++ b/arch/xtensa/src/common/xtensa_swint.c @@ -32,6 +32,7 @@ #include #include +#include #include #include "sched/sched.h" @@ -39,6 +40,19 @@ #include "signal/signal.h" #include "xtensa.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_ARCH_KERNEL_STACK +/* A stack pointer is 16-byte aligned, and the windowed ABI reserves the + * 16 bytes below one as the base save area of the frame that owns it. + */ + +# define SIGTRAMP_STACK_ALIGN 16 +# define SIGTRAMP_SAVE_AREA 16 +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -66,6 +80,15 @@ int xtensa_swint(int irq, void *context, void *arg) cmd = regs[REG_A2]; +#ifdef CONFIG_LIB_SYSCALL + /* Record the caller's register context for the duration of the call. A + * system call body runs as C code after this exception has returned, so + * this is how it reaches the registers of the thread that called it. + */ + + tcb->xcp.sregs = regs; +#endif + /* The syscall software interrupt is called with A2 = system call command * and A3..A9 = variable number of arguments depending on the system call. */ @@ -102,6 +125,24 @@ int xtensa_swint(int irq, void *context, void *arg) case SYS_restore_context: case SYS_switch_context: { +#ifdef CONFIG_ARCH_ADDRENV + /* Close down the outgoing task's address environment and + * instantiate the incoming one. up_switch_context() is a + * SYS_switch_context call on this architecture, so this is the + * path every *voluntary* context switch takes -- without it a task + * resumed here keeps running against whatever address environment + * happened to be resident, which on the ESP32-S3 means the + * cache-MMU windows still point at another process's pages. + * + * addrenv_switch() may change this_task(), because dropping an + * address environment can post to the high-priority work queue, so + * re-read the TCB afterwards -- as arm_syscall.c does. + */ + + addrenv_switch(tcb); + tcb = this_task(); +#endif + restore_critical_section(tcb, this_cpu()); #ifdef CONFIG_DEBUG_SYSCALL_INFO svcinfo("SYSCALL Return: Context switch!\n"); @@ -151,6 +192,19 @@ int xtensa_swint(int irq, void *context, void *arg) rtcb->xcp.nsyscalls = index; +#ifdef CONFIG_ARCH_KERNEL_STACK + /* Leaving the outermost system call: hand the thread back its own + * stack, which it has not touched while the kernel borrowed its + * context. + */ + + if (index == 0 && rtcb->xcp.ustkptr != NULL) + { + regs[REG_A1] = (uintptr_t)rtcb->xcp.ustkptr; + rtcb->xcp.ustkptr = NULL; + } +#endif + /* Handle any signal actions that were deferred while processing * the system call. */ @@ -275,7 +329,11 @@ int xtensa_swint(int irq, void *context, void *arg) * unprivileged mode. */ +#if defined(CONFIG_BUILD_PROTECTED) regs[REG_PC] = (uintptr_t)USERSPACE->signal_handler; +#else + regs[REG_PC] = (uintptr_t)ARCH_DATA_RESERVE->ar_sigtramp; +#endif xtensa_lowerprivilege(regs); /* User mode */ @@ -287,6 +345,55 @@ int xtensa_swint(int irq, void *context, void *arg) regs[REG_A3] = regs[REG_A4]; /* signal */ regs[REG_A4] = regs[REG_A5]; /* info */ regs[REG_A5] = regs[REG_A6]; /* ucontext */ + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* The handler runs in user mode, so it has to run on the user + * stack. Signal dispatch always reaches here on the thread's + * kernel stack -- up_schedule_sigaction() builds the dispatch + * context below the interrupted one -- so put that stack pointer + * aside and hand the thread its own stack back for the duration. + * + * Having a kernel stack at all is what says this is a user + * process. Testing xcp.ustkptr instead would be wrong: that + * holds the user stack pointer only while a system call is in + * progress, so a signal caught in user code would leave the + * handler running on the kernel stack. + */ + + if (rtcb->xcp.kstack != NULL) + { + uintptr_t usp; + + rtcb->xcp.kstkptr = (uint32_t *)regs[REG_A1]; + + /* The thread's own stack pointer is the one the system call + * saved if it was in one, and otherwise the one it was + * interrupted with, which up_schedule_sigaction() kept. + */ + + usp = rtcb->xcp.ustkptr != NULL ? + (uintptr_t)rtcb->xcp.ustkptr : + (uintptr_t)rtcb->xcp.saved_regs[REG_A1]; + + /* The siginfo passed in lives on the kernel stack, which the + * handler must not reach -- and cannot, once the permission + * control is programmed. Copy it onto the user stack and + * hand the handler that copy. + * + * Skip the base save area the windowed ABI keeps in the + * 16 bytes below a stack pointer: it belongs to the frame + * that was interrupted. + */ + + usp = (usp - SIGTRAMP_SAVE_AREA - sizeof(siginfo_t)) & + ~(SIGTRAMP_STACK_ALIGN - 1); + + memcpy((void *)usp, (void *)regs[REG_A4], sizeof(siginfo_t)); + + regs[REG_A4] = usp; /* info */ + regs[REG_A1] = usp; + } +#endif } break; #endif @@ -313,6 +420,20 @@ int xtensa_swint(int irq, void *context, void *arg) xtensa_raiseprivilege(regs); /* Privileged mode */ rtcb->xcp.sigreturn = 0; + +#ifdef CONFIG_ARCH_KERNEL_STACK + /* The handler is done: return to the kernel stack the signal + * dispatch was running on. + */ + + if (rtcb->xcp.kstack != NULL) + { + DEBUGASSERT(rtcb->xcp.kstkptr != NULL); + + regs[REG_A1] = (uintptr_t)rtcb->xcp.kstkptr; + rtcb->xcp.kstkptr = NULL; + } +#endif } break; #endif @@ -353,6 +474,39 @@ int xtensa_swint(int irq, void *context, void *arg) xtensa_raiseprivilege(regs); /* Privileged mode */ #endif +#ifdef CONFIG_ARCH_KERNEL_STACK + /* The system call itself runs in this task's own context, so + * without help it would run the kernel on the *user* stack. That + * cannot be allowed in a kernel build: the user stack lives in a + * cache-MMU window, and any system call that selects a different + * address environment -- exec() loading a program, for one -- + * reprograms that window and the kernel's stack disappears from + * under it, taking the frames it is standing on. + * + * So the outermost system call moves to the thread's kernel + * stack, which lives in kernel memory and is unaffected by + * address environment changes. Nested calls are already on it. + */ + + if (index == 0 && rtcb->xcp.ktopstk != NULL) + { + rtcb->xcp.ustkptr = (uint32_t *)regs[REG_A1]; + + /* Start at the top of the kernel stack -- unless a signal + * handler is running, in which case the kernel stack is in + * use down to the point the dispatch left it at, and this + * call has to continue below that. Restarting at the top + * would overwrite both the suspended signal dispatch and the + * context it saved to resume the thread with, which sits in + * the topmost frame. + */ + + regs[REG_A1] = rtcb->xcp.kstkptr != NULL ? + (uintptr_t)rtcb->xcp.kstkptr : + (uintptr_t)rtcb->xcp.ktopstk; + } +#endif + /* Offset A2 to account for the reserved values */ regs[REG_A2] -= CONFIG_SYS_RESERVED; diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index 23c017e1acd70..b0b392d3b99b6 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -343,6 +343,26 @@ config ESP32S3_RUN_IRAM This loads all of NuttX inside IRAM. Used to test somewhat small images that can fit entirely in IRAM. +config ESP32S3_PGPOOL_SCRATCH + bool + default y + depends on BUILD_KERNEL && ARCH_ADDRENV && MM_PGALLOC + select ARCH_KVMA_MAPPING + ---help--- + The page pool is deliberately not kept mapped into the kernel + address space: it is carved out of the PSRAM the user processes + themselves run from, and the external memory permissions are indexed + by physical address, so a kernel window onto the pool is a window + onto every process's memory that no permission setting can close. + + The kernel reaches a pool page through a small scratch region + instead, mapped for the duration of one operation and invalidated + afterwards. ARCH_KMAP_VBASE and ARCH_KMAP_NPAGES describe it. + + The pool itself is described by ARCH_PGPOOL_PBASE and + ARCH_PGPOOL_SIZE as usual. Only ARCH_PGPOOL_VBASE does not apply, + because there is no permanent virtual mapping to name. + menu "ESP32-S3 Peripheral Selection" source "arch/xtensa/src/common/espressif/Kconfig" @@ -923,7 +943,35 @@ config ESP32S3_WCL bool "World Controller" default n select ARCH_USE_MPU - select XTENSA_HAVE_GENERAL_EXCEPTION_HOOKS if BUILD_PROTECTED + select XTENSA_HAVE_GENERAL_EXCEPTION_HOOKS if !BUILD_FLAT + +config ESP32S3_PAGEFAULT + bool "Recoverable PMS permission faults" + default n + depends on !BUILD_FLAT + ---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" diff --git a/arch/xtensa/src/esp32s3/Make.defs b/arch/xtensa/src/esp32s3/Make.defs index b306e15c46cd1..6fd8e8087fbab 100644 --- a/arch/xtensa/src/esp32s3/Make.defs +++ b/arch/xtensa/src/esp32s3/Make.defs @@ -43,6 +43,25 @@ ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += esp32s3_userspace.c endif +# The MMU/PMS/WCL primitives back both the protected user split and the +# BUILD_KERNEL address-environment remap. + +ifneq ($(filter y,$(CONFIG_BUILD_PROTECTED) $(CONFIG_ARCH_ADDRENV)),) +CHIP_CSRCS += esp32s3_mmu.c esp32s3_pms.c esp32s3_wcl.c +endif + +ifeq ($(CONFIG_ESP32S3_PAGEFAULT),y) +CHIP_CSRCS += esp32s3_pagefault.c +endif + +ifeq ($(CONFIG_ARCH_ADDRENV),y) +CHIP_CSRCS += esp32s3_addrenv.c esp32s3_addrenv_utils.c +endif + +ifeq ($(CONFIG_MM_PGALLOC),y) +CHIP_CSRCS += esp32s3_pgalloc.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/chip_macros.h b/arch/xtensa/src/esp32s3/chip_macros.h index 652d95a3a3120..963fa91d174fb 100644 --- a/arch/xtensa/src/esp32s3/chip_macros.h +++ b/arch/xtensa/src/esp32s3/chip_macros.h @@ -33,7 +33,7 @@ #endif #endif -#if defined(CONFIG_ESP32S3_WCL) && defined(CONFIG_BUILD_PROTECTED) +#if defined(CONFIG_ESP32S3_WCL) && !defined(CONFIG_BUILD_FLAT) #include "hardware/esp32s3_wcl_core.h" #endif @@ -47,7 +47,7 @@ #define HANDLER_SECTION .iram1 -#if defined(CONFIG_ESP32S3_WCL) && defined(CONFIG_BUILD_PROTECTED) +#if defined(CONFIG_ESP32S3_WCL) && !defined(CONFIG_BUILD_FLAT) /* Definitions for the Worlds reserved for Kernel and Userspace */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_addrenv.c b/arch/xtensa/src/esp32s3/esp32s3_addrenv.c new file mode 100644 index 0000000000000..f1c85e45a413d --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_addrenv.c @@ -0,0 +1,760 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_addrenv.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 +#include + +#include "soc/ext_mem_defs.h" + +#include "esp32s3_addrenv.h" +#include "esp32s3_mmu.h" +#include "esp32s3_spiram.h" + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The ESP32-S3 cache MMU is a single global remap table: only one user + * address environment can be resident in the shared .text/.data/.heap + * windows at a time. Track which one it is so up_addrenv_select() can skip + * the (expensive) remap when the incoming environment is already active -- + * the common thread<->thread, ISR and syscall case. + */ + +static const arch_addrenv_t *g_current_addrenv; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: alloc_region + * + * Description: + * Allocate and wipe the physical page-pool (PSRAM) pages that back a + * single user region, recording them in the caller's page array. On the + * ESP32-S3 the physical pages are only recorded here; the global cache-MMU + * table is (re)programmed lazily in up_addrenv_select(). + * + * Input Parameters: + * pages - Destination page array (physical addresses) + * maxpages - Capacity of the page array + * size - Region size in bytes + * count - Receives the number of pages actually allocated + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. On failure + * *count reflects the pages allocated so far so the caller can free them. + * + ****************************************************************************/ + +static int alloc_region(uintptr_t *pages, unsigned int maxpages, size_t size, + uint16_t *count) +{ + unsigned int npages = MM_NPAGES(size); + unsigned int i; + + *count = 0; + + if (npages > maxpages) + { + berr("ERROR: region needs %u pages, only %u available\n", + npages, maxpages); + return -E2BIG; + } + + for (i = 0; i < npages; i++) + { + uintptr_t paddr = mm_pgalloc(1); + if (paddr == 0) + { + berr("ERROR: page pool exhausted at page %u of %u\n", i, npages); + *count = i; + return -ENOMEM; + } + + esp32s3_pgwipe(paddr); + pages[i] = paddr; + } + + *count = npages; + return OK; +} + +/**************************************************************************** + * Name: free_region + * + * Description: + * Return every page recorded in a region's page array to the page pool. + * + ****************************************************************************/ + +static void free_region(uintptr_t *pages, uint16_t *count) +{ + uint16_t i; + + for (i = 0; i < *count; i++) + { + if (pages[i] != 0) + { + mm_pgfree(pages[i], 1); + pages[i] = 0; + } + } + + *count = 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_addrenv_create + * + * Description: + * This function is called when a new task is created in order to + * instantiate an address environment for the new task group. Physical + * pages for .text, .data/.bss and the heap are allocated from the PSRAM + * page pool and recorded in 'addrenv'. The reserved OS region (heap MM + * bookkeeping and signal delivery support) occupies the first page of the + * data window. + * + ****************************************************************************/ + +int up_addrenv_create(size_t textsize, size_t datasize, size_t heapsize, + arch_addrenv_t *addrenv) +{ + size_t datatotal; + int ret; + + DEBUGASSERT(addrenv); + DEBUGASSERT(MM_ISALIGNED(CONFIG_ARCH_TEXT_VBASE)); + DEBUGASSERT(MM_ISALIGNED(CONFIG_ARCH_DATA_VBASE)); + DEBUGASSERT(MM_ISALIGNED(CONFIG_ARCH_HEAP_VBASE)); + + /* Start from a clean slate */ + + memset(addrenv, 0, sizeof(arch_addrenv_t)); + + /* The data window carries the OS reserve at its base, followed by the + * task's .data/.bss. vdata is therefore reported past the reserve. + */ + + datatotal = MM_PGALIGNUP(ARCH_DATA_RESERVE_SIZE) + datasize; + + addrenv->textvbase = CONFIG_ARCH_TEXT_VBASE; + addrenv->datavbase = CONFIG_ARCH_DATA_VBASE + + MM_PGALIGNUP(ARCH_DATA_RESERVE_SIZE); + addrenv->heapvbase = CONFIG_ARCH_HEAP_VBASE; + addrenv->heapsize = heapsize; + + /* Allocate the backing pages for each region */ + + ret = alloc_region(addrenv->textpages, CONFIG_ARCH_TEXT_NPAGES, textsize, + &addrenv->ntext); + if (ret < 0) + { + goto errout; + } + + ret = alloc_region(addrenv->datapages, CONFIG_ARCH_DATA_NPAGES, datatotal, + &addrenv->ndata); + if (ret < 0) + { + goto errout; + } + + ret = alloc_region(addrenv->heappages, CONFIG_ARCH_HEAP_NPAGES, heapsize, + &addrenv->nheap); + if (ret < 0) + { + goto errout; + } + + return OK; + +errout: + up_addrenv_destroy(addrenv); + return ret; +} + +/**************************************************************************** + * Name: up_addrenv_destroy + * + * Description: + * This function is called when a task group is finally deleted. Return + * all of the group's physical pages to the page pool. + * + ****************************************************************************/ + +int up_addrenv_destroy(arch_addrenv_t *addrenv) +{ + DEBUGASSERT(addrenv); + + /* If this environment is the resident one, forget it so a later select of + * a different environment that happens to reuse this address does not take + * the fast path by mistake. + */ + + if (addrenv == g_current_addrenv) + { + g_current_addrenv = NULL; + } + + free_region(addrenv->textpages, &addrenv->ntext); + free_region(addrenv->datapages, &addrenv->ndata); + free_region(addrenv->heappages, &addrenv->nheap); + + memset(addrenv, 0, sizeof(arch_addrenv_t)); + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_vtext + * + * Description: + * Return the virtual address associated with the newly created .text + * address environment. + * + ****************************************************************************/ + +int up_addrenv_vtext(arch_addrenv_t *addrenv, void **vtext) +{ + DEBUGASSERT(addrenv && vtext); + *vtext = (void *)addrenv->textvbase; + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_vdata + * + * Description: + * Return the virtual address associated with the newly created .bss/.data + * address environment. + * + ****************************************************************************/ + +int up_addrenv_vdata(arch_addrenv_t *addrenv, uintptr_t textsize, + void **vdata) +{ + DEBUGASSERT(addrenv && vdata); + *vdata = (void *)addrenv->datavbase; + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_vheap + * + * Description: + * Return the heap virtual address associated with the newly created + * address environment. + * + ****************************************************************************/ + +int up_addrenv_vheap(const arch_addrenv_t *addrenv, void **vheap) +{ + DEBUGASSERT(addrenv && vheap); + *vheap = (void *)addrenv->heapvbase; + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_heapsize + * + * Description: + * Return the size of the initial heap allocation. + * + ****************************************************************************/ + +ssize_t up_addrenv_heapsize(const arch_addrenv_t *addrenv) +{ + DEBUGASSERT(addrenv); + return (ssize_t)addrenv->heapsize; +} + +/**************************************************************************** + * Name: up_addrenv_select + * + * Description: + * After an address environment has been established for a task group (via + * up_addrenv_create()), this function may be called to instantiate that + * address environment in the virtual address space. On the ESP32-S3 there + * is no page-table-base register to load; instead the shared user + * cache-MMU windows (.text on the instruction bus, .data/.bss and heap on + * the data bus) are reprogrammed to point at this environment's PSRAM + * pages. Only one user environment can be resident at a time, so while a + * group runs only its own pages are visible *in the windows*: the entries + * it uses are pointed at its pages and the rest are invalidated. + * + * That is not by itself isolation between groups. Every user page comes + * from the one page pool, which the kernel keeps permanently mapped at + * CONFIG_ARCH_PGPOOL_VBASE, and the external-memory permissions are + * indexed by physical address, so they cannot deny the unprivileged world + * that window without also denying it its own pages. Closing that is a + * separate piece of work on the kernel's side of the map. + * + * The remap is skipped when 'addrenv' is already the resident environment + * (thread<->thread within a group, ISRs, syscalls), which pays nothing. + * + * Input Parameters: + * addrenv - Describes the address environment to instantiate. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_select(const arch_addrenv_t *addrenv) +{ + irqstate_t flags; + uint32_t cache_state; + uint16_t i; + + DEBUGASSERT(addrenv); + + /* Fast path: this environment is already resident */ + + if (addrenv == g_current_addrenv) + { + return OK; + } + + flags = enter_critical_section(); + + /* Suspend the data cache while the windows are rewritten. Instruction + * fetches keep running from the (unchanged) flash mapping through the + * instruction cache, so this function may execute from flash. + */ + + cache_state = esp32s3_dcache_suspend(false); + + /* Point the instruction-bus (.text) window at this group's pages */ + + for (i = 0; i < addrenv->ntext; i++) + { + esp32s3_mmu_map_ibus(SOC_MMU_ACCESS_SPIRAM, + ESP32S3_TEXT_VBASE + i * MM_PGSIZE, + addrenv->textpages[i], 1); + } + + /* Point the data-bus (.data/.bss, then heap) windows at this group's + * pages + */ + + for (i = 0; i < addrenv->ndata; i++) + { + esp32s3_mmu_map_dbus(SOC_MMU_ACCESS_SPIRAM, + ESP32S3_DATA_VBASE + i * MM_PGSIZE, + addrenv->datapages[i], 1); + } + + for (i = 0; i < addrenv->nheap; i++) + { + esp32s3_mmu_map_dbus(SOC_MMU_ACCESS_SPIRAM, + ESP32S3_HEAP_VBASE + i * MM_PGSIZE, + addrenv->heappages[i], 1); + } + + /* Take away what this group does not use. Only as many entries as the + * incoming group has pages were rewritten above; the rest of each window + * would otherwise still point at the pages of whoever was resident before, + * which a task that ran off the end of its own allocation could read. + * + * A group's page count only ever grows (the heap window, through + * pgalloc()), so this cannot invalidate an entry that is about to be + * needed again without a select in between. + */ + + esp32s3_mmu_unmap(ESP32S3_TEXT_VBASE + addrenv->ntext * MM_PGSIZE, + CONFIG_ARCH_TEXT_NPAGES - addrenv->ntext); + esp32s3_mmu_unmap(ESP32S3_DATA_VBASE + addrenv->ndata * MM_PGSIZE, + CONFIG_ARCH_DATA_NPAGES - addrenv->ndata); + esp32s3_mmu_unmap(ESP32S3_HEAP_VBASE + addrenv->nheap * MM_PGSIZE, + CONFIG_ARCH_HEAP_NPAGES - addrenv->nheap); + + /* Drop stale instruction lines from the previous mapping and resume */ + + esp32s3_icache_invalidate_all(); + esp32s3_dcache_resume(cache_state); + + g_current_addrenv = addrenv; + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: esp32s3_addrenv_coherent + * + * Description: + * Make everything written through the data bus visible to instruction + * fetch: push the data cache out to PSRAM, then drop the instruction cache + * so the next fetch reloads from it. + * + * Both halves are needed. The write-back is needed because text arrives + * through the data side; the invalidate is needed because the cache-MMU is + * one global table, so an entry now pointing at a process's page may still + * have instruction-cache lines belonging to whatever it mapped before. + * + ****************************************************************************/ + +static void esp32s3_addrenv_coherent(void) +{ + irqstate_t flags = enter_critical_section(); + + esp_spiram_writeback_cache(); + esp32s3_icache_invalidate_all(); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: esp32s3_addrenv_mapnew + * + * Description: + * Make a page that was added to an address environment after that + * environment was created -- heap growth through sbrk()/pgalloc() -- + * visible to the running task. The cache-MMU windows only ever reflect + * the resident environment, so this is a no-op unless 'addrenv' is the one + * currently selected. For any other environment the page is picked up + * from the page array by the next up_addrenv_select(). + * + * Input Parameters: + * addrenv - The address environment the page was added to. + * vaddr - The user virtual address the page is mapped at. + * paddr - The physical (page pool) address of the page. + * + ****************************************************************************/ + +void esp32s3_addrenv_mapnew(const arch_addrenv_t *addrenv, uintptr_t vaddr, + uintptr_t paddr) +{ + irqstate_t flags; + uint32_t cache_state; + + DEBUGASSERT(addrenv); + + if (addrenv != g_current_addrenv) + { + return; + } + + flags = enter_critical_section(); + + cache_state = esp32s3_dcache_suspend(false); + esp32s3_mmu_map_dbus(SOC_MMU_ACCESS_SPIRAM, vaddr, paddr, 1); + esp32s3_dcache_resume(cache_state); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: up_addrenv_coherent + * + * Description: + * Flush D-Cache and invalidate I-Cache in preparation for a change in + * address environments. + * + * This matters more here than the name suggests, because it is the point + * at which a freshly loaded program becomes executable. The ELF loader + * writes .text as *data*, so it lands in the data cache; instructions are + * fetched through the separate instruction cache. Without a writeback the + * PSRAM pages still hold whatever was there before, and the new process + * runs a mixture of its own code and stale bytes -- which shows up as an + * illegal instruction somewhere in the middle of a function, not at its + * entry point. up_addrenv_select() cannot cover this: it skips all cache + * maintenance when the environment it is handed is already resident, which + * is exactly the case on the way out of the loader. + * + ****************************************************************************/ + +int up_addrenv_coherent(const arch_addrenv_t *addrenv) +{ + DEBUGASSERT(addrenv); + + esp32s3_addrenv_coherent(); + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_mprot + * + * Description: + * Modify access rights to an address range. The ELF loader uses this to + * make .text writable while it copies the program in, and read-only again + * afterwards. + * + * The ESP32-S3 cannot honour that. A cache-MMU entry carries only a valid + * bit, a memory type and a physical page number -- there are no per-page + * permission bits -- and the coarse alternatives cannot express it either: + * the PMS areas gate a whole window per world rather than a page, and they + * report violations asynchronously. A mapped user page is therefore + * always readable and writable by its owner, so a process can write to its + * own .text. This does not weaken isolation between groups, which comes + * from the window remap in up_addrenv_select(), and the request is + * accepted so that the loader can proceed. + * + ****************************************************************************/ + +int up_addrenv_mprot(arch_addrenv_t *addrenv, uintptr_t addr, size_t len, + int prot) +{ + UNUSED(addrenv); + UNUSED(addr); + UNUSED(len); + UNUSED(prot); + + /* The permission change cannot be honoured, but the call still marks the + * two moments that matter for cache state: the loader asks for write + * access before it copies a program in, and takes it away again once the + * program is complete. Make both a synchronisation point. + * + * This is what makes a freshly loaded program executable. Text is written + * as data through the data-bus alias of the same cache-MMU entry, so it + * sits in the data cache, while instructions are fetched through the + * separate instruction cache -- which may still hold lines from whatever + * that entry mapped before, since one global table is shared by every + * process and by the kernel's own PSRAM window. Those stale lines read + * back as zeroes, so without this the process runs some of its own code + * and then executes a hole. + */ + + esp32s3_addrenv_coherent(); + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_clone + * + * Description: + * Duplicate an address environment. The threads of a task group share one + * address environment, so cloning it is simply copying the descriptor: the + * copy references the same PSRAM pages. (A true fork() that gives the + * child its own pages is a separate, higher-level operation built on + * up_addrenv_create() + a content copy.) + * + ****************************************************************************/ + +int up_addrenv_clone(const arch_addrenv_t *src, arch_addrenv_t *dest) +{ + DEBUGASSERT(src && dest); + memcpy(dest, src, sizeof(arch_addrenv_t)); + return OK; +} + +/**************************************************************************** + * Name: copy_region + * + * Description: + * Copy one region's pages from a parent environment into a child's. Both + * sides are reached through the kernel's scratch region, which is why it + * has two slots: source and destination are mapped at the same time so + * this is one memcpy rather than a bounce through kernel memory. + * + * sched_lock() is held across each page pair for the reason every scratch + * mapping is: those addresses are ordinary external memory to the + * permission control, so no unprivileged task may run while a page of + * somebody's memory is parked at one. + * + ****************************************************************************/ + +static int copy_region(const uintptr_t *src, uintptr_t *dest, uint16_t count) +{ + uint16_t i; + + for (i = 0; i < count; i++) + { + uintptr_t svaddr; + uintptr_t dvaddr; + + sched_lock(); + + svaddr = esp32s3_pgmap(src[i]); + dvaddr = esp32s3_pgmap(dest[i]); + + if (svaddr == 0 || dvaddr == 0) + { + if (svaddr != 0) + { + esp32s3_pgunmap(svaddr); + } + + if (dvaddr != 0) + { + esp32s3_pgunmap(dvaddr); + } + + sched_unlock(); + berr("ERROR: no scratch mapping for page %u\n", i); + return -EFAULT; + } + + memcpy((void *)dvaddr, (const void *)svaddr, MM_PGSIZE); + + esp32s3_pgunmap(dvaddr); + esp32s3_pgunmap(svaddr); + + sched_unlock(); + } + + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_fork + * + * Description: + * Duplicate an address environment for fork(): allocate the child pages + * to match the parent's regions and copy the parent's contents into them. + * + * The copy is eager and complete. There is no copy-on-write and no demand + * fill, because this chip provides no synchronous restartable write fault + * to build them on -- proven, not assumed. So a fork costs a full copy of + * the process image. + * + * The child's pages land at the same *virtual* addresses as the parent's, + * which is the property the whole thing rests on: a copied stack is full + * of pointers into itself, and they are only still correct because the + * copy is addressed identically. + * + ****************************************************************************/ + +int up_addrenv_fork(const arch_addrenv_t *src, arch_addrenv_t *dest) +{ + int ret; + + DEBUGASSERT(src && dest); + + memset(dest, 0, sizeof(arch_addrenv_t)); + + dest->textvbase = src->textvbase; + dest->datavbase = src->datavbase; + dest->heapvbase = src->heapvbase; + dest->heapsize = src->heapsize; + + /* Allocate the child's pages. alloc_region() takes a size, and the + * parent's page counts are the exact sizes wanted. + */ + + ret = alloc_region(dest->textpages, CONFIG_ARCH_TEXT_NPAGES, + (size_t)src->ntext * MM_PGSIZE, &dest->ntext); + if (ret < 0) + { + goto errout; + } + + ret = alloc_region(dest->datapages, CONFIG_ARCH_DATA_NPAGES, + (size_t)src->ndata * MM_PGSIZE, &dest->ndata); + if (ret < 0) + { + goto errout; + } + + ret = alloc_region(dest->heappages, CONFIG_ARCH_HEAP_NPAGES, + (size_t)src->nheap * MM_PGSIZE, &dest->nheap); + if (ret < 0) + { + goto errout; + } + + /* Then fill them from the parent */ + + ret = copy_region(src->textpages, dest->textpages, dest->ntext); + if (ret < 0) + { + goto errout; + } + + ret = copy_region(src->datapages, dest->datapages, dest->ndata); + if (ret < 0) + { + goto errout; + } + + ret = copy_region(src->heappages, dest->heappages, dest->nheap); + if (ret < 0) + { + goto errout; + } + + /* The text pages were written through the data bus. Make them visible to + * instruction fetch before anything runs from them. + */ + + esp32s3_addrenv_coherent(); + return OK; + +errout: + up_addrenv_destroy(dest); + return ret; +} + +/**************************************************************************** + * Name: up_addrenv_attach + * + * Description: + * Called when a task or thread is created in order to instantiate an + * address environment. On the ESP32-S3 the group's environment is made + * resident lazily by up_addrenv_select() at context-switch time, so there + * is nothing to do here. + * + ****************************************************************************/ + +int up_addrenv_attach(struct tcb_s *ptcb, struct tcb_s *tcb) +{ + return OK; +} + +/**************************************************************************** + * Name: up_addrenv_detach + * + * Description: + * Called when a task or thread exits. The group's pages are released by + * up_addrenv_destroy() when the final member leaves, so there is nothing + * per-thread to undo here. + * + ****************************************************************************/ + +int up_addrenv_detach(struct tcb_s *tcb) +{ + return OK; +} + +#endif /* CONFIG_ARCH_ADDRENV */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_addrenv.h b/arch/xtensa/src/esp32s3/esp32s3_addrenv.h new file mode 100644 index 0000000000000..1242d0de18add --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_addrenv.h @@ -0,0 +1,245 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_addrenv.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_ADDRENV_H +#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_ADDRENV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The page pool must NOT be statically mapped into the kernel. It is carved + * out of the same PSRAM the user processes run from, and the external memory + * permissions (APB_CTRL_SRAM_ACEn_*) are indexed by physical address, so a + * permanent kernel window onto the pool is a window onto every process's + * memory that no permission setting can close: a process's own pages are + * pool pages. This was measured, not assumed -- a user task read another + * process's .bss through it. The kernel uses the scratch region below + * instead. + */ + +#ifdef CONFIG_ARCH_PGPOOL_MAPPING +# error "ESP32-S3 must not map the page pool; see esp32s3_pgmap()" +#endif + +#ifndef CONFIG_ARCH_KMAP_VBASE +# error "ESP32-S3 address environments need CONFIG_ARCH_KMAP_VBASE" +#endif + +#if CONFIG_ARCH_KMAP_NPAGES < 2 +# error "CONFIG_ARCH_KMAP_NPAGES must be at least 2 (fork() copies " \ + "a source and a destination page at once)" +#endif + +/* CONFIG_MM_KMAP cannot work here. kmm_map()'s single-page path is + * up_addrenv_page_vaddr(), which asks for a kernel address that stays valid + * after the call returns -- exactly what a scratch mapping cannot promise. + */ + +#ifdef CONFIG_MM_KMAP +# error "CONFIG_MM_KMAP needs a permanently mapped page pool" +#endif + +/* The kernel's scratch region. ARCH_KMAP_VEND is only defined by + * when CONFIG_MM_KMAP is set, which it is not. + */ + +#define ESP32S3_KMAP_VBASE (CONFIG_ARCH_KMAP_VBASE) +#define ESP32S3_KMAP_NPAGES (CONFIG_ARCH_KMAP_NPAGES) +#define ESP32S3_KMAP_VEND (CONFIG_ARCH_KMAP_VBASE + \ + CONFIG_ARCH_KMAP_NPAGES * MM_PGSIZE) + +/* The page pool, described physically. mm_pgalloc() hands out physical page + * addresses; only the virtual mapping went away, not the pool. + */ + +#define ESP32S3_PGPOOL_PBASE (CONFIG_ARCH_PGPOOL_PBASE) +#define ESP32S3_PGPOOL_SIZE (CONFIG_ARCH_PGPOOL_SIZE) +#define ESP32S3_PGPOOL_PEND (CONFIG_ARCH_PGPOOL_PBASE + \ + CONFIG_ARCH_PGPOOL_SIZE) + +/* The user address space is split across two disjoint cache-MMU windows: + * .text lives in the instruction-bus window, .data/.bss and the heap in the + * data-bus window. Each window is described by its base and page count. + */ + +#define ESP32S3_TEXT_VBASE (CONFIG_ARCH_TEXT_VBASE) +#define ESP32S3_TEXT_VEND (CONFIG_ARCH_TEXT_VBASE + \ + CONFIG_ARCH_TEXT_NPAGES * MM_PGSIZE) +#define ESP32S3_DATA_VBASE (CONFIG_ARCH_DATA_VBASE) +#define ESP32S3_DATA_VEND (CONFIG_ARCH_DATA_VBASE + \ + CONFIG_ARCH_DATA_NPAGES * MM_PGSIZE) +#define ESP32S3_HEAP_VBASE (CONFIG_ARCH_HEAP_VBASE) +#define ESP32S3_HEAP_VEND (CONFIG_ARCH_HEAP_VBASE + \ + CONFIG_ARCH_HEAP_NPAGES * MM_PGSIZE) + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_pgpool_page + * + * Description: + * Return true if paddr is a page-pool physical address. + * + ****************************************************************************/ + +static inline bool esp32s3_pgpool_page(uintptr_t paddr) +{ + return (paddr >= ESP32S3_PGPOOL_PBASE && paddr < ESP32S3_PGPOOL_PEND); +} + +/**************************************************************************** + * Name: esp32s3_uservaddr + * + * Description: + * Return true if vaddr lies inside one of the user (.text/.data/heap) + * cache-MMU windows. + * + ****************************************************************************/ + +static inline bool esp32s3_uservaddr(uintptr_t vaddr) +{ + return ((vaddr >= ESP32S3_TEXT_VBASE && vaddr < ESP32S3_TEXT_VEND) || + (vaddr >= ESP32S3_DATA_VBASE && vaddr < ESP32S3_DATA_VEND) || + (vaddr >= ESP32S3_HEAP_VBASE && vaddr < ESP32S3_HEAP_VEND)); +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_pgmap + * + * Description: + * Map a page-pool physical page into the kernel's scratch region and + * return the virtual address it can be reached at. + * + * This replaces the arithmetic esp32s3_pgvaddr() the port used while the + * whole pool was mapped, and unlike it the result has a *lifetime*: it is + * valid until the matching esp32s3_pgunmap(), and not one instruction + * longer. It must not be stored anywhere that outlives the operation. + * + * The caller must hold sched_lock() across the whole map/use/unmap + * sequence. A scratch address is ordinary external memory as far as the + * permission control is concerned, so an unprivileged task that ran while + * the mapping was live could read the page through it -- which is the leak + * this whole arrangement exists to close. Interrupts do not need to be + * disabled: no interrupt handler reaches these paths. + * + * Input Parameters: + * paddr - Physical (page pool) address of the page, page-aligned. + * + * Returned Value: + * The kernel virtual address of the page, or 0 if 'paddr' is not a pool + * page or no scratch slot is free. + * + ****************************************************************************/ + +uintptr_t esp32s3_pgmap(uintptr_t paddr); + +/**************************************************************************** + * Name: esp32s3_pgunmap + * + * Description: + * Release a mapping made by esp32s3_pgmap(), writing back anything written + * through it and leaving the scratch entry invalid. + * + * Input Parameters: + * vaddr - The address esp32s3_pgmap() returned. + * + ****************************************************************************/ + +void esp32s3_pgunmap(uintptr_t vaddr); + +/**************************************************************************** + * Name: esp32s3_pgwipe + * + * Description: + * Zero a page-pool physical page, mapping it into the kernel's scratch + * region for as long as that takes. + * + * Input Parameters: + * paddr - Physical (page pool) address of the page. + * + ****************************************************************************/ + +void esp32s3_pgwipe(uintptr_t paddr); + +/**************************************************************************** + * Name: esp32s3_pgpool_unmap + * + * Description: + * Tear down the boot-time cache-MMU mapping of the page pool, leaving the + * pool reachable only a page at a time through esp32s3_pgmap(). Called + * once, from up_allocate_pgheap(), after its consistency checks -- which + * have to run first, since they ask the cache MMU where the pool actually + * is rather than deriving it. + * + * Input Parameters: + * vbase - Virtual base the boot-time mapping put the pool at. + * size - Size of the pool in bytes. + * + ****************************************************************************/ + +void esp32s3_pgpool_unmap(uintptr_t vbase, size_t size); + +/**************************************************************************** + * Name: esp32s3_addrenv_mapnew + * + * Description: + * Make a page that was added to an address environment after that + * environment was created -- heap growth through sbrk()/pgalloc() -- + * visible to the running task. The cache-MMU windows only ever reflect + * the resident environment, so this is a no-op unless 'addrenv' is the one + * currently selected. For any other environment the page is picked up + * from the page array by the next up_addrenv_select(). + * + * Input Parameters: + * addrenv - The address environment the page was added to. + * vaddr - The user virtual address the page is mapped at. + * paddr - The physical (page pool) address of the page. + * + ****************************************************************************/ + +void esp32s3_addrenv_mapnew(const arch_addrenv_t *addrenv, uintptr_t vaddr, + uintptr_t paddr); + +#endif /* CONFIG_ARCH_ADDRENV */ +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_ADDRENV_H */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_addrenv_utils.c b/arch/xtensa/src/esp32s3/esp32s3_addrenv_utils.c new file mode 100644 index 0000000000000..08a2a2554217d --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_addrenv_utils.c @@ -0,0 +1,369 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_addrenv_utils.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 +#include + +#include "esp32s3_addrenv.h" +#include "esp32s3_mmu.h" + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The kernel's scratch region: the only way it can reach a page-pool page, + * one page at a time and only while it is working on it. A slot holds the + * physical page it currently maps, or 0 when it is free. + * + * There is no lock here on purpose. Callers hold sched_lock() for the whole + * map/use/unmap sequence -- which they must anyway, so that a live mapping + * cannot be observed by an unprivileged task -- and that also makes this + * table single-threaded. No interrupt handler reaches these paths. + */ + +static uintptr_t g_scratch[ESP32S3_KMAP_NPAGES]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: scratch_slot + * + * Description: + * Return the scratch slot index a scratch virtual address belongs to, or + * -1 if the address is not in the scratch region. + * + ****************************************************************************/ + +static int scratch_slot(uintptr_t vaddr) +{ + if (vaddr < ESP32S3_KMAP_VBASE || vaddr >= ESP32S3_KMAP_VEND) + { + return -1; + } + + return (int)((vaddr - ESP32S3_KMAP_VBASE) >> MM_PGSHIFT); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_pgmap + ****************************************************************************/ + +uintptr_t esp32s3_pgmap(uintptr_t paddr) +{ + uintptr_t vaddr; + int i; + + DEBUGASSERT(!up_interrupt_context()); + DEBUGASSERT(MM_ISALIGNED(paddr)); + + if (!esp32s3_pgpool_page(paddr)) + { + return 0; + } + + for (i = 0; i < ESP32S3_KMAP_NPAGES; i++) + { + if (g_scratch[i] == 0) + { + break; + } + } + + if (i >= ESP32S3_KMAP_NPAGES) + { + return 0; + } + + g_scratch[i] = paddr; + vaddr = ESP32S3_KMAP_VBASE + i * MM_PGSIZE; + + esp32s3_mmu_scratch_map(vaddr, paddr); + + return vaddr; +} + +/**************************************************************************** + * Name: esp32s3_pgunmap + ****************************************************************************/ + +void esp32s3_pgunmap(uintptr_t vaddr) +{ + int i = scratch_slot(vaddr); + + DEBUGASSERT(i >= 0 && g_scratch[i] != 0); + + esp32s3_mmu_scratch_unmap(ESP32S3_KMAP_VBASE + i * MM_PGSIZE); + + g_scratch[i] = 0; +} + +/**************************************************************************** + * Name: esp32s3_pgwipe + ****************************************************************************/ + +void esp32s3_pgwipe(uintptr_t paddr) +{ + uintptr_t vaddr; + + /* Hold off the scheduler for the whole sequence. Not for mutual exclusion + * against another wipe -- there is none to worry about here -- but because + * a scratch address is reachable by the unprivileged world like any other + * external memory address, so no user task may run while a pool page is + * parked at one. + */ + + sched_lock(); + + vaddr = esp32s3_pgmap(paddr); + if (vaddr == 0) + { + /* Not recoverable, and much too dangerous to let pass: an unwiped page + * carries its previous tenant's data into a new process. This is + * exactly how the CONFIG_ARCH_PGPOOL_PBASE drift stayed hidden. + */ + + _err("ERROR: no scratch mapping for page %08" PRIxPTR "\n", paddr); + PANIC(); + } + + memset((void *)vaddr, 0, MM_PGSIZE); + esp32s3_pgunmap(vaddr); + + sched_unlock(); +} + +/**************************************************************************** + * Name: esp32s3_pgpool_unmap + ****************************************************************************/ + +void esp32s3_pgpool_unmap(uintptr_t vbase, size_t size) +{ + uint32_t cache_state; + + /* Take away the boot-time mapping of the pool in one go. esp32s3_spiram.c + * mapped the whole PSRAM device; only the pool's share of that window is + * withdrawn, because the rest maps no page a process will ever own and is + * the aperture a kernel-side PSRAM allocation (a loaded library's text + * heap, say) would have to come from. + * + * Write back before invalidating the entries: this runs after the PSRAM + * memory test, which leaves the window's lines dirty. + */ + + cache_state = esp32s3_dcache_suspend(true); + esp32s3_mmu_unmap(vbase, size / MM_PGSIZE); + esp32s3_icache_invalidate_all(); + esp32s3_dcache_resume(cache_state); +} + +/**************************************************************************** + * Name: up_addrenv_find_page + * + * Description: + * Find the physical address of the page that backs a user virtual address + * in the given address environment. On the ESP32-S3 there is no per-task + * page table to walk; the physical pages are recorded per region in the + * address environment, so the lookup reduces to selecting the window that + * contains 'vaddr' and indexing that region's page array. + * + * Returned Value: + * Physical address of the backing page (page-aligned) on success, or 0 if + * 'vaddr' is not a mapped user address. + * + ****************************************************************************/ + +uintptr_t up_addrenv_find_page(arch_addrenv_t *addrenv, uintptr_t vaddr) +{ + const uintptr_t *pages; + uintptr_t base; + uint16_t count; + uint16_t index; + + DEBUGASSERT(addrenv); + + if (vaddr >= ESP32S3_TEXT_VBASE && vaddr < ESP32S3_TEXT_VEND) + { + base = ESP32S3_TEXT_VBASE; + pages = addrenv->textpages; + count = addrenv->ntext; + } + else if (vaddr >= ESP32S3_DATA_VBASE && vaddr < ESP32S3_DATA_VEND) + { + base = ESP32S3_DATA_VBASE; + pages = addrenv->datapages; + count = addrenv->ndata; + } + else if (vaddr >= ESP32S3_HEAP_VBASE && vaddr < ESP32S3_HEAP_VEND) + { + base = ESP32S3_HEAP_VBASE; + pages = addrenv->heappages; + count = addrenv->nheap; + } + else + { + return 0; + } + + index = (uint16_t)((vaddr - base) >> MM_PGSHIFT); + if (index >= count) + { + return 0; + } + + return pages[index]; +} + +/**************************************************************************** + * Name: up_addrenv_page_vaddr + * + * Description: + * Get the kernel virtual address of a physical page allocated for an + * address environment. + * + * The ESP32-S3 cannot answer this. The contract is a kernel address that + * stays valid after the call returns, and the page pool is deliberately + * not mapped: a pool page is reachable only for the duration of an + * esp32s3_pgmap()/esp32s3_pgunmap() pair. Returning an address that is + * about to stop meaning anything would be worse than refusing, so this + * fails, and CONFIG_MM_KMAP -- whose single-page path is built on this + * function -- is rejected at compile time in esp32s3_addrenv.h. + * + ****************************************************************************/ + +uintptr_t up_addrenv_page_vaddr(uintptr_t page) +{ + UNUSED(page); + return 0; +} + +/**************************************************************************** + * Name: up_addrenv_user_vaddr + * + * Description: + * Check if a virtual address is a user virtual address. + * + ****************************************************************************/ + +bool up_addrenv_user_vaddr(uintptr_t vaddr) +{ + return esp32s3_uservaddr(vaddr); +} + +/**************************************************************************** + * Name: up_addrenv_page_wipe + * + * Description: + * Wipe a page of physical memory, first mapping it into kernel virtual + * memory. + * + ****************************************************************************/ + +void up_addrenv_page_wipe(uintptr_t page) +{ + esp32s3_pgwipe(page); +} + +/**************************************************************************** + * Name: up_addrenv_pa_to_va + * + * Description: + * Map a physical page-pool address to the kernel virtual address that + * currently maps it. + * + * Which is now literally what this does: only a page held in a scratch + * slot is mapped at all, so the answer comes from the slot table rather + * than from arithmetic over a window that no longer exists. A page nobody + * is working on has no kernel virtual address, and 0 says so. + * + ****************************************************************************/ + +void *up_addrenv_pa_to_va(uintptr_t pa) +{ + uintptr_t page = MM_PGALIGNDOWN(pa); + int i; + + /* A free slot holds 0, so page 0 could otherwise match one of them */ + + if (!esp32s3_pgpool_page(page)) + { + return NULL; + } + + for (i = 0; i < ESP32S3_KMAP_NPAGES; i++) + { + if (g_scratch[i] == page) + { + return (void *)(ESP32S3_KMAP_VBASE + i * MM_PGSIZE + + (pa & MM_PGMASK)); + } + } + + return NULL; +} + +/**************************************************************************** + * Name: up_addrenv_va_to_pa + * + * Description: + * Map a kernel page-pool virtual address back to its physical address. + * The inverse of the above, and just as narrow: scratch addresses are the + * only kernel virtual addresses that name a pool page. + * + ****************************************************************************/ + +uintptr_t up_addrenv_va_to_pa(void *va) +{ + uintptr_t vaddr = (uintptr_t)va; + int i = scratch_slot(vaddr); + + if (i < 0 || g_scratch[i] == 0) + { + return 0; + } + + return g_scratch[i] + (vaddr & MM_PGMASK); +} + +#endif /* CONFIG_ARCH_ADDRENV */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_allocateheap.c b/arch/xtensa/src/esp32s3/esp32s3_allocateheap.c index fbab081a55068..11034a9b5efbe 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_allocateheap.c +++ b/arch/xtensa/src/esp32s3/esp32s3_allocateheap.c @@ -96,6 +96,17 @@ void up_allocate_heap(void **heap_start, size_t *heap_size) utop = ALIGN_DOWN(ets_rom_layout_p->dram0_rtos_reserved_start, 256); +# elif defined(CONFIG_BUILD_KERNEL) + /* There is no single user heap in a kernel build: each task group grows + * its own out of the page pool as it is created. nx_start() knows this + * and never asks -- MM_KERNEL_USRHEAP_INIT is undefined for BUILD_KERNEL + * -- so report an empty heap and let the assertion below catch it if this + * ever is reached. + */ + + ubase = 0; + utop = 0; + # elif defined(CONFIG_BUILD_FLAT) # ifdef MM_USER_HEAP_EXTRAM ubase = (uintptr_t)esp_spiram_allocable_vaddr_start(); @@ -160,6 +171,16 @@ void up_allocate_kheap(void **heap_start, size_t *heap_size) kbase = (uintptr_t)_sheap; ktop = KDRAM_END; +#elif defined(CONFIG_BUILD_KERNEL) + /* A kernel build has a single kernel heap and one user heap per task + * group, the latter grown out of the page pool by pgalloc(). So the whole + * internal DRAM heap belongs to the kernel here, whether or not PSRAM is + * fitted: PSRAM backs the page pool, not this heap. + */ + + kbase = (uintptr_t)_sheap + XTENSA_IMEM_REGION_SIZE; + ktop = (uintptr_t)ets_rom_layout_p->dram0_rtos_reserved_start; + #elif defined(CONFIG_BUILD_FLAT) # ifdef MM_USER_HEAP_IRAM /* Skip internal heap region if CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP is diff --git a/arch/xtensa/src/esp32s3/esp32s3_mmu.c b/arch/xtensa/src/esp32s3/esp32s3_mmu.c new file mode 100644 index 0000000000000..9e801d28249c4 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_mmu.c @@ -0,0 +1,268 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_mmu.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 + +#include +#include + +#include +#include + +#include "xtensa.h" +#include "esp_attr.h" + +#include "soc/ext_mem_defs.h" +#include "soc/extmem_reg.h" + +#include "esp32s3_mmu.h" + +/**************************************************************************** + * ROM Function Prototypes + ****************************************************************************/ + +extern uint32_t cache_suspend_dcache(void); +extern void cache_resume_dcache(uint32_t val); +extern void cache_invalidate_dcache_all(void); +extern void cache_invalidate_icache_all(void); +extern void cache_writeback_all(void); +extern int cache_dbus_mmu_set(uint32_t ext_ram, uint32_t vaddr, + uint32_t paddr, uint32_t psize, uint32_t num, + uint32_t fixed); +extern int cache_ibus_mmu_set(uint32_t ext_ram, uint32_t vaddr, + uint32_t paddr, uint32_t psize, uint32_t num, + uint32_t fixed); +extern int cache_invalidate_addr(uint32_t addr, uint32_t size); +extern int cache_writeback_addr(uint32_t addr, uint32_t size); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: dcache_unshut + * + * Description: + * Re-open the data cache bus that cache_suspend_dcache() shut. Taken from + * esp_spiram_map(): the range cache maintenance that follows a remap has + * to run with the bus open but the cache still suspended, so this is the + * half of esp32s3_dcache_resume() that comes before cache_resume_dcache(). + * + ****************************************************************************/ + +static void IRAM_ATTR dcache_unshut(void) +{ + uint32_t regval; + + regval = getreg32(EXTMEM_DCACHE_CTRL1_REG); + regval &= ~EXTMEM_DCACHE_SHUT_CORE0_BUS; +#ifdef CONFIG_SMP + regval &= ~EXTMEM_DCACHE_SHUT_CORE1_BUS; +#endif + putreg32(regval, EXTMEM_DCACHE_CTRL1_REG); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_dcache_suspend + ****************************************************************************/ + +uint32_t IRAM_ATTR esp32s3_dcache_suspend(bool needs_wb) +{ + uint32_t dcache_state = cache_suspend_dcache(); + + if (needs_wb) + { + cache_writeback_all(); + } + + cache_invalidate_dcache_all(); + + return dcache_state; +} + +/**************************************************************************** + * Name: esp32s3_dcache_resume + ****************************************************************************/ + +void IRAM_ATTR esp32s3_dcache_resume(uint32_t cache_state) +{ + uint32_t regval; + + regval = getreg32(EXTMEM_DCACHE_CTRL1_REG); + regval &= ~EXTMEM_DCACHE_SHUT_CORE0_BUS; +#ifdef CONFIG_SMP + regval &= ~EXTMEM_DCACHE_SHUT_CORE1_BUS; +#endif + putreg32(regval, EXTMEM_DCACHE_CTRL1_REG); + + cache_resume_dcache(cache_state); +} + +/**************************************************************************** + * Name: esp32s3_icache_invalidate_all + ****************************************************************************/ + +void IRAM_ATTR esp32s3_icache_invalidate_all(void) +{ + cache_invalidate_icache_all(); +} + +/**************************************************************************** + * Name: esp32s3_mmu_calc_pages + ****************************************************************************/ + +uint32_t esp32s3_mmu_calc_pages(uint32_t size, uint32_t vaddr) +{ + return (size + (vaddr - (vaddr & MMU_FLASH_MASK)) + MMU_PAGE_SIZE - 1) / + MMU_PAGE_SIZE; +} + +/**************************************************************************** + * Name: esp32s3_mmu_map_dbus + ****************************************************************************/ + +int IRAM_ATTR esp32s3_mmu_map_dbus(uint32_t ext_ram, uint32_t vaddr, + uint32_t paddr, uint32_t npages) +{ + return cache_dbus_mmu_set(ext_ram, vaddr, paddr, 64, (int)npages, 0); +} + +/**************************************************************************** + * Name: esp32s3_mmu_map_ibus + ****************************************************************************/ + +int IRAM_ATTR esp32s3_mmu_map_ibus(uint32_t ext_ram, uint32_t vaddr, + uint32_t paddr, uint32_t npages) +{ + return cache_ibus_mmu_set(ext_ram, vaddr, paddr, 64, (int)npages, 0); +} + +/**************************************************************************** + * Name: esp32s3_mmu_paddr + ****************************************************************************/ + +bool esp32s3_mmu_paddr(uint32_t vaddr, uint32_t *paddr) +{ + uint32_t entry = FLASH_MMU_TABLE[MMU_ENTRY_OF(vaddr)]; + + if ((entry & MMU_TABLE_INVALID_VAL) != 0) + { + return false; + } + + *paddr = (entry & MMU_ADDRESS_MASK) * MMU_PAGE_SIZE + + (vaddr & (MMU_PAGE_SIZE - 1)); + return true; +} + +/**************************************************************************** + * Name: esp32s3_mmu_unmap + ****************************************************************************/ + +void IRAM_ATTR esp32s3_mmu_unmap(uint32_t vaddr, uint32_t npages) +{ + uint32_t entry = MMU_ENTRY_OF(vaddr); + uint32_t i; + + /* One table, one entry per 64 KB, shared by the instruction and the data + * bus: the IBUS and DBUS linear addresses are asserted equal in + * ext_mem_defs.h, so a single write covers both views of the page. + */ + + for (i = 0; i < npages && entry + i < SOC_MMU_ENTRY_NUM; i++) + { + FLASH_MMU_TABLE[entry + i] = MMU_TABLE_INVALID_VAL; + } +} + +/**************************************************************************** + * Name: esp32s3_mmu_scratch_map + ****************************************************************************/ + +void esp32s3_mmu_scratch_map(uint32_t vaddr, uint32_t paddr) +{ + irqstate_t flags; + uint32_t cache_state; + + flags = enter_critical_section(); + + /* Suspend the cache, point the entry at the page, then invalidate just + * this page's lines rather than the whole cache. The difference matters: + * esp32s3_dcache_suspend() invalidates everything, which would discard the + * resident process's dirty lines, and its write-back variant would put a + * whole-cache write-back inside this critical section. Neither is + * acceptable at the rate this is called -- once per page of every process + * created. + */ + + cache_state = cache_suspend_dcache(); + + esp32s3_mmu_map_dbus(SOC_MMU_ACCESS_SPIRAM, vaddr, paddr, 1); + + dcache_unshut(); + cache_invalidate_addr(vaddr, MMU_PAGE_SIZE); + + cache_resume_dcache(cache_state); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: esp32s3_mmu_scratch_unmap + ****************************************************************************/ + +void esp32s3_mmu_scratch_unmap(uint32_t vaddr) +{ + irqstate_t flags; + uint32_t cache_state; + + /* Push whatever was written through this window out to the page it maps, + * while it still maps it. The lines are tagged by virtual address, so + * repointing the entry with them still dirty would write them back to + * whichever page the scratch slot is used for next. + * + * This runs with interrupts enabled on purpose -- it is a write-back of up + * to a page, and the caller holds sched_lock(), so nothing else can reach + * the slot in the meantime. + */ + + cache_writeback_addr(vaddr, MMU_PAGE_SIZE); + + flags = enter_critical_section(); + + cache_state = cache_suspend_dcache(); + + esp32s3_mmu_unmap(vaddr, 1); + + dcache_unshut(); + cache_invalidate_addr(vaddr, MMU_PAGE_SIZE); + + cache_resume_dcache(cache_state); + + leave_critical_section(flags); +} diff --git a/arch/xtensa/src/esp32s3/esp32s3_mmu.h b/arch/xtensa/src/esp32s3/esp32s3_mmu.h new file mode 100644 index 0000000000000..5dc2f1baf65d5 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_mmu.h @@ -0,0 +1,242 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_mmu.h + * + * 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_MMU_H +#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_MMU_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "hardware/esp32s3_cache_memory.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Cache MMU address mask (MMU tables ignore bits which are zero) */ + +#define MMU_FLASH_MASK (~(MMU_PAGE_SIZE - 1)) + +/* The cache MMU entry a virtual address resolves to. There is one table for + * both buses -- ext_mem_defs.h asserts that the IRAM0 and DRAM0 linear + * addresses are equal -- so an instruction-bus and a data-bus address 64 MB + * apart share an entry, and comparing entries is the only way to tell + * whether two windows collide. + */ + +#define MMU_ENTRY_OF(vaddr) (((vaddr) & SOC_MMU_VADDR_MASK) >> 16) + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_dcache_suspend + * + * Description: + * Suspend the data cache access for the CPU, optionally writing back its + * contents first, then invalidating it. + * + * Input Parameters: + * needs_wb - Whether to write back the data cache contents prior to + * invalidation. + * + * Returned Value: + * Current cache state (to be passed to esp32s3_dcache_resume()). + * + ****************************************************************************/ + +uint32_t esp32s3_dcache_suspend(bool needs_wb); + +/**************************************************************************** + * Name: esp32s3_dcache_resume + * + * Description: + * Resume the data cache access for the CPU. + * + * Input Parameters: + * cache_state - Previously saved data cache state to be restored. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void esp32s3_dcache_resume(uint32_t cache_state); + +/**************************************************************************** + * Name: esp32s3_icache_invalidate_all + * + * Description: + * Invalidate the whole instruction cache. + * + ****************************************************************************/ + +void esp32s3_icache_invalidate_all(void); + +/**************************************************************************** + * Name: esp32s3_mmu_calc_pages + * + * Description: + * Calculate the required number of MMU pages for mapping a given region + * at the cache MMU 64 KB page granularity. + * + * Input Parameters: + * size - Length of the region to map. + * vaddr - Starting offset to map (its intra-page offset is accounted for). + * + * Returned Value: + * Number of 64 KB MMU pages required. + * + ****************************************************************************/ + +uint32_t esp32s3_mmu_calc_pages(uint32_t size, uint32_t vaddr); + +/**************************************************************************** + * Name: esp32s3_mmu_map_dbus + * + * Description: + * Program the data-bus cache MMU to map a range of 64 KB pages from an + * external memory (flash or PSRAM) into the CPU virtual address space. + * The caller is responsible for suspending/resuming the data cache around + * the mapping change. + * + * Input Parameters: + * ext_ram - Selects the external memory (SOC_MMU_ACCESS_FLASH or + * SOC_MMU_ACCESS_SPIRAM). + * vaddr - 64 KB-aligned virtual base address. + * paddr - 64 KB-aligned physical/flash offset. + * npages - Number of 64 KB pages to map. + * + * Returned Value: + * Zero (OK) on success; the ROM error code otherwise. + * + ****************************************************************************/ + +int esp32s3_mmu_map_dbus(uint32_t ext_ram, uint32_t vaddr, uint32_t paddr, + uint32_t npages); + +/**************************************************************************** + * Name: esp32s3_mmu_map_ibus + * + * Description: + * Program the instruction-bus cache MMU to map a range of 64 KB pages from + * an external memory into the CPU virtual address space. The caller is + * responsible for suspending/resuming the data cache around the change. + * + * Input Parameters: + * ext_ram - Selects the external memory (SOC_MMU_ACCESS_FLASH or + * SOC_MMU_ACCESS_SPIRAM). + * vaddr - 64 KB-aligned virtual base address. + * paddr - 64 KB-aligned physical/flash offset. + * npages - Number of 64 KB pages to map. + * + * Returned Value: + * Zero (OK) on success; the ROM error code otherwise. + * + ****************************************************************************/ + +int esp32s3_mmu_map_ibus(uint32_t ext_ram, uint32_t vaddr, uint32_t paddr, + uint32_t npages); + +/**************************************************************************** + * Name: esp32s3_mmu_paddr + * + * Description: + * Ask the cache MMU what a virtual address currently resolves to. This is + * ground truth rather than arithmetic, which matters because where the + * kernel's PSRAM window lands is decided at run time (see + * up_allocate_pgheap()). + * + * Input Parameters: + * vaddr - A virtual address inside one of the external memory windows. + * paddr - Receives the physical/flash address it maps to. + * + * Returned Value: + * True if the entry is valid and *paddr was written; false if the entry + * maps nothing. + * + ****************************************************************************/ + +bool esp32s3_mmu_paddr(uint32_t vaddr, uint32_t *paddr); + +/**************************************************************************** + * Name: esp32s3_mmu_unmap + * + * Description: + * Invalidate a range of 64 KB cache MMU entries, so that the virtual + * addresses they covered map nothing at all. Note that an access to an + * invalidated entry is not necessarily a fault: unless the cache reject + * monitors are armed it reads back as zero and swallows writes. The + * caller is responsible for suspending/resuming the data cache around the + * change and for invalidating the instruction cache afterwards -- entries + * being taken away is exactly when stale instruction lines are left + * behind. + * + * Input Parameters: + * vaddr - 64 KB-aligned virtual base address. + * npages - Number of 64 KB pages to invalidate. + * + ****************************************************************************/ + +void esp32s3_mmu_unmap(uint32_t vaddr, uint32_t npages); + +/**************************************************************************** + * Name: esp32s3_mmu_scratch_map + * + * Description: + * Point one 64 KB data-bus entry at a PSRAM page and make it usable, doing + * the cache maintenance itself and confining it to that page. This is the + * kernel's way of reaching a page-pool page, which is otherwise not mapped + * at all (see esp32s3_pgmap()). + * + * The caller must already hold the scratch slot -- these entries are + * reachable by the unprivileged world like any other external memory + * address, so a mapping must not outlive the operation that needs it. + * + * Input Parameters: + * vaddr - 64 KB-aligned scratch virtual address. + * paddr - 64 KB-aligned PSRAM physical address. + * + ****************************************************************************/ + +void esp32s3_mmu_scratch_map(uint32_t vaddr, uint32_t paddr); + +/**************************************************************************** + * Name: esp32s3_mmu_scratch_unmap + * + * Description: + * Take a scratch mapping away again: write back what was written through + * it, invalidate the entry, and drop the page's cache lines. + * + * Input Parameters: + * vaddr - The scratch virtual address returned when it was mapped. + * + ****************************************************************************/ + +void esp32s3_mmu_scratch_unmap(uint32_t vaddr); + +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_MMU_H */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.c b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c new file mode 100644 index 0000000000000..e9f9c64472f28 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.c @@ -0,0 +1,135 @@ +/**************************************************************************** + * 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 + +#include +#include +#include +#include + +#include +#include +#include + +#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=%08x PC=%08x\n", + g_pf_selftest_hits, (unsigned)vaddr, (unsigned)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=%08x PC=%08x task=%s\n", + exccause, (unsigned)vaddr, (unsigned)pc, + get_task_name(this_task())); + + return -EFAULT; +} diff --git a/arch/xtensa/src/esp32s3/esp32s3_pagefault.h b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h new file mode 100644 index 0000000000000..13ed1dbbcf388 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_pagefault.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * 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 + * 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_PAGEFAULT_H +#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_pagefault_dispatch + * + * Description: + * 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 - The EXCCAUSE value (20/28/29). + * regs - Pointer to the register save area. + * + * Returned Value: + * 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). + * + ****************************************************************************/ + +int esp32s3_pagefault_dispatch(int exccause, uint32_t *regs); + +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PAGEFAULT_H */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_pgalloc.c b/arch/xtensa/src/esp32s3/esp32s3_pgalloc.c new file mode 100644 index 0000000000000..381c7050e200b --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_pgalloc.c @@ -0,0 +1,379 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_pgalloc.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 + +#include "sched/sched.h" + +#include "esp32s3_addrenv.h" +#include "esp32s3_mmu.h" + +#ifdef CONFIG_ESP32S3_SPIRAM +# include "esp32s3_spiram.h" +#endif + +#ifdef CONFIG_MM_PGALLOC + +#if defined(CONFIG_ESP32S3_SPIRAM) && defined(CONFIG_ARCH_ADDRENV) + +/* The page pool and a PSRAM kernel heap cannot coexist. xtensa_add_region() + * hands the *whole* allocable PSRAM window to the kernel heap, which + * necessarily includes the pool -- and a kernel heap that contains pool + * pages is a permanently mapped view of every process's memory, arriving + * from the other side of the same problem esp32s3_pgmap() exists to solve. + * + * This is not hypothetical: up_textheap_memalign() falls back to the kernel + * heap and derives an instruction-bus alias for anything it finds outside + * internal RAM, which is the path a dlopen()ed shared library would take. + * Kernel-side PSRAM has to come from outside the pool. + */ + +#ifdef CONFIG_ESP32S3_SPIRAM_COMMON_HEAP +# error "the page pool cannot be shared with a PSRAM kernel heap" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct window_s +{ + FAR const char *name; + uintptr_t vbase; + uint32_t npages; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Every cache-MMU window this configuration uses, checked against each other + * and against the kernel's PSRAM window at boot. The scratch region is one + * of them: it is a real window now, and the heap window growing into it + * would be as silent as every other mapping mistake in this port. + */ + +static const struct window_s g_windows[] = +{ + { + .name = "text", + .vbase = ESP32S3_TEXT_VBASE, + .npages = CONFIG_ARCH_TEXT_NPAGES + }, + { + .name = "data", + .vbase = ESP32S3_DATA_VBASE, + .npages = CONFIG_ARCH_DATA_NPAGES + }, + { + .name = "heap", + .vbase = ESP32S3_HEAP_VBASE, + .npages = CONFIG_ARCH_HEAP_NPAGES + }, + { + .name = "scratch", + .vbase = ESP32S3_KMAP_VBASE, + .npages = ESP32S3_KMAP_NPAGES + } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: check_windows_clear + * + * Description: + * Panic if any two cache-MMU windows overlap, or if one of them overlaps + * the kernel's PSRAM window. They would otherwise do it silently: the + * instruction and the data bus share one entry per 64 KB, so two windows + * 64 MB apart are the same entries, and mapping one over another simply + * takes the first one's pages away. + * + * The comparison is by entry, since that is what actually collides, and it + * belongs at run time because the kernel PSRAM window starts wherever the + * flash mappings end and so moves as the image grows. + * + * Input Parameters: + * kfirst - First cache-MMU entry of the kernel's PSRAM window. + * klast - Last cache-MMU entry of the kernel's PSRAM window. + * + ****************************************************************************/ + +static void check_windows_clear(uint32_t kfirst, uint32_t klast) +{ + size_t i; + size_t j; + + for (i = 0; i < nitems(g_windows); i++) + { + uint32_t first = MMU_ENTRY_OF(g_windows[i].vbase); + uint32_t last = first + g_windows[i].npages - 1; + + if (first <= klast && last >= kfirst) + { + _err("ERROR: %s window (MMU entries %" PRIu32 "-%" PRIu32 ") " + "overlaps the kernel PSRAM window (entries %" PRIu32 "-%" + PRIu32 ")\n", g_windows[i].name, first, last, kfirst, klast); + PANIC(); + } + + for (j = 0; j < i; j++) + { + uint32_t ofirst = MMU_ENTRY_OF(g_windows[j].vbase); + uint32_t olast = ofirst + g_windows[j].npages - 1; + + if (first <= olast && last >= ofirst) + { + _err("ERROR: %s window (MMU entries %" PRIu32 "-%" PRIu32 ") " + "overlaps the %s window (entries %" PRIu32 "-%" PRIu32 + ")\n", g_windows[i].name, first, last, g_windows[j].name, + ofirst, olast); + PANIC(); + } + } + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_allocate_pgheap + * + * Description: + * If there is a page allocator in the configuration, then this function + * must be provided by the platform-specific code. The OS initialization + * logic will call this function early in the initialization sequence to + * get the page heap information needed to configure the page allocator. + * + * On the ESP32-S3 the page pool is a slice of the external octal PSRAM. + * The pool is described by its *physical* base -- for the cache MMU that + * is the zero-based offset into the PSRAM device -- because mm_pgalloc() + * hands out physical page addresses. + * + * It is deliberately not mapped into the kernel. esp32s3_spiram.c maps + * the whole PSRAM device at boot, and this function is where the pool's + * share of that mapping is withdrawn again, after the checks that need it. + * The kernel reaches a pool page one at a time through esp32s3_pgmap(); + * see esp32s3_addrenv.h for why a permanent window cannot be allowed to + * stand. + * + * Input Parameters: + * heap_start - Receives the physical base address of the page pool. + * heap_size - Receives the size of the page pool in bytes. + * + ****************************************************************************/ + +void up_allocate_pgheap(void **heap_start, size_t *heap_size) +{ + DEBUGASSERT(heap_start && heap_size); + +#if defined(CONFIG_ESP32S3_SPIRAM) && defined(CONFIG_ARCH_ADDRENV) + /* Where the kernel's PSRAM window lands is decided at run time: + * esp32s3_spiram.c maps PSRAM immediately after the last cache-MMU entry + * the flash mappings occupy, so it moves as the kernel image grows. The + * pool is described by a compile-time *physical* base, so ask the cache + * MMU where that physical page currently is rather than deriving it from + * a constant -- which is how CONFIG_ARCH_PGPOOL_PBASE went stale twice, + * and the second time by exactly one page, which left one unwiped page in + * every region of every new process. + */ + + { + uintptr_t ramstart = (uintptr_t)esp_spiram_allocable_vaddr_start(); + uintptr_t ramend = (uintptr_t)esp_spiram_allocable_vaddr_end(); + uintptr_t poolvbase; + uint32_t rampbase; + + if (!esp32s3_mmu_paddr(ramstart, &rampbase)) + { + _err("ERROR: PSRAM window base %08" PRIxPTR " maps nothing\n", + ramstart); + PANIC(); + } + + if (ESP32S3_PGPOOL_PBASE < rampbase || + ESP32S3_PGPOOL_PEND > rampbase + (ramend - ramstart)) + { + _err("ERROR: page pool %08x-%08x is outside the mapped PSRAM " + "%08" PRIx32 "-%08" PRIxPTR "\n", + ESP32S3_PGPOOL_PBASE, ESP32S3_PGPOOL_PEND, + rampbase, rampbase + (ramend - ramstart)); + PANIC(); + } + + poolvbase = ramstart + (ESP32S3_PGPOOL_PBASE - rampbase); + + _info("PSRAM window %08" PRIxPTR "-%08" PRIxPTR " (phys %08" PRIx32 + "), page pool phys %08x-%08x at %08" PRIxPTR "\n", + ramstart, ramend, rampbase, + ESP32S3_PGPOOL_PBASE, ESP32S3_PGPOOL_PEND, poolvbase); + + check_windows_clear(MMU_ENTRY_OF(ramstart), MMU_ENTRY_OF(ramend - 1)); + + /* Everything above has been established while the pool was still + * mapped, which is the only time it can be. Now take that mapping + * away: from here the kernel reaches a pool page only through + * esp32s3_pgmap(), and an unprivileged task reaches one only if it is + * its own. + */ + + esp32s3_pgpool_unmap(poolvbase, ESP32S3_PGPOOL_SIZE); + } +#endif + + *heap_start = (void *)ESP32S3_PGPOOL_PBASE; + *heap_size = (size_t)ESP32S3_PGPOOL_SIZE; +} + +#ifdef CONFIG_BUILD_KERNEL + +/**************************************************************************** + * Name: pgalloc + * + * Description: + * If there is a page allocator in the configuration and if and MMU is + * available to map physical addresses to virtual address, then function + * must be provided by the platform-specific code. This is part of the + * implementation of sbrk(). This function will allocate the requested + * number of pages using the page allocator and map them into consecutive + * virtual addresses beginning with 'brkaddr' + * + * NOTE: This function does not use the up_ naming standard because it + * is indirectly callable from user-space code via a system trap. + * Therefore, it is a system interface and follows a different naming + * convention. + * + * The ESP32-S3 has no per-process page table: a page is "mapped" by + * recording it in the address environment's heap page array, and the + * shared data-bus cache-MMU window is programmed from that array by + * up_addrenv_select(). Since sbrk() runs on behalf of the calling task, + * whose environment is by definition the resident one, the new pages are + * also programmed into the window right away so the caller can use them + * without waiting for a context switch. + * + * Input Parameters: + * brkaddr - The heap break address. The next page will be allocated and + * mapped to this address. Must be page aligned. If the memory manager + * has not yet been initialized and this is the first block requested for + * the heap, then brkaddr should be zero. pgalloc will then assigned the + * well-known virtual address of the beginning of the heap. + * npages - The number of pages to allocate and map. Mapping of pages + * will be contiguous beginning beginning at 'brkaddr' + * + * Returned Value: + * The (virtual) base address of the mapped page will returned on success. + * Normally this will be the same as the 'brkaddr' input. However, if + * the 'brkaddr' input was zero, this will be the virtual address of the + * beginning of the heap. Zero is returned on any failure. + * + ****************************************************************************/ + +uintptr_t pgalloc(uintptr_t brkaddr, unsigned int npages) +{ + struct tcb_s *tcb = this_task(); + arch_addrenv_t *addrenv; + uintptr_t vaddr; + unsigned int index; + + DEBUGASSERT(tcb && tcb->addrenv_own); + addrenv = &tcb->addrenv_own->addrenv; + + /* brkaddr = 0 means that no heap has yet been allocated */ + + if (brkaddr == 0) + { + brkaddr = addrenv->heapvbase; + } + + DEBUGASSERT(brkaddr >= addrenv->heapvbase); + DEBUGASSERT(MM_ISALIGNED(brkaddr)); + + /* Start mapping from the old heap break address */ + + vaddr = brkaddr; + index = (brkaddr - addrenv->heapvbase) >> MM_PGSHIFT; + + for (; npages > 0; npages--, index++, vaddr += MM_PGSIZE) + { + uintptr_t paddr; + + if (index >= CONFIG_ARCH_HEAP_NPAGES) + { + berr("ERROR: heap window is full (%d pages)\n", + CONFIG_ARCH_HEAP_NPAGES); + return 0; + } + + /* up_addrenv_create() already backs the initial heap allocation, so a + * page may well be present at this index. Reuse it rather than + * leaking it behind a fresh allocation. + */ + + paddr = addrenv->heappages[index]; + if (paddr == 0) + { + paddr = mm_pgalloc(1); + if (paddr == 0) + { + berr("ERROR: page pool exhausted\n"); + return 0; + } + + esp32s3_pgwipe(paddr); + + addrenv->heappages[index] = paddr; + if (index >= addrenv->nheap) + { + addrenv->nheap = index + 1; + } + } + + /* Make the page reachable by the running task */ + + esp32s3_addrenv_mapnew(addrenv, vaddr, paddr); + } + + return brkaddr; +} + +#endif /* CONFIG_BUILD_KERNEL */ +#endif /* CONFIG_MM_PGALLOC */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_pms.c b/arch/xtensa/src/esp32s3/esp32s3_pms.c new file mode 100644 index 0000000000000..e68c284252a5e --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_pms.c @@ -0,0 +1,608 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_pms.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 + +#include +#include +#include + +#include + +#include "chip.h" +#include "xtensa.h" +#include "hardware/esp32s3_apb_ctrl.h" +#include "hardware/esp32s3_cache_memory.h" +#include "hardware/esp32s3_sensitive.h" +#include "hardware/esp32s3_soc.h" + +#include "esp32s3_pms.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Helper just for shortening */ + +#define VALUE_TO_PMS_FIELD(v, f) VALUE_TO_FIELD(v, SENSITIVE_CORE_X_ ## f) + +/* Categories bits for split line configuration */ + +#define PMS_SRAM_CATEGORY_BELOW 0x0 +#define PMS_SRAM_CATEGORY_EQUAL 0x2 +#define PMS_SRAM_CATEGORY_ABOVE 0x3 + +/* Offsets for helping setting values to register fields */ + +#define ICACHE_PMS_W0_BASE 12 +#define ICACHE_PMS_W1_BASE 12 +#define ICACHE_PMS_S 3 +#define ICACHE_PMS_V 7 + +#define IRAM_PMS_W0_BASE 0 +#define IRAM_PMS_W1_BASE 0 +#define IRAM_PMS_S 3 +#define IRAM_PMS_V 7 + +#define DRAM_PMS_W0_BASE 0 +#define DRAM_PMS_W1_BASE 12 +#define DRAM_PMS_S 2 +#define DRAM_PMS_V 3 + +#define FLASH_CACHE_S 3 +#define FLASH_CACHE_V 7 + +#define PIF_PMS_MAX_REG_ENTRY 16 +#define PIF_PMS_V 3 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const intptr_t g_sram_rg3_level_hlimits[] = +{ + 0x4037ffff, /* Block 2 (32KB) */ + 0x4038ffff, /* Block 3 (64KB) */ + 0x4039ffff, /* Block 4 (64KB) */ + 0x403affff, /* Block 5 (64KB) */ + 0x403bffff, /* Block 6 (64KB) */ + 0x403cffff, /* Block 7 (64KB) */ + 0x403dffff /* Block 8 (64KB) */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: set_iram_split_line + * + * Description: + * Split the IRAM region into two sub regions. + * + * Input Parameters: + * addr - Address for the split line. + * sensitive_reg - Register to which the split line configuration will be + * applied. + * + ****************************************************************************/ + +static void set_iram_split_line(uintptr_t addr, const uint32_t sensitive_reg) +{ + /* Set category bits for a given split line */ + + uint32_t cat[7] = + { + [0 ... 6] = PMS_SRAM_CATEGORY_ABOVE + }; + + for (size_t x = 0; x < 7; x++) + { + if (addr <= g_sram_rg3_level_hlimits[x]) + { + cat[x] = PMS_SRAM_CATEGORY_EQUAL; + break; + } + else + { + cat[x] = PMS_SRAM_CATEGORY_BELOW; + } + } + + /* Resolve split address' significant bits. + * Split address must be aligned to 256 bytes. + */ + + uint32_t regval = + VALUE_TO_PMS_FIELD((addr >> 8), IRAM0_DRAM0_DMA_SRAM_SPLITADDR) | + VALUE_TO_PMS_FIELD(cat[6], IRAM0_DRAM0_DMA_SRAM_CATEGORY_6) | + VALUE_TO_PMS_FIELD(cat[5], IRAM0_DRAM0_DMA_SRAM_CATEGORY_5) | + VALUE_TO_PMS_FIELD(cat[4], IRAM0_DRAM0_DMA_SRAM_CATEGORY_4) | + VALUE_TO_PMS_FIELD(cat[3], IRAM0_DRAM0_DMA_SRAM_CATEGORY_3) | + VALUE_TO_PMS_FIELD(cat[2], IRAM0_DRAM0_DMA_SRAM_CATEGORY_2) | + VALUE_TO_PMS_FIELD(cat[1], IRAM0_DRAM0_DMA_SRAM_CATEGORY_1) | + VALUE_TO_PMS_FIELD(cat[0], IRAM0_DRAM0_DMA_SRAM_CATEGORY_0); + + putreg32(regval, sensitive_reg); +} + +/**************************************************************************** + * Name: set_dram_split_line + * + * Description: + * Split the DRAM region into two sub regions. + * + * Input Parameters: + * addr - Address for the split line. + * sensitive_reg - Register to which the split line configuration will be + * applied. + * + ****************************************************************************/ + +static void set_dram_split_line(uintptr_t addr, const uint32_t sensitive_reg) +{ + /* Set category bits for a given split line */ + + uint32_t cat[7] = + { + [0 ... 6] = PMS_SRAM_CATEGORY_ABOVE + }; + + for (size_t x = 0; x < 7; x++) + { + if (addr <= MAP_IRAM_TO_DRAM(g_sram_rg3_level_hlimits[x])) + { + cat[x] = PMS_SRAM_CATEGORY_EQUAL; + break; + } + else + { + cat[x] = PMS_SRAM_CATEGORY_BELOW; + } + } + + /* Resolve split address' significant bits. + * Split address must be aligned to 256 bytes. + */ + + uint32_t regval = + VALUE_TO_PMS_FIELD((addr >> 8), DRAM0_DMA_SRAM_LINE_0_SPLITADDR) | + VALUE_TO_PMS_FIELD(cat[6], DRAM0_DMA_SRAM_LINE_0_CATEGORY_6) | + VALUE_TO_PMS_FIELD(cat[5], DRAM0_DMA_SRAM_LINE_0_CATEGORY_5) | + VALUE_TO_PMS_FIELD(cat[4], DRAM0_DMA_SRAM_LINE_0_CATEGORY_4) | + VALUE_TO_PMS_FIELD(cat[3], DRAM0_DMA_SRAM_LINE_0_CATEGORY_3) | + VALUE_TO_PMS_FIELD(cat[2], DRAM0_DMA_SRAM_LINE_0_CATEGORY_2) | + VALUE_TO_PMS_FIELD(cat[1], DRAM0_DMA_SRAM_LINE_0_CATEGORY_1) | + VALUE_TO_PMS_FIELD(cat[0], DRAM0_DMA_SRAM_LINE_0_CATEGORY_0); + + putreg32(regval, sensitive_reg); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_pms_set_sram_main_split_line + ****************************************************************************/ + +void esp32s3_pms_set_sram_main_split_line(uintptr_t addr) +{ + set_iram_split_line(addr, + SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_1_REG); +} + +/**************************************************************************** + * Name: esp32s3_pms_set_iram_split_line + ****************************************************************************/ + +void esp32s3_pms_set_iram_split_line(enum pms_split_line_e line, + uintptr_t addr) +{ + switch (line) + { + case PMS_SPLIT_LINE_0: + { + set_iram_split_line(addr, + SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_2_REG); + } + break; + case PMS_SPLIT_LINE_1: + { + set_iram_split_line(addr, + SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_3_REG); + } + break; + default: + { + PANIC(); + } + break; + } +} + +/**************************************************************************** + * Name: esp32s3_pms_set_dram_split_line + ****************************************************************************/ + +void esp32s3_pms_set_dram_split_line(enum pms_split_line_e line, + uintptr_t addr) +{ + switch (line) + { + case PMS_SPLIT_LINE_0: + { + set_dram_split_line(addr, + SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_4_REG); + } + break; + case PMS_SPLIT_LINE_1: + { + set_dram_split_line(addr, + SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_5_REG); + } + break; + default: + { + PANIC(); + } + break; + } +} + +/**************************************************************************** + * Name: esp32s3_pms_set_flash_cache_split_line + ****************************************************************************/ + +void esp32s3_pms_set_flash_cache_split_line(enum pms_split_line_e line, + uintptr_t addr, size_t length) +{ + /* The starting address of each region should be aligned to 64 KB */ + + uintptr_t aligned_addr = ALIGN_DOWN(addr, MMU_PAGE_SIZE); + + /* The length of each region should be the integral multiples of 64 KB */ + + size_t length_pages = length / MMU_PAGE_SIZE; + + switch (line) + { + case PMS_SPLIT_LINE_0: + { + modifyreg32(APB_CTRL_FLASH_ACE0_ADDR_REG, + APB_CTRL_FLASH_ACE0_ADDR_S_M, + VALUE_TO_FIELD(aligned_addr, + APB_CTRL_FLASH_ACE0_ADDR_S)); + modifyreg32(APB_CTRL_FLASH_ACE0_SIZE_REG, + APB_CTRL_FLASH_ACE0_SIZE_M, + VALUE_TO_FIELD(length_pages, + APB_CTRL_FLASH_ACE0_SIZE)); + } + break; + case PMS_SPLIT_LINE_1: + { + modifyreg32(APB_CTRL_FLASH_ACE1_ADDR_REG, + APB_CTRL_FLASH_ACE1_ADDR_S_M, + VALUE_TO_FIELD(aligned_addr, + APB_CTRL_FLASH_ACE1_ADDR_S)); + modifyreg32(APB_CTRL_FLASH_ACE1_SIZE_REG, + APB_CTRL_FLASH_ACE1_SIZE_M, + VALUE_TO_FIELD(length_pages, + APB_CTRL_FLASH_ACE1_SIZE)); + } + break; + case PMS_SPLIT_LINE_2: + { + modifyreg32(APB_CTRL_FLASH_ACE2_ADDR_REG, + APB_CTRL_FLASH_ACE2_ADDR_S_M, + VALUE_TO_FIELD(aligned_addr, + APB_CTRL_FLASH_ACE2_ADDR_S)); + modifyreg32(APB_CTRL_FLASH_ACE2_SIZE_REG, + APB_CTRL_FLASH_ACE2_SIZE_M, + VALUE_TO_FIELD(length_pages, + APB_CTRL_FLASH_ACE2_SIZE)); + } + break; + case PMS_SPLIT_LINE_3: + { + modifyreg32(APB_CTRL_FLASH_ACE3_ADDR_REG, + APB_CTRL_FLASH_ACE3_ADDR_S_M, + VALUE_TO_FIELD(aligned_addr, + APB_CTRL_FLASH_ACE3_ADDR_S)); + modifyreg32(APB_CTRL_FLASH_ACE3_SIZE_REG, + APB_CTRL_FLASH_ACE3_SIZE_M, + VALUE_TO_FIELD(length_pages, + APB_CTRL_FLASH_ACE3_SIZE)); + } + break; + default: + { + PANIC(); + } + break; + } +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_iram_region + ****************************************************************************/ + +void esp32s3_pms_configure_iram_region(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags) +{ + uint32_t reg; + uint32_t offset; + + if (world == PMS_WORLD_0) + { + reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_2_REG; + offset = IRAM_PMS_W0_BASE; + } + else + { + reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_1_REG; + offset = IRAM_PMS_W1_BASE; + } + + uint32_t shift = offset + (area * IRAM_PMS_S); + uint32_t mask = IRAM_PMS_V << shift; + uint32_t val = (flags & IRAM_PMS_V) << shift; + + modifyreg32(reg, mask, val); +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_icache + ****************************************************************************/ + +void esp32s3_pms_configure_icache(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags) +{ + uint32_t reg; + uint32_t offset; + + if (world == PMS_WORLD_0) + { + reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_2_REG; + offset = ICACHE_PMS_W0_BASE; + } + else + { + reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_1_REG; + offset = ICACHE_PMS_W1_BASE; + } + + uint32_t shift = offset + (area * ICACHE_PMS_S); + uint32_t mask = ICACHE_PMS_V << shift; + uint32_t val = (flags & ICACHE_PMS_V) << shift; + + modifyreg32(reg, mask, val); +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_dcache + ****************************************************************************/ + +void esp32s3_pms_configure_dcache(enum esp32s3_pms_world_e world, + enum pms_flags_e flags) +{ + switch (world) + { + case PMS_WORLD_0: + { + modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, + SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_0_M + | SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_1_M, + VALUE_TO_PMS_FIELD(flags, + DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_0) + | VALUE_TO_PMS_FIELD(flags, + DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_1)); + } + break; + case PMS_WORLD_1: + { + modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, + SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_0_M + | SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_1_M, + VALUE_TO_PMS_FIELD(flags, + DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_0) + | VALUE_TO_PMS_FIELD(flags, + DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_1)); + } + break; + default: + { + PANIC(); + } + break; + } +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_dram_region + ****************************************************************************/ + +void esp32s3_pms_configure_dram_region(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags) +{ + uint32_t offset; + + if (world == PMS_WORLD_0) + { + offset = DRAM_PMS_W0_BASE; + } + else + { + offset = DRAM_PMS_W1_BASE; + } + + uint32_t shift = offset + (area * DRAM_PMS_S); + uint32_t mask = DRAM_PMS_V << shift; + uint32_t val = (flags & DRAM_PMS_V) << shift; + + modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, mask, val); +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_flash_cache_region + ****************************************************************************/ + +void esp32s3_pms_configure_flash_cache_region(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags) +{ + const uint32_t shift = (FLASH_CACHE_S * world); + const uint32_t mask = FLASH_CACHE_V << shift; + uint32_t attr; + + if (flags == PMS_ACCESS_ALL) + { + attr = 0b11; + } + else if ((flags & PMS_ACCESS_W) != 0) + { + PANIC(); + } + else if ((flags & PMS_ACCESS_X) != 0) + { + attr = flags | 0b1; + } + else + { + attr = flags; + } + + uint32_t val = 0x40 | (attr & FLASH_CACHE_V) << shift; + + switch (area) + { + case PMS_AREA_0: + { + modifyreg32(APB_CTRL_FLASH_ACE0_ATTR_REG, mask, val); + } + break; + case PMS_AREA_1: + { + modifyreg32(APB_CTRL_FLASH_ACE1_ATTR_REG, mask, val); + } + break; + case PMS_AREA_2: + { + modifyreg32(APB_CTRL_FLASH_ACE2_ATTR_REG, mask, val); + } + break; + case PMS_AREA_3: + { + modifyreg32(APB_CTRL_FLASH_ACE3_ATTR_REG, mask, val); + } + break; + default: + { + PANIC(); + } + break; + } +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_peripheral + ****************************************************************************/ + +void esp32s3_pms_configure_peripheral(enum pms_peripheral_e periph, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags) +{ + uint32_t reg = 0; + uint32_t reg_off = periph / PIF_PMS_MAX_REG_ENTRY; + uint32_t bit_field_base = 30 - (2 * (periph % PIF_PMS_MAX_REG_ENTRY)); + + switch (world) + { + case PMS_WORLD_0: + { + reg = SENSITIVE_CORE_0_PIF_PMS_CONSTRAIN_1_REG + (4 * reg_off); + } + break; + case PMS_WORLD_1: + { + reg = SENSITIVE_CORE_0_PIF_PMS_CONSTRAIN_5_REG + (4 * reg_off); + } + break; + default: + { + PANIC(); + } + break; + } + + uint32_t mask = PIF_PMS_V << bit_field_base; + uint32_t val = (flags & PIF_PMS_V) << bit_field_base; + + modifyreg32(reg, mask, val); +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_irom_access + ****************************************************************************/ + +void esp32s3_pms_configure_irom_access(void) +{ + /* Kernel permission to the IROM region */ + + modifyreg32(SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_2_REG, + SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS_M, + VALUE_TO_PMS_FIELD(PMS_ACCESS_ALL, + IRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS)); + + /* User permission to the IROM region */ + + modifyreg32(SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_1_REG, + SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS_M, + VALUE_TO_PMS_FIELD(PMS_ACCESS_NONE, + IRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS)); +} + +/**************************************************************************** + * Name: esp32s3_pms_configure_drom_access + ****************************************************************************/ + +void esp32s3_pms_configure_drom_access(void) +{ + /* Kernel permission to the DROM region */ + + modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, + SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS_M, + VALUE_TO_PMS_FIELD(PMS_ACCESS_ALL, + DRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS)); + + /* User permission to the DROM region */ + + modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, + SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS_M, + VALUE_TO_PMS_FIELD(PMS_ACCESS_NONE, + DRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS)); +} diff --git a/arch/xtensa/src/esp32s3/esp32s3_pms.h b/arch/xtensa/src/esp32s3/esp32s3_pms.h new file mode 100644 index 0000000000000..fe4bb7cc787c6 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_pms.h @@ -0,0 +1,288 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_pms.h + * + * 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_PMS_H +#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PMS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "esp32s3_wcl.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Split lines partition a memory type into areas. Up to 4 split lines + * (PMS_SPLIT_LINE_0..3) exist per applicable memory type. + */ + +enum pms_split_line_e +{ + PMS_SPLIT_LINE_0 = 0, + PMS_SPLIT_LINE_1, + PMS_SPLIT_LINE_2, + PMS_SPLIT_LINE_3 +}; + +/* Areas are the regions delimited by the BASE, the split lines and the END + * of a memory type. Up to 4 areas (PMS_AREA_0..3) exist per memory type. + */ + +enum pms_area_e +{ + PMS_AREA_0 = 0, /* Area between BASE and split_line 0 */ + PMS_AREA_1, /* Area between split_line 0 and split_line 1 */ + PMS_AREA_2, /* Area between split_line 1 and END */ + PMS_AREA_3, + PMS_AREA_INVALID +}; + +/* Per-operation access flags applied to a world within an area. */ + +enum pms_flags_e +{ + PMS_ACCESS_NONE = 0, + PMS_ACCESS_R = 1, + PMS_ACCESS_W = 2, + PMS_ACCESS_X = 4, + PMS_ACCESS_ALL = PMS_ACCESS_X | PMS_ACCESS_W | PMS_ACCESS_R +}; + +/* There are 55 peripherals, each having 2 bits for permission configuration. + * These are spread across 4 registers, each register having maximum of 16 + * peripheral entries. + * + * Enum defined as per the bit field position of the peripheral in the + * register: + * FIELD = 30 - 2 * (ENUM % 16) + */ + +enum pms_peripheral_e +{ + PMS_UART1 = 0, + PMS_I2S0, + PMS_I2C, + PMS_MISC, + PMS_HINF = 5, + PMS_IO_MUX = 7, + PMS_RTC, + PMS_FE = 10, + PMS_FE2 = 11, + PMS_GPIO, + PMS_G0SPI_0, + PMS_G0SPI_1, + PMS_UART, + PMS_SYSTIMER, + PMS_TIMERGROUP1, + PMS_TIMERGROUP, + PMS_PWM0, + PMS_BB, + PMS_BACKUP = 22, + PMS_LEDC, + PMS_SLC, + PMS_PCNT, + PMS_RMT, + PMS_SLCHOST, + PMS_UHCI0, + PMS_I2C_EXT0, + PMS_BT = 31, + PMS_PWR = 33, + PMS_WIFIMAC, + PMS_RWBT = 36, + PMS_UART2 = 39, + PMS_I2S1, + PMS_PWM1, + PMS_CAN, + PMS_SDIO_HOST, + PMS_I2C_EXT1, + PMS_APB_CTRL, + PMS_SPI_3, + PMS_SPI_2, + PMS_WORLD_CONTROLLER, + PMS_DIO, + PMS_AD, + PMS_CACHE_CONFIG, + PMS_DMA_COPY, + PMS_INTERRUPT, + PMS_SENSITIVE, + PMS_SYSTEM, + PMS_USB, + PMS_BT_PWR, + PMS_LCD_CAM, + PMS_APB_ADC, + PMS_CRYPTO_DMA, + PMS_CRYPTO_PERI, + PMS_USB_WRAP, + PMS_USB_DEVICE, + PMS_MAX +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_pms_set_sram_main_split_line + * + * Description: + * Configure the main Internal SRAM1 Instruction/Data split line. + * + ****************************************************************************/ + +void esp32s3_pms_set_sram_main_split_line(uintptr_t addr); + +/**************************************************************************** + * Name: esp32s3_pms_set_iram_split_line + * + * Description: + * Set one of the IRAM region split lines. + * + ****************************************************************************/ + +void esp32s3_pms_set_iram_split_line(enum pms_split_line_e line, + uintptr_t addr); + +/**************************************************************************** + * Name: esp32s3_pms_set_dram_split_line + * + * Description: + * Set one of the DRAM region split lines. + * + ****************************************************************************/ + +void esp32s3_pms_set_dram_split_line(enum pms_split_line_e line, + uintptr_t addr); + +/**************************************************************************** + * Name: esp32s3_pms_set_flash_cache_split_line + * + * Description: + * Split the External Flash cached region into sub regions. The starting + * address is aligned to 64 KB and the length is expressed in 64 KB pages. + * + ****************************************************************************/ + +void esp32s3_pms_set_flash_cache_split_line(enum pms_split_line_e line, + uintptr_t addr, size_t length); + +/**************************************************************************** + * Name: esp32s3_pms_configure_iram_region + * + * Description: + * Configure access permissions to a given IRAM split region for a world. + * + ****************************************************************************/ + +void esp32s3_pms_configure_iram_region(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags); + +/**************************************************************************** + * Name: esp32s3_pms_configure_dram_region + * + * Description: + * Configure access permissions to a given DRAM split region for a world. + * + ****************************************************************************/ + +void esp32s3_pms_configure_dram_region(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags); + +/**************************************************************************** + * Name: esp32s3_pms_configure_icache + * + * Description: + * Configure access permissions to the Internal SRAM0 blocks not used as + * Instruction Cache, for a world. + * + ****************************************************************************/ + +void esp32s3_pms_configure_icache(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags); + +/**************************************************************************** + * Name: esp32s3_pms_configure_dcache + * + * Description: + * Configure access permissions to the Internal SRAM2 blocks not used as + * Data Cache, for a world. + * + ****************************************************************************/ + +void esp32s3_pms_configure_dcache(enum esp32s3_pms_world_e world, + enum pms_flags_e flags); + +/**************************************************************************** + * Name: esp32s3_pms_configure_flash_cache_region + * + * Description: + * Configure access permissions to a given External Flash cached split + * region for a world. + * + ****************************************************************************/ + +void esp32s3_pms_configure_flash_cache_region(enum pms_area_e area, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags); + +/**************************************************************************** + * Name: esp32s3_pms_configure_peripheral + * + * Description: + * Configure access permissions to a given peripheral for a world. + * + ****************************************************************************/ + +void esp32s3_pms_configure_peripheral(enum pms_peripheral_e periph, + enum esp32s3_pms_world_e world, + enum pms_flags_e flags); + +/**************************************************************************** + * Name: esp32s3_pms_configure_irom_access + * + * Description: + * Configure the kernel (all) / user (none) access permissions to the IROM + * region. + * + ****************************************************************************/ + +void esp32s3_pms_configure_irom_access(void); + +/**************************************************************************** + * Name: esp32s3_pms_configure_drom_access + * + * Description: + * Configure the kernel (all) / user (none) access permissions to the DROM + * region. + * + ****************************************************************************/ + +void esp32s3_pms_configure_drom_access(void); + +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_PMS_H */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_user.c b/arch/xtensa/src/esp32s3/esp32s3_user.c index cb1c43e9949de..88050c5eb8976 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_user.c +++ b/arch/xtensa/src/esp32s3/esp32s3_user.c @@ -27,6 +27,10 @@ #include #include "xtensa.h" +#include +#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); diff --git a/arch/xtensa/src/esp32s3/esp32s3_userspace.c b/arch/xtensa/src/esp32s3/esp32s3_userspace.c index 30257f686978a..4f9731bd667d1 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_userspace.c +++ b/arch/xtensa/src/esp32s3/esp32s3_userspace.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -39,12 +40,13 @@ #include "esp_attr.h" #include "esp_irq.h" #include "esp32s3_userspace.h" -#include "hardware/esp32s3_apb_ctrl.h" +#include "esp32s3_mmu.h" +#include "esp32s3_pms.h" +#include "esp32s3_wcl.h" #include "hardware/esp32s3_cache_memory.h" #include "hardware/esp32s3_rom_layout.h" #include "hardware/esp32s3_sensitive.h" #include "hardware/esp32s3_soc.h" -#include "hardware/esp32s3_wcl_core.h" #include "soc/extmem_reg.h" @@ -60,57 +62,10 @@ #define MMU_SIZE 0x3f0000 #define MMU_BLOCK63_VADDR (MMU_BLOCK0_VADDR + MMU_SIZE) -/* Cache MMU address mask (MMU tables ignore bits which are zero) */ - -#define MMU_FLASH_MASK (~(MMU_PAGE_SIZE - 1)) - -/* Helper just for shortening */ - -#define VALUE_TO_PMS_FIELD(v, f) VALUE_TO_FIELD(v, SENSITIVE_CORE_X_ ## f) - /* Total addressable space is 1GB for the External Memories */ #define EXTMEM_MAX_LENGTH 0x40000000 -/* Maximum number of supported entry addresses */ - -#define WCL_ENTRY_MAX 13 - -/* Last value of the agreed sequence to be written to the address configured - * in WCL_CORE_0_MESSAGE_ADDR register. - */ - -#define WCL_SEQ_LAST_VAL 6 - -/* Categories bits for split line configuration */ - -#define PMS_SRAM_CATEGORY_BELOW 0x0 -#define PMS_SRAM_CATEGORY_EQUAL 0x2 -#define PMS_SRAM_CATEGORY_ABOVE 0x3 - -/* Offsets for helping setting values to register fields */ - -#define ICACHE_PMS_W0_BASE 12 -#define ICACHE_PMS_W1_BASE 12 -#define ICACHE_PMS_S 3 -#define ICACHE_PMS_V 7 - -#define IRAM_PMS_W0_BASE 0 -#define IRAM_PMS_W1_BASE 0 -#define IRAM_PMS_S 3 -#define IRAM_PMS_V 7 - -#define DRAM_PMS_W0_BASE 0 -#define DRAM_PMS_W1_BASE 12 -#define DRAM_PMS_S 2 -#define DRAM_PMS_V 3 - -#define FLASH_CACHE_S 3 -#define FLASH_CACHE_V 7 - -#define PIF_PMS_MAX_REG_ENTRY 16 -#define PIF_PMS_V 3 - /**************************************************************************** * Private Types ****************************************************************************/ @@ -128,224 +83,16 @@ struct user_image_load_header_s uintptr_t irom_size; /* Size of IROM region */ }; -enum pms_world_e -{ - PMS_WORLD_0 = 0, - PMS_WORLD_1 -}; - -enum pms_split_line_e -{ - PMS_SPLIT_LINE_0 = 0, - PMS_SPLIT_LINE_1, - PMS_SPLIT_LINE_2, - PMS_SPLIT_LINE_3 -}; - -enum pms_area_e -{ - PMS_AREA_0 = 0, /* Area between BASE and split_line 0 */ - PMS_AREA_1, /* Area between split_line 0 and split_line 1 */ - PMS_AREA_2, /* Area between split_line 1 and END */ - PMS_AREA_3, - PMS_AREA_INVALID -}; - -enum pms_flags_e -{ - PMS_ACCESS_NONE = 0, - PMS_ACCESS_R = 1, - PMS_ACCESS_W = 2, - PMS_ACCESS_X = 4, - PMS_ACCESS_ALL = PMS_ACCESS_X | PMS_ACCESS_W | PMS_ACCESS_R -}; - -/* There are 55 peripherals, each having 2 bits for permission configuration. - * These are spread across 4 registers, each register having maximum of 16 - * peripheral entries. - * - * Enum defined as per the bit field position of the peripheral in the - * register: - * FIELD = 30 - 2 * (ENUM % 16) - */ - -enum pms_peripheral_e -{ - PMS_UART1 = 0, - PMS_I2S0, - PMS_I2C, - PMS_MISC, - PMS_HINF = 5, - PMS_IO_MUX = 7, - PMS_RTC, - PMS_FE = 10, - PMS_FE2 = 11, - PMS_GPIO, - PMS_G0SPI_0, - PMS_G0SPI_1, - PMS_UART, - PMS_SYSTIMER, - PMS_TIMERGROUP1, - PMS_TIMERGROUP, - PMS_PWM0, - PMS_BB, - PMS_BACKUP = 22, - PMS_LEDC, - PMS_SLC, - PMS_PCNT, - PMS_RMT, - PMS_SLCHOST, - PMS_UHCI0, - PMS_I2C_EXT0, - PMS_BT = 31, - PMS_PWR = 33, - PMS_WIFIMAC, - PMS_RWBT = 36, - PMS_UART2 = 39, - PMS_I2S1, - PMS_PWM1, - PMS_CAN, - PMS_SDIO_HOST, - PMS_I2C_EXT1, - PMS_APB_CTRL, - PMS_SPI_3, - PMS_SPI_2, - PMS_WORLD_CONTROLLER, - PMS_DIO, - PMS_AD, - PMS_CACHE_CONFIG, - PMS_DMA_COPY, - PMS_INTERRUPT, - PMS_SENSITIVE, - PMS_SYSTEM, - PMS_USB, - PMS_BT_PWR, - PMS_LCD_CAM, - PMS_APB_ADC, - PMS_CRYPTO_DMA, - PMS_CRYPTO_PERI, - PMS_USB_WRAP, - PMS_USB_DEVICE, - PMS_MAX -}; - -/**************************************************************************** - * ROM Function Prototypes - ****************************************************************************/ - -extern uint32_t cache_suspend_dcache(void); -extern void cache_resume_dcache(uint32_t val); -extern void cache_invalidate_dcache_all(void); -extern void cache_invalidate_icache_all(void); -extern void cache_writeback_all(void); -extern int cache_dbus_mmu_set(uint32_t ext_ram, uint32_t vaddr, - uint32_t paddr, uint32_t psize, uint32_t num, - uint32_t fixed); -extern int cache_ibus_mmu_set(uint32_t ext_ram, uint32_t vaddr, - uint32_t paddr, uint32_t psize, uint32_t num, - uint32_t fixed); - /**************************************************************************** * Private Data ****************************************************************************/ static struct user_image_load_header_s g_header; -static const intptr_t g_sram_rg3_level_hlimits[] = -{ - 0x4037ffff, /* Block 2 (32KB) */ - 0x4038ffff, /* Block 3 (64KB) */ - 0x4039ffff, /* Block 4 (64KB) */ - 0x403affff, /* Block 5 (64KB) */ - 0x403bffff, /* Block 6 (64KB) */ - 0x403cffff, /* Block 7 (64KB) */ - 0x403dffff /* Block 8 (64KB) */ -}; - /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: dcache_suspend - * - * Description: - * Helper function for suspending the data cache access for the CPU. - * - * Input Parameters: - * needs_wb - Flag indicating whether the CPU should perform the - * write-back of the data cache contents prior to - * invalidation. - * - * Returned Value: - * Current cache state. - * - ****************************************************************************/ - -static inline uint32_t dcache_suspend(bool needs_wb) -{ - uint32_t dcache_state = cache_suspend_dcache(); - - if (needs_wb) - { - cache_writeback_all(); - } - - cache_invalidate_dcache_all(); - - return dcache_state; -} - -/**************************************************************************** - * Name: dcache_resume - * - * Description: - * Helper function for resuming the data cache access for the CPU. - * - * Input Parameters: - * cache_state - Previously saved data cache state to be restored. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static inline void dcache_resume(uint32_t cache_state) -{ - uint32_t regval; - - regval = getreg32(EXTMEM_DCACHE_CTRL1_REG); - regval &= ~EXTMEM_DCACHE_SHUT_CORE0_BUS; -#ifdef CONFIG_SMP - regval &= ~EXTMEM_DCACHE_SHUT_CORE1_BUS; -#endif - putreg32(regval, EXTMEM_DCACHE_CTRL1_REG); - - cache_resume_dcache(cache_state); -} - -/**************************************************************************** - * Name: calc_mmu_pages - * - * Description: - * Calculate the required number of MMU pages for mapping a given region - * from External Flash into Internal RAM. - * - * Input Parameters: - * size - Length of the region to map - * vaddr - Starting External Flash offset to map to Internal RAM - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static inline uint32_t calc_mmu_pages(uint32_t size, uint32_t vaddr) -{ - return (size + (vaddr - (vaddr & MMU_FLASH_MASK)) + MMU_PAGE_SIZE - 1) / - MMU_PAGE_SIZE; -} - /**************************************************************************** * Name: configure_flash_mmu * @@ -378,23 +125,21 @@ static noinline_function IRAM_ATTR void configure_flash_mmu(void) uint32_t app_irom_size = g_header.irom_size; uint32_t app_irom_vma = g_header.irom_vma; - uint32_t cache_state = dcache_suspend(false); + uint32_t cache_state = esp32s3_dcache_suspend(false); drom_lma_aligned = app_drom_lma & MMU_FLASH_MASK; drom_vma_aligned = app_drom_vma & MMU_FLASH_MASK; - drom_page_count = calc_mmu_pages(app_drom_size, app_drom_vma); - ASSERT(cache_dbus_mmu_set(SOC_MMU_ACCESS_FLASH, drom_vma_aligned, - drom_lma_aligned, 64, - (int)drom_page_count, 0) == 0); + drom_page_count = esp32s3_mmu_calc_pages(app_drom_size, app_drom_vma); + ASSERT(esp32s3_mmu_map_dbus(SOC_MMU_ACCESS_FLASH, drom_vma_aligned, + drom_lma_aligned, drom_page_count) == 0); irom_lma_aligned = app_irom_lma & MMU_FLASH_MASK; irom_vma_aligned = app_irom_vma & MMU_FLASH_MASK; - irom_page_count = calc_mmu_pages(app_irom_size, app_irom_vma); - ASSERT(cache_ibus_mmu_set(SOC_MMU_ACCESS_FLASH, irom_vma_aligned, - irom_lma_aligned, 64, - (int)irom_page_count, 0) == 0); + irom_page_count = esp32s3_mmu_calc_pages(app_irom_size, app_irom_vma); + ASSERT(esp32s3_mmu_map_ibus(SOC_MMU_ACCESS_FLASH, irom_vma_aligned, + irom_lma_aligned, irom_page_count) == 0); - dcache_resume(cache_state); + esp32s3_dcache_resume(cache_state); } /**************************************************************************** @@ -418,15 +163,15 @@ static noinline_function IRAM_ATTR const void *map_flash(uint32_t src_addr, uint32_t src_addr_aligned; uint32_t page_count; - uint32_t cache_state = dcache_suspend(false); + uint32_t cache_state = esp32s3_dcache_suspend(false); src_addr_aligned = src_addr & MMU_FLASH_MASK; - page_count = calc_mmu_pages(size, src_addr); + page_count = esp32s3_mmu_calc_pages(size, src_addr); - ASSERT(cache_dbus_mmu_set(SOC_MMU_ACCESS_FLASH, MMU_BLOCK63_VADDR, - src_addr_aligned, 64, (int)page_count, 0) == 0); + ASSERT(esp32s3_mmu_map_dbus(SOC_MMU_ACCESS_FLASH, MMU_BLOCK63_VADDR, + src_addr_aligned, page_count) == 0); - dcache_resume(cache_state); + esp32s3_dcache_resume(cache_state); return (void *)(MMU_BLOCK63_VADDR + (src_addr - src_addr_aligned)); } @@ -550,102 +295,6 @@ static void initialize_iram(void) } } -/**************************************************************************** - * Name: wcl_set_vecbase - * - * Description: - * Override Vector Table base address via World Controller. - * - * Input Parameters: - * world - World to which the vector table base address will apply - * to. - * vecbase - Vector table base address to set. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void wcl_set_vecbase(enum pms_world_e world, uintptr_t vecbase) -{ - switch (world) - { - case PMS_WORLD_0: - { - modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_1_REG, - SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD0_VALUE_M, - VALUE_TO_FIELD(vecbase >> 10, - SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD0_VALUE)); - } - break; - case PMS_WORLD_1: - { - modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_2_REG, - SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD1_VALUE_M, - VALUE_TO_FIELD(vecbase >> 10, - SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD1_VALUE)); - } - break; - default: - { - PANIC(); - } - break; - } - - modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_1_REG, - SENSITIVE_CORE_0_VECBASE_OVERRIDE_SEL_M, - VALUE_TO_FIELD(0x3, SENSITIVE_CORE_0_VECBASE_OVERRIDE_SEL)); - - modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_0_REG, - SENSITIVE_CORE_0_VECBASE_WORLD_MASK_M, 0); -} - -/**************************************************************************** - * Name: wcl_set_world0_entry - * - * Description: - * Configure the World Controller to switch to World 0 whenever the CPU - * performs an instruction fetch from a given address. - * - * Input Parameters: - * entry - Entry number. Up to 13 entry addresses are supported. - * Entry 0 is reserved and must be skipped. - * addr - Interrupt vector address that will trigger the change to - * World 0. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void wcl_set_world0_entry(uint32_t entry, uintptr_t addr) -{ - ASSERT(entry > 0 && entry <= WCL_ENTRY_MAX); - - /* Configure registers required for cleaning the World Controller write - * buffer upon World0 entry. - * - * Refer to ESP32-S3 Technical Reference Manual, section 16.4.3, for a - * detailed description of the write buffer clearing process. - */ - - putreg32(SOC_RTC_DATA_LOW, WCL_CORE_0_MESSAGE_ADDR_REG); - putreg32(WCL_SEQ_LAST_VAL, WCL_CORE_0_MESSAGE_MAX_REG); - - uint32_t reg = WCL_CORE_0_ENTRY_1_ADDR_REG + ((entry - 1) * 4); - - /* Write ENTRY address */ - - putreg32(addr, reg); - - /* Enable check for that particular address. When fetched, World will - * switch to World 0. - */ - - modifyreg32(WCL_CORE_0_ENTRY_CHECK_REG, 0, BIT(entry)); -} - /**************************************************************************** * Name: pms_violation_isr * @@ -696,13 +345,13 @@ static void pms_enable_interrupts(void) * The vector table for WORLD1 is placed right at the start of WORLD1 IRAM. */ - wcl_set_vecbase(PMS_WORLD_0, VECTORS_START); - wcl_set_vecbase(PMS_WORLD_1, UIRAM_START); + esp32s3_wcl_set_vecbase(PMS_WORLD_0, VECTORS_START); + esp32s3_wcl_set_vecbase(PMS_WORLD_1, UIRAM_START); extern void _user_exception_vector(void); - wcl_set_world0_entry(1, (uintptr_t)_user_exception_vector); + esp32s3_wcl_set_world0_entry(1, (uintptr_t)_user_exception_vector); extern void _xtensa_level3_vector(void); - wcl_set_world0_entry(2, (uintptr_t)_xtensa_level3_vector); + esp32s3_wcl_set_world0_entry(2, (uintptr_t)_xtensa_level3_vector); /* Enable IRAM0 permission violation interrupt */ @@ -741,664 +390,6 @@ static void pms_enable_interrupts(void) SENSITIVE_CORE_0_PIF_PMS_MONITOR_VIOLATE_EN); } -/**************************************************************************** - * Name: set_iram_split_line - * - * Description: - * Split the IRAM region into two sub regions. - * - * Input Parameters: - * addr - Address for the split line. - * sensitive_reg - Register to which the split line configuration will be - * applied. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void set_iram_split_line(uintptr_t addr, const uint32_t sensitive_reg) -{ - /* Set category bits for a given split line */ - - uint32_t cat[7] = - { - [0 ... 6] = PMS_SRAM_CATEGORY_ABOVE - }; - - for (size_t x = 0; x < 7; x++) - { - if (addr <= g_sram_rg3_level_hlimits[x]) - { - cat[x] = PMS_SRAM_CATEGORY_EQUAL; - break; - } - else - { - cat[x] = PMS_SRAM_CATEGORY_BELOW; - } - } - - /* Resolve split address' significant bits. - * Split address must be aligned to 256 bytes. - */ - - uint32_t regval = - VALUE_TO_PMS_FIELD((addr >> 8), IRAM0_DRAM0_DMA_SRAM_SPLITADDR) | - VALUE_TO_PMS_FIELD(cat[6], IRAM0_DRAM0_DMA_SRAM_CATEGORY_6) | - VALUE_TO_PMS_FIELD(cat[5], IRAM0_DRAM0_DMA_SRAM_CATEGORY_5) | - VALUE_TO_PMS_FIELD(cat[4], IRAM0_DRAM0_DMA_SRAM_CATEGORY_4) | - VALUE_TO_PMS_FIELD(cat[3], IRAM0_DRAM0_DMA_SRAM_CATEGORY_3) | - VALUE_TO_PMS_FIELD(cat[2], IRAM0_DRAM0_DMA_SRAM_CATEGORY_2) | - VALUE_TO_PMS_FIELD(cat[1], IRAM0_DRAM0_DMA_SRAM_CATEGORY_1) | - VALUE_TO_PMS_FIELD(cat[0], IRAM0_DRAM0_DMA_SRAM_CATEGORY_0); - - putreg32(regval, sensitive_reg); -} - -/**************************************************************************** - * Name: pms_set_sram_main_split_line - * - * Description: - * Configure the Internal SRAM1 Instruction and Data regions. - * - * Input Parameters: - * addr - Address for the split line. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static inline void pms_set_sram_main_split_line(uintptr_t addr) -{ - set_iram_split_line(addr, - SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_1_REG); -} - -/**************************************************************************** - * Name: pms_set_iram_split_line - * - * Description: - * Helper function for setting the a split line into IRAM region. - * - * Input Parameters: - * line - Split line to be set. - * addr - Address for the split line. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static inline void pms_set_iram_split_line(enum pms_split_line_e line, - uintptr_t addr) -{ - switch (line) - { - case PMS_SPLIT_LINE_0: - { - set_iram_split_line(addr, - SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_2_REG); - } - break; - case PMS_SPLIT_LINE_1: - { - set_iram_split_line(addr, - SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_3_REG); - } - break; - default: - { - PANIC(); - } - break; - } -} - -/**************************************************************************** - * Name: pms_configure_iram_split_region - * - * Description: - * Configure access permissions to a given split region in IRAM. - * - * Input Parameters: - * area - A given region created after setting a split line. - * world - World to which the flags will apply to. - * flags - Attributes representing the operations allowed to be - * performed within the target area. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_iram_split_region(enum pms_area_e area, - enum pms_world_e world, - enum pms_flags_e flags) -{ - uint32_t reg; - uint32_t offset; - - if (world == PMS_WORLD_0) - { - reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_2_REG; - offset = IRAM_PMS_W0_BASE; - } - else - { - reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_1_REG; - offset = IRAM_PMS_W1_BASE; - } - - uint32_t shift = offset + (area * IRAM_PMS_S); - uint32_t mask = IRAM_PMS_V << shift; - uint32_t val = (flags & IRAM_PMS_V) << shift; - - modifyreg32(reg, mask, val); -} - -/**************************************************************************** - * Name: pms_configure_icache_permission - * - * Description: - * Configure access permissions to the Internal SRAM 0 blocks that won't be - * used as Instruction Cache. - * - * Input Parameters: - * area - Which of the two SRAM blocks used as Instruction Cache. - * world - World to which the flags will apply to. - * flags - Attributes representing the operations allowed to be - * performed within the target area. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_icache_permission(enum pms_area_e area, - enum pms_world_e world, - enum pms_flags_e flags) -{ - uint32_t reg; - uint32_t offset; - - if (world == PMS_WORLD_0) - { - reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_2_REG; - offset = ICACHE_PMS_W0_BASE; - } - else - { - reg = SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_1_REG; - offset = ICACHE_PMS_W1_BASE; - } - - uint32_t shift = offset + (area * ICACHE_PMS_S); - uint32_t mask = ICACHE_PMS_V << shift; - uint32_t val = (flags & ICACHE_PMS_V) << shift; - - modifyreg32(reg, mask, val); -} - -/**************************************************************************** - * Name: pms_configure_dcache_permission - * - * Description: - * Configure access permissions to the Internal SRAM 2 blocks that won't be - * used as Data Cache. - * - * Input Parameters: - * world - World to which the flags will apply to. - * flags - Attributes representing the operations allowed to be - * performed within the target area. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_dcache_permission(enum pms_world_e world, - enum pms_flags_e flags) -{ - switch (world) - { - case PMS_WORLD_0: - { - modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, - SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_0_M - | SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_1_M, - VALUE_TO_PMS_FIELD(flags, - DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_0) - | VALUE_TO_PMS_FIELD(flags, - DRAM0_PMS_CONSTRAIN_SRAM_WORLD_0_CACHEDATAARRAY_PMS_1)); - } - break; - case PMS_WORLD_1: - { - modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, - SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_0_M - | SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_1_M, - VALUE_TO_PMS_FIELD(flags, - DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_0) - | VALUE_TO_PMS_FIELD(flags, - DRAM0_PMS_CONSTRAIN_SRAM_WORLD_1_CACHEDATAARRAY_PMS_1)); - } - break; - default: - { - PANIC(); - } - break; - } -} - -/**************************************************************************** - * Name: set_dram_split_line - * - * Description: - * Split the DRAM region into two sub regions. - * - * Input Parameters: - * addr - Address for the split line. - * sensitive_reg - Register to which the split line configuration will be - * applied. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void set_dram_split_line(uintptr_t addr, const uint32_t sensitive_reg) -{ - /* Set category bits for a given split line */ - - uint32_t cat[7] = - { - [0 ... 6] = PMS_SRAM_CATEGORY_ABOVE - }; - - for (size_t x = 0; x < 7; x++) - { - if (addr <= MAP_IRAM_TO_DRAM(g_sram_rg3_level_hlimits[x])) - { - cat[x] = PMS_SRAM_CATEGORY_EQUAL; - break; - } - else - { - cat[x] = PMS_SRAM_CATEGORY_BELOW; - } - } - - /* Resolve split address' significant bits. - * Split address must be aligned to 256 bytes. - */ - - uint32_t regval = - VALUE_TO_PMS_FIELD((addr >> 8), DRAM0_DMA_SRAM_LINE_0_SPLITADDR) | - VALUE_TO_PMS_FIELD(cat[6], DRAM0_DMA_SRAM_LINE_0_CATEGORY_6) | - VALUE_TO_PMS_FIELD(cat[5], DRAM0_DMA_SRAM_LINE_0_CATEGORY_5) | - VALUE_TO_PMS_FIELD(cat[4], DRAM0_DMA_SRAM_LINE_0_CATEGORY_4) | - VALUE_TO_PMS_FIELD(cat[3], DRAM0_DMA_SRAM_LINE_0_CATEGORY_3) | - VALUE_TO_PMS_FIELD(cat[2], DRAM0_DMA_SRAM_LINE_0_CATEGORY_2) | - VALUE_TO_PMS_FIELD(cat[1], DRAM0_DMA_SRAM_LINE_0_CATEGORY_1) | - VALUE_TO_PMS_FIELD(cat[0], DRAM0_DMA_SRAM_LINE_0_CATEGORY_0); - - putreg32(regval, sensitive_reg); -} - -/**************************************************************************** - * Name: pms_set_dram_split_line - * - * Description: - * Helper function for setting the a split line into DRAM region. - * - * Input Parameters: - * line - Split line to be set. - * addr - Address for the split line. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_set_dram_split_line(enum pms_split_line_e line, - uintptr_t addr) -{ - switch (line) - { - case PMS_SPLIT_LINE_0: - { - set_dram_split_line(addr, - SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_4_REG); - } - break; - case PMS_SPLIT_LINE_1: - { - set_dram_split_line(addr, - SENSITIVE_CORE_X_IRAM0_DRAM0_DMA_SPLIT_LINE_CONSTRAIN_5_REG); - } - break; - default: - { - PANIC(); - } - break; - } -} - -/**************************************************************************** - * Name: pms_configure_dram_split_region - * - * Description: - * Configure access permissions to a given split region in DRAM. - * - * Input Parameters: - * area - A given region created after setting a split line. - * world - World to which the flags will apply to. - * flags - Attributes representing the operations allowed to be - * performed within the target area. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_dram_split_region(enum pms_area_e area, - enum pms_world_e world, - enum pms_flags_e flags) -{ - uint32_t offset; - - if (world == PMS_WORLD_0) - { - offset = DRAM_PMS_W0_BASE; - } - else - { - offset = DRAM_PMS_W1_BASE; - } - - uint32_t shift = offset + (area * DRAM_PMS_S); - uint32_t mask = DRAM_PMS_V << shift; - uint32_t val = (flags & DRAM_PMS_V) << shift; - - modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, mask, val); -} - -/**************************************************************************** - * Name: pms_set_flash_cache_split_line - * - * Description: - * Split the External Flash region into sub regions. - * - * Input Parameters: - * line - Split line to be set. - * addr - Starting address for the new region. - * length - Length of the new region in bytes. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_set_flash_cache_split_line(enum pms_split_line_e line, - uintptr_t addr, size_t length) -{ - /* The starting address of each region should be aligned to 64 KB */ - - uintptr_t aligned_addr = ALIGN_DOWN(addr, MMU_PAGE_SIZE); - - /* The length of each region should be the integral multiples of 64 KB */ - - size_t length_pages = length / MMU_PAGE_SIZE; - - switch (line) - { - case PMS_SPLIT_LINE_0: - { - modifyreg32(APB_CTRL_FLASH_ACE0_ADDR_REG, - APB_CTRL_FLASH_ACE0_ADDR_S_M, - VALUE_TO_FIELD(aligned_addr, - APB_CTRL_FLASH_ACE0_ADDR_S)); - modifyreg32(APB_CTRL_FLASH_ACE0_SIZE_REG, - APB_CTRL_FLASH_ACE0_SIZE_M, - VALUE_TO_FIELD(length_pages, - APB_CTRL_FLASH_ACE0_SIZE)); - } - break; - case PMS_SPLIT_LINE_1: - { - modifyreg32(APB_CTRL_FLASH_ACE1_ADDR_REG, - APB_CTRL_FLASH_ACE1_ADDR_S_M, - VALUE_TO_FIELD(aligned_addr, - APB_CTRL_FLASH_ACE1_ADDR_S)); - modifyreg32(APB_CTRL_FLASH_ACE1_SIZE_REG, - APB_CTRL_FLASH_ACE1_SIZE_M, - VALUE_TO_FIELD(length_pages, - APB_CTRL_FLASH_ACE1_SIZE)); - } - break; - case PMS_SPLIT_LINE_2: - { - modifyreg32(APB_CTRL_FLASH_ACE2_ADDR_REG, - APB_CTRL_FLASH_ACE2_ADDR_S_M, - VALUE_TO_FIELD(aligned_addr, - APB_CTRL_FLASH_ACE2_ADDR_S)); - modifyreg32(APB_CTRL_FLASH_ACE2_SIZE_REG, - APB_CTRL_FLASH_ACE2_SIZE_M, - VALUE_TO_FIELD(length_pages, - APB_CTRL_FLASH_ACE2_SIZE)); - } - break; - case PMS_SPLIT_LINE_3: - { - modifyreg32(APB_CTRL_FLASH_ACE3_ADDR_REG, - APB_CTRL_FLASH_ACE3_ADDR_S_M, - VALUE_TO_FIELD(aligned_addr, - APB_CTRL_FLASH_ACE3_ADDR_S)); - modifyreg32(APB_CTRL_FLASH_ACE3_SIZE_REG, - APB_CTRL_FLASH_ACE3_SIZE_M, - VALUE_TO_FIELD(length_pages, - APB_CTRL_FLASH_ACE3_SIZE)); - } - break; - default: - { - PANIC(); - } - break; - } -} - -/**************************************************************************** - * Name: pms_configure_flash_cache_split_region - * - * Description: - * Configure access permissions to a given split region in External Flash. - * - * Input Parameters: - * area - A given region created after setting a split line. - * world - World to which the flags will apply to. - * flags - Attributes representing the operations allowed to be - * performed within the target area. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void -pms_configure_flash_cache_split_region(enum pms_area_e area, - enum pms_world_e world, - enum pms_flags_e flags) -{ - const uint32_t shift = (FLASH_CACHE_S * world); - const uint32_t mask = FLASH_CACHE_V << shift; - uint32_t attr; - - if (flags == PMS_ACCESS_ALL) - { - attr = 0b11; - } - else if ((flags & PMS_ACCESS_W) != 0) - { - PANIC(); - } - else if ((flags & PMS_ACCESS_X) != 0) - { - attr = flags | 0b1; - } - else - { - attr = flags; - } - - uint32_t val = 0x40 | (attr & FLASH_CACHE_V) << shift; - - switch (area) - { - case PMS_AREA_0: - { - modifyreg32(APB_CTRL_FLASH_ACE0_ATTR_REG, mask, val); - } - break; - case PMS_AREA_1: - { - modifyreg32(APB_CTRL_FLASH_ACE1_ATTR_REG, mask, val); - } - break; - case PMS_AREA_2: - { - modifyreg32(APB_CTRL_FLASH_ACE2_ATTR_REG, mask, val); - } - break; - case PMS_AREA_3: - { - modifyreg32(APB_CTRL_FLASH_ACE3_ATTR_REG, mask, val); - } - break; - default: - { - PANIC(); - } - break; - } -} - -/**************************************************************************** - * Name: pms_configure_peripheral_permission - * - * Description: - * Configure access permissions to a given peripheral. - * - * Input Parameters: - * periph - A given peripheral to be configured. - * world - World to which the flags will apply to. - * flags - Attributes representing the operations allowed to be - * performed with the selected peripheral. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_peripheral_permission(enum pms_peripheral_e periph, - enum pms_world_e world, - enum pms_flags_e flags) -{ - uint32_t reg = 0; - uint32_t reg_off = periph / PIF_PMS_MAX_REG_ENTRY; - uint32_t bit_field_base = 30 - (2 * (periph % PIF_PMS_MAX_REG_ENTRY)); - - switch (world) - { - case PMS_WORLD_0: - { - reg = SENSITIVE_CORE_0_PIF_PMS_CONSTRAIN_1_REG + (4 * reg_off); - } - break; - case PMS_WORLD_1: - { - reg = SENSITIVE_CORE_0_PIF_PMS_CONSTRAIN_5_REG + (4 * reg_off); - } - break; - default: - { - PANIC(); - } - break; - } - - uint32_t mask = PIF_PMS_V << bit_field_base; - uint32_t val = (flags & PIF_PMS_V) << bit_field_base; - - modifyreg32(reg, mask, val); -} - -/**************************************************************************** - * Name: pms_configure_irom_access - * - * Description: - * Configure the access permissions to the IROM region. - * - * Input Parameters: - * None. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_irom_access(void) -{ - /* Kernel permission to the IROM region */ - - modifyreg32(SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_2_REG, - SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS_M, - VALUE_TO_PMS_FIELD(PMS_ACCESS_ALL, - IRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS)); - - /* User permission to the IROM region */ - - modifyreg32(SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_1_REG, - SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS_M, - VALUE_TO_PMS_FIELD(PMS_ACCESS_NONE, - IRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS)); -} - -/**************************************************************************** - * Name: pms_configure_drom_access - * - * Description: - * Configure the access permissions to the DROM region. - * - * Input Parameters: - * None. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -static void pms_configure_drom_access(void) -{ - /* Kernel permission to the DROM region */ - - modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, - SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS_M, - VALUE_TO_PMS_FIELD(PMS_ACCESS_ALL, - DRAM0_PMS_CONSTRAIN_ROM_WORLD_0_PMS)); - - /* User permission to the DROM region */ - - modifyreg32(SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_1_REG, - SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS_M, - VALUE_TO_PMS_FIELD(PMS_ACCESS_NONE, - DRAM0_PMS_CONSTRAIN_ROM_WORLD_1_PMS)); -} - /**************************************************************************** * Name: pms_configure_iram_access * @@ -1417,36 +408,36 @@ static void pms_configure_iram_access(void) { /* Kernel permission to the Instruction Cache */ - pms_configure_icache_permission(PMS_AREA_0, PMS_WORLD_0, PMS_ACCESS_ALL); - pms_configure_icache_permission(PMS_AREA_1, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_icache(PMS_AREA_0, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_icache(PMS_AREA_1, PMS_WORLD_0, PMS_ACCESS_ALL); /* User permission to the Instruction Cache */ - pms_configure_icache_permission(PMS_AREA_0, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_icache(PMS_AREA_0, PMS_WORLD_1, PMS_ACCESS_NONE); #ifdef CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB /* In case the Instruction Cache size is configured to 16KB, the other 16KB * block from Internal SRAM0 (Block1) will be used as IRAM. */ - pms_configure_icache_permission(PMS_AREA_1, PMS_WORLD_1, PMS_ACCESS_ALL); + esp32s3_pms_configure_icache(PMS_AREA_1, PMS_WORLD_1, PMS_ACCESS_ALL); #else /* CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB */ /* In case the Instruction Cache size is configured to 32KB, the WORLD1 * access permissions to the Internal SRAM0 must be revoked. */ - pms_configure_icache_permission(PMS_AREA_1, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_icache(PMS_AREA_1, PMS_WORLD_1, PMS_ACCESS_NONE); #endif /* Set split lines to partition the IRAM into regions */ - pms_set_iram_split_line(PMS_SPLIT_LINE_0, UIRAM_END); - pms_set_iram_split_line(PMS_SPLIT_LINE_1, KIRAM_END); + esp32s3_pms_set_iram_split_line(PMS_SPLIT_LINE_0, UIRAM_END); + esp32s3_pms_set_iram_split_line(PMS_SPLIT_LINE_1, KIRAM_END); /* Configure Kernel access permissions to each split region */ - pms_configure_iram_split_region(PMS_AREA_0, PMS_WORLD_0, PMS_ACCESS_ALL); - pms_configure_iram_split_region(PMS_AREA_1, PMS_WORLD_0, PMS_ACCESS_ALL); - pms_configure_iram_split_region(PMS_AREA_2, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_iram_region(PMS_AREA_0, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_iram_region(PMS_AREA_1, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_iram_region(PMS_AREA_2, PMS_WORLD_0, PMS_ACCESS_ALL); /* Configure User access permissions to each split region */ @@ -1456,24 +447,29 @@ static void pms_configure_iram_access(void) * we can safely revoke permissions to the shared Internal SRAM1. */ - pms_configure_iram_split_region(PMS_AREA_0, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_iram_region(PMS_AREA_0, PMS_WORLD_1, + PMS_ACCESS_NONE); #else /* CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB */ /* In case the Instruction Cache size is configured to 32KB, the first * block from the shared Internal SRAM1 (Block2) will be allocated to * WORLD1. */ - pms_configure_iram_split_region(PMS_AREA_0, PMS_WORLD_1, PMS_ACCESS_ALL); + esp32s3_pms_configure_iram_region(PMS_AREA_0, PMS_WORLD_1, PMS_ACCESS_ALL); #endif - pms_configure_iram_split_region(PMS_AREA_1, PMS_WORLD_1, PMS_ACCESS_NONE); - pms_configure_iram_split_region(PMS_AREA_2, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_iram_region(PMS_AREA_1, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_iram_region(PMS_AREA_2, PMS_WORLD_1, + PMS_ACCESS_NONE); /* PMS_AREA_3 corresponds to the region after the main split line, * i.e. the entire DRAM. */ - pms_configure_iram_split_region(PMS_AREA_3, PMS_WORLD_0, PMS_ACCESS_NONE); - pms_configure_iram_split_region(PMS_AREA_3, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_iram_region(PMS_AREA_3, PMS_WORLD_0, + PMS_ACCESS_NONE); + esp32s3_pms_configure_iram_region(PMS_AREA_3, PMS_WORLD_1, + PMS_ACCESS_NONE); } /**************************************************************************** @@ -1494,11 +490,11 @@ static void pms_configure_dram_access(void) { /* Kernel permission to the Data Cache */ - pms_configure_dcache_permission(PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_dcache(PMS_WORLD_0, PMS_ACCESS_ALL); /* User permission to the Data Cache */ - pms_configure_dcache_permission(PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_dcache(PMS_WORLD_1, PMS_ACCESS_NONE); /* Split line to protect DRAM area reserved for ROM functions. * ALIGN_DOWN macro is used to align the address to 256 bit boundary. @@ -1509,27 +505,31 @@ static void pms_configure_dram_access(void) /* Set split lines to partition the DRAM into regions */ - pms_set_dram_split_line(PMS_SPLIT_LINE_0, UDRAM_START); - pms_set_dram_split_line(PMS_SPLIT_LINE_1, rom_reserve_aligned); + esp32s3_pms_set_dram_split_line(PMS_SPLIT_LINE_0, UDRAM_START); + esp32s3_pms_set_dram_split_line(PMS_SPLIT_LINE_1, rom_reserve_aligned); /* PMS_AREA_0 corresponds to the region before the main split line, * i.e entire IRAM. */ - pms_configure_dram_split_region(PMS_AREA_0, PMS_WORLD_0, PMS_ACCESS_NONE); - pms_configure_dram_split_region(PMS_AREA_0, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_dram_region(PMS_AREA_0, PMS_WORLD_0, + PMS_ACCESS_NONE); + esp32s3_pms_configure_dram_region(PMS_AREA_0, PMS_WORLD_1, + PMS_ACCESS_NONE); /* Configure Kernel access permissions to each split region */ - pms_configure_dram_split_region(PMS_AREA_1, PMS_WORLD_0, PMS_ACCESS_ALL); - pms_configure_dram_split_region(PMS_AREA_2, PMS_WORLD_0, PMS_ACCESS_ALL); - pms_configure_dram_split_region(PMS_AREA_3, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_dram_region(PMS_AREA_1, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_dram_region(PMS_AREA_2, PMS_WORLD_0, PMS_ACCESS_ALL); + esp32s3_pms_configure_dram_region(PMS_AREA_3, PMS_WORLD_0, PMS_ACCESS_ALL); /* Configure User access permissions to each split region */ - pms_configure_dram_split_region(PMS_AREA_1, PMS_WORLD_1, PMS_ACCESS_NONE); - pms_configure_dram_split_region(PMS_AREA_2, PMS_WORLD_1, PMS_ACCESS_ALL); - pms_configure_dram_split_region(PMS_AREA_3, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_dram_region(PMS_AREA_1, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_dram_region(PMS_AREA_2, PMS_WORLD_1, PMS_ACCESS_ALL); + esp32s3_pms_configure_dram_region(PMS_AREA_3, PMS_WORLD_1, + PMS_ACCESS_NONE); } /**************************************************************************** @@ -1551,8 +551,8 @@ static IRAM_ATTR void pms_configure_flash_cache_access(void) { /* Invalidate Cache */ - uint32_t cache_state = dcache_suspend(true); - cache_invalidate_icache_all(); + uint32_t cache_state = esp32s3_dcache_suspend(true); + esp32s3_icache_invalidate_all(); size_t partition_offset = USER_IMAGE_OFFSET; @@ -1576,43 +576,43 @@ static IRAM_ATTR void pms_configure_flash_cache_access(void) uint32_t region3_start_addr = region2_start_addr + region2_size; uint32_t region3_size = remaining_size / 2; - pms_set_flash_cache_split_line(PMS_SPLIT_LINE_0, region0_start_addr, - region0_size); - pms_set_flash_cache_split_line(PMS_SPLIT_LINE_1, region1_start_addr, - region1_size); - pms_set_flash_cache_split_line(PMS_SPLIT_LINE_2, region2_start_addr, - region2_size); - pms_set_flash_cache_split_line(PMS_SPLIT_LINE_3, region3_start_addr, - region3_size); + esp32s3_pms_set_flash_cache_split_line(PMS_SPLIT_LINE_0, + region0_start_addr, region0_size); + esp32s3_pms_set_flash_cache_split_line(PMS_SPLIT_LINE_1, + region1_start_addr, region1_size); + esp32s3_pms_set_flash_cache_split_line(PMS_SPLIT_LINE_2, + region2_start_addr, region2_size); + esp32s3_pms_set_flash_cache_split_line(PMS_SPLIT_LINE_3, + region3_start_addr, region3_size); /* Configure Kernel access permissions to each split region */ - pms_configure_flash_cache_split_region(PMS_AREA_0, PMS_WORLD_0, - PMS_ACCESS_ALL); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_0, PMS_WORLD_0, + PMS_ACCESS_ALL); /* WORLD0 requires access to WORLD1 to load the cache when returning to * WORLD1 from WORLD0. */ - pms_configure_flash_cache_split_region(PMS_AREA_1, PMS_WORLD_0, - PMS_ACCESS_ALL); - pms_configure_flash_cache_split_region(PMS_AREA_2, PMS_WORLD_0, - PMS_ACCESS_ALL); - pms_configure_flash_cache_split_region(PMS_AREA_3, PMS_WORLD_0, - PMS_ACCESS_ALL); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_1, PMS_WORLD_0, + PMS_ACCESS_ALL); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_2, PMS_WORLD_0, + PMS_ACCESS_ALL); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_3, PMS_WORLD_0, + PMS_ACCESS_ALL); /* Configure User access permissions to each split region */ - pms_configure_flash_cache_split_region(PMS_AREA_0, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_flash_cache_split_region(PMS_AREA_1, PMS_WORLD_1, - PMS_ACCESS_ALL); - pms_configure_flash_cache_split_region(PMS_AREA_2, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_flash_cache_split_region(PMS_AREA_3, PMS_WORLD_1, - PMS_ACCESS_NONE); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_0, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_1, PMS_WORLD_1, + PMS_ACCESS_ALL); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_2, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_flash_cache_region(PMS_AREA_3, PMS_WORLD_1, + PMS_ACCESS_NONE); - dcache_resume(cache_state); + esp32s3_dcache_resume(cache_state); } /**************************************************************************** @@ -1634,114 +634,81 @@ static void pms_configure_peripheral_access(void) { /* Revoke User access permission to every peripheral */ - pms_configure_peripheral_permission(PMS_UART1, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_I2C, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_MISC, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_IO_MUX, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_RTC, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_FE, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_FE2, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_GPIO, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_G0SPI_0, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_G0SPI_1, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_UART, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SYSTIMER, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_TIMERGROUP1, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_TIMERGROUP, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_BB, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_LEDC, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_RMT, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_UHCI0, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_I2C_EXT0, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_BT, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_PWR, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_WIFIMAC, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_RWBT, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_I2S1, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_CAN, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_APB_CTRL, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SPI_2, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_WORLD_CONTROLLER, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_DIO, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_AD, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_CACHE_CONFIG, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_DMA_COPY, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_INTERRUPT, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SENSITIVE, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SYSTEM, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_BT_PWR, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_APB_ADC, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_CRYPTO_DMA, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_CRYPTO_PERI, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_USB_WRAP, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_USB_DEVICE, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_I2S0, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_HINF, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_PWM0, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_BACKUP, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SLC, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_PCNT, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SLCHOST, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_UART2, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_PWM1, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SDIO_HOST, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_I2C_EXT1, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_SPI_3, PMS_WORLD_1, - PMS_ACCESS_NONE); - pms_configure_peripheral_permission(PMS_USB, PMS_WORLD_1, - PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_UART1, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_I2C, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_MISC, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_IO_MUX, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_RTC, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_FE, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_FE2, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_GPIO, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_G0SPI_0, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_G0SPI_1, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_UART, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SYSTIMER, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_TIMERGROUP1, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_TIMERGROUP, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_BB, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_LEDC, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_RMT, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_UHCI0, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_I2C_EXT0, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_BT, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_PWR, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_WIFIMAC, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_RWBT, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_I2S1, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_CAN, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_APB_CTRL, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SPI_2, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_WORLD_CONTROLLER, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_DIO, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_AD, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_CACHE_CONFIG, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_DMA_COPY, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_INTERRUPT, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SENSITIVE, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SYSTEM, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_BT_PWR, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_APB_ADC, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_CRYPTO_DMA, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_CRYPTO_PERI, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_USB_WRAP, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_USB_DEVICE, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_I2S0, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_HINF, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_PWM0, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_BACKUP, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SLC, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_PCNT, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SLCHOST, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_UART2, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_PWM1, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SDIO_HOST, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_I2C_EXT1, PMS_WORLD_1, + PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_SPI_3, PMS_WORLD_1, PMS_ACCESS_NONE); + esp32s3_pms_configure_peripheral(PMS_USB, PMS_WORLD_1, PMS_ACCESS_NONE); } /**************************************************************************** @@ -1768,7 +735,7 @@ static void configure_mpu(void) * split lines. */ - pms_set_sram_main_split_line(KIRAM_END); + esp32s3_pms_set_sram_main_split_line(KIRAM_END); /* Configure Kernel and Userspace permissions for accessing the internal * memories. @@ -1776,11 +743,11 @@ static void configure_mpu(void) /* IROM */ - pms_configure_irom_access(); + esp32s3_pms_configure_irom_access(); /* DROM */ - pms_configure_drom_access(); + esp32s3_pms_configure_drom_access(); /* IRAM */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_wcl.c b/arch/xtensa/src/esp32s3/esp32s3_wcl.c new file mode 100644 index 0000000000000..d66e836b321c0 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_wcl.c @@ -0,0 +1,127 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_wcl.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 + +#include +#include + +#include + +#include "chip.h" +#include "xtensa.h" +#include "hardware/esp32s3_sensitive.h" +#include "hardware/esp32s3_soc.h" +#include "hardware/esp32s3_wcl_core.h" + +#include "esp32s3_wcl.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maximum number of supported entry addresses */ + +#define WCL_ENTRY_MAX 13 + +/* Last value of the agreed sequence to be written to the address configured + * in WCL_CORE_0_MESSAGE_ADDR register. + */ + +#define WCL_SEQ_LAST_VAL 6 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_wcl_set_vecbase + ****************************************************************************/ + +void esp32s3_wcl_set_vecbase(enum esp32s3_pms_world_e world, + uintptr_t vecbase) +{ + switch (world) + { + case PMS_WORLD_0: + { + modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_1_REG, + SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD0_VALUE_M, + VALUE_TO_FIELD(vecbase >> 10, + SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD0_VALUE)); + } + break; + case PMS_WORLD_1: + { + modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_2_REG, + SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD1_VALUE_M, + VALUE_TO_FIELD(vecbase >> 10, + SENSITIVE_CORE_0_VECBASE_OVERRIDE_WORLD1_VALUE)); + } + break; + default: + { + PANIC(); + } + break; + } + + modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_1_REG, + SENSITIVE_CORE_0_VECBASE_OVERRIDE_SEL_M, + VALUE_TO_FIELD(0x3, SENSITIVE_CORE_0_VECBASE_OVERRIDE_SEL)); + + modifyreg32(SENSITIVE_CORE_0_VECBASE_OVERRIDE_0_REG, + SENSITIVE_CORE_0_VECBASE_WORLD_MASK_M, 0); +} + +/**************************************************************************** + * Name: esp32s3_wcl_set_world0_entry + ****************************************************************************/ + +void esp32s3_wcl_set_world0_entry(uint32_t entry, uintptr_t addr) +{ + ASSERT(entry > 0 && entry <= WCL_ENTRY_MAX); + + /* Configure registers required for cleaning the World Controller write + * buffer upon World0 entry. + * + * Refer to ESP32-S3 Technical Reference Manual, section 16.4.3, for a + * detailed description of the write buffer clearing process. + */ + + putreg32(SOC_RTC_DATA_LOW, WCL_CORE_0_MESSAGE_ADDR_REG); + putreg32(WCL_SEQ_LAST_VAL, WCL_CORE_0_MESSAGE_MAX_REG); + + uint32_t reg = WCL_CORE_0_ENTRY_1_ADDR_REG + ((entry - 1) * 4); + + /* Write ENTRY address */ + + putreg32(addr, reg); + + /* Enable check for that particular address. When fetched, World will + * switch to World 0. + */ + + modifyreg32(WCL_CORE_0_ENTRY_CHECK_REG, 0, BIT(entry)); +} diff --git a/arch/xtensa/src/esp32s3/esp32s3_wcl.h b/arch/xtensa/src/esp32s3/esp32s3_wcl.h new file mode 100644 index 0000000000000..b4be24467a71f --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_wcl.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_wcl.h + * + * 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_WCL_H +#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WCL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The ESP32-S3 World Controller provides a binary privileged (WORLD_0, + * kernel) / non-privileged (WORLD_1, user) split. The world identifier is + * shared vocabulary between the World Controller and the PMS permission + * subsystem, so it is defined here at the lowest layer. + */ + +enum esp32s3_pms_world_e +{ + PMS_WORLD_0 = 0, + PMS_WORLD_1 +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_wcl_set_vecbase + * + * Description: + * Override the Vector Table base address for a given world via the World + * Controller. + * + * Input Parameters: + * world - World to which the vector table base address will apply. + * vecbase - Vector table base address to set. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void esp32s3_wcl_set_vecbase(enum esp32s3_pms_world_e world, + uintptr_t vecbase); + +/**************************************************************************** + * Name: esp32s3_wcl_set_world0_entry + * + * Description: + * Configure the World Controller to switch to World 0 whenever the CPU + * performs an instruction fetch from a given address. + * + * Input Parameters: + * entry - Entry number. Up to 13 entry addresses are supported. Entry 0 + * is reserved and must be skipped. + * addr - Vector fetch address that triggers the switch to World 0. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void esp32s3_wcl_set_world0_entry(uint32_t entry, uintptr_t addr); + +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_WCL_H */ diff --git a/arch/xtensa/src/lx7/Toolchain.defs b/arch/xtensa/src/lx7/Toolchain.defs index cd4e593230cff..26dcb1f053591 100644 --- a/arch/xtensa/src/lx7/Toolchain.defs +++ b/arch/xtensa/src/lx7/Toolchain.defs @@ -240,7 +240,16 @@ LDMODULEFLAGS = -r -T $(call CONVERT_PATH,$(TOPDIR)/libs/libc/elf/gnu-elf.ld) CELFFLAGS = $(CFLAGS) -fvisibility=hidden -mtext-section-literals CXXELFFLAGS = $(CXXFLAGS) -fvisibility=hidden -mtext-section-literals -LDELFFLAGS = -r -e _start +LDELFFLAGS = -e _start + +# A relocatable object is the default for loadable modules. A kernel build +# instead needs each user program fully linked at the addresses of its +# address environment, so the partial link is dropped there. + +ifeq ($(CONFIG_BINFMT_ELF_RELOCATABLE),y) + LDELFFLAGS += -r +endif + LDELFFLAGS += -T $(call CONVERT_PATH,$(TOPDIR)$(DELIM)libs$(DELIM)libc$(DELIM)elf$(DELIM)gnu-elf.ld) ifneq ($(CONFIG_BUILD_KERNEL),y) # Flat build and protected elf entry point use crt0, diff --git a/boards/xtensa/esp32s3/common/scripts/esp32s3_sections.ld b/boards/xtensa/esp32s3/common/scripts/esp32s3_sections.ld index e0384d88f4200..5fa66fd9f3706 100644 --- a/boards/xtensa/esp32s3/common/scripts/esp32s3_sections.ld +++ b/boards/xtensa/esp32s3/common/scripts/esp32s3_sections.ld @@ -22,6 +22,19 @@ #include +/* A kernel build archives the architecture objects into libkarch.a rather + * than libarch.a, so the placement rules below have to name the right one. + * Getting this wrong is silent and fatal: the octal-flash bring-up functions + * stay in mapped flash and are called while that mapping is being + * reconfigured. + */ + +#ifdef CONFIG_BUILD_KERNEL +# define ARCHLIB *libkarch.a +#else +# define ARCHLIB *libarch.a +#endif + /* Default entry point: */ ENTRY(__start); @@ -165,20 +178,20 @@ SECTIONS *libpp.a:wifi_slp_iram.*(.literal .text .literal.* .text.*) *libpp.a:wifi_or_slp_iram.*(.literal .text .literal.* .text.*) *libpp.a:wifi_slp_rx_iram.*(.literal .text .literal.* .text.*) - *libarch.a:*esp_loader.*(.literal .text .literal.* .text.*) - *libarch.a:esp32s3_cpuindex.*(.literal .text .literal.* .text.*) - *libarch.a:esp32s3_user.*(.literal .text .literal.* .text.*) - *libarch.a:esp32s3_spiflash.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_assert.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_cpuint.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_cpupause.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_irqdispatch.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_modifyreg32.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_testset.*(.literal .text .literal.* .text.*) - *libarch.a:esp_app_desc.*(.literal .text .literal.* .text.*) - *libarch.a:intr_alloc.*(.literal.esp_intr_get_intno .text.esp_intr_get_intno) - *libarch.a:xtensa_intr.*(.literal.xt_get_interrupt_handler .text.xt_get_interrupt_handler) - *libarch.a:xtensa_intr.*(.literal.xt_get_interrupt_handler_arg .text.xt_get_interrupt_handler_arg) + ARCHLIB:*esp_loader.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_cpuindex.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_user.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_spiflash.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_assert.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_cpuint.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_cpupause.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_irqdispatch.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_modifyreg32.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_testset.*(.literal .text .literal.* .text.*) + ARCHLIB:esp_app_desc.*(.literal .text .literal.* .text.*) + ARCHLIB:intr_alloc.*(.literal.esp_intr_get_intno .text.esp_intr_get_intno) + ARCHLIB:xtensa_intr.*(.literal.xt_get_interrupt_handler .text.xt_get_interrupt_handler) + ARCHLIB:xtensa_intr.*(.literal.xt_get_interrupt_handler_arg .text.xt_get_interrupt_handler_arg) *libc.a:sq_remlast.*(.literal .text .literal.* .text.*) @@ -199,11 +212,11 @@ SECTIONS *libsched.a:stack_monitor.*(.literal .text .literal.* .text.*) #ifdef CONFIG_ESP32S3_SPEED_UP_ISR - *libarch.a:xtensa_switchcontext.*(.literal.up_switch_context .text.up_switch_context) + ARCHLIB:xtensa_switchcontext.*(.literal.up_switch_context .text.up_switch_context) - *libarch.a:esp32s3_timerisr.*(.literal.systimer_isr .text.systimer_isr) - *libarch.a:esp32s3_idle.*(.literal.up_idle .text.up_idle) - *libarch.a:esp32s3_dma.*(.literal.esp32s3_dma_load .text.esp32s3_dma_load \ + ARCHLIB:esp32s3_timerisr.*(.literal.systimer_isr .text.systimer_isr) + ARCHLIB:esp32s3_idle.*(.literal.up_idle .text.up_idle) + ARCHLIB:esp32s3_dma.*(.literal.esp32s3_dma_load .text.esp32s3_dma_load \ .literal.esp32s3_dma_enable .text.esp32s3_dma_enable) *libsched.a:sched_processtimer.*(.literal.nxsched_process_timer .text.nxsched_process_timer) @@ -219,12 +232,12 @@ SECTIONS *libc.a:sq_remfirst.*(.literal.sq_remfirst .text.sq_remfirst) #endif - *libarch.a:esp32s3_spi_timing.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_spi_timing.*(.literal .text .literal.* .text.*) #ifdef CONFIG_ESP32S3_SPIRAM_MODE_QUAD - *libarch.a:esp32s3_psram_quad.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_psram_quad.*(.literal .text .literal.* .text.*) #endif #ifdef CONFIG_ESP32S3_SPIRAM_MODE_OCT - *libarch.a:esp32s3_psram_octal.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_psram_octal.*(.literal .text .literal.* .text.*) #endif #if defined(CONFIG_STACK_CANARIES) && \ (defined(CONFIG_ESP32S3_SPIFLASH) || \ @@ -232,98 +245,98 @@ SECTIONS *libc.a:lib_stackchk.*(.literal .text .literal.* .text.*) #endif - *libarch.a:*brownout_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*cpu.*(.text .text.* .literal .literal.*) - *libarch.a:*gpio_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*periph_ctrl.*(.text .text.* .literal .literal.*) - *libarch.a:*clk.*(.text .text.* .literal .literal.*) - *libarch.a:*efuse_hal.*(.literal.is_eco0 .text.is_eco0) - *libarch.a:*efuse_utility.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_clk.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_clk_tree.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_clk_tree_common.*(.text .text.* .literal .literal.*) - *libarch.a:*clk_tree_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*rtc_init.*(.text .text.* .literal .literal.*) - *libarch.a:*rtc_clk.*(.text .text.* .literal .literal.*) - *libarch.a:*rtc_clk_init.*(.text .text.* .literal .literal.*) - *libarch.a:*rtc_sleep.*(.text .text.* .literal .literal.*) - *libarch.a:*rtc_time.*(.text .text.* .literal .literal.*) - *libarch.a:*regi2c_ctrl.*(.text .text.* .literal .literal.*) - *libarch.a:*uart_hal_iram.*(.text .text.* .literal .literal.*) - *libarch.a:*wdt_hal_iram.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_banner_wrap.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_init.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_common.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_common_loader.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_console.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_console_loader.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_esp32s3.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_flash.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_flash_config_esp32s3.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_clock_init.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_clock_loader.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_efuse.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_panic.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_mem.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_random.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_random*.*(.literal.bootloader_random_disable .text.bootloader_random_disable) - *libarch.a:*bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) - *libarch.a:*bootloader_random_esp32s3.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_image_format.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_soc.*(.text .text.* .literal .literal.*) - *libarch.a:*bootloader_sha.*(.text .text.* .literal .literal.*) - *libarch.a:*flash_encrypt.*(.text .text.* .literal .literal.*) - *libarch.a:*cache_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*uart_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*mpu_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*mmu_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*efuse_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*uart_periph.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_uart.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_sys.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_spiflash.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_cache_esp32s2_esp32s3.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_wdt.*(.text .text.* .literal .literal.*) - *libarch.a:*log.*(.text .text.* .literal .literal.*) - *libarch.a:*log_lock.*(.literal .literal.* .text .text.*) - *libarch.a:*log_print.*(.literal .literal.* .text .text.*) - *libarch.a:*log_timestamp.*(.literal.esp_log_early_timestamp .text.esp_log_early_timestamp) - *libarch.a:*log_timestamp.*(.literal.esp_log_timestamp .text.esp_log_timestamp) - *libarch.a:*log_timestamp_common.*(.literal .literal.* .text .text.*) - *libarch.a:*log_write.*(.literal.esp_log_write .text.esp_log_write) - *libarch.a:*log_write.*(.literal.esp_log_writev .text.esp_log_writev) - *libarch.a:*cpu_region_protect.*(.text .text.* .literal .literal.*) - *libarch.a:*mspi_timing_tuning.*(.text .text.* .literal .literal.*) - *libarch.a:*mspi_timing_config.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_cache_esp32s2_esp32s3.*(.literal .text .literal.* .text.*) - *libarch.a:*flash_qio_mode.*(.text .text.* .literal .literal.*) - *libarch.a:*spi_flash_wrap.*(.text .text.* .literal .literal.*) - *libarch.a:*spi_flash_oct_flash_init.*(.text .text.* .literal .literal.*) - *libarch.a:*spi_flash_hpm_enable.*(.text .text.* .literal .literal.*) - *libarch.a:esp_spiflash.*(.literal .text .literal.* .text.*) - *libarch.a:esp_flash_api.*(.text .text.* .literal .literal.*) - *libarch.a:esp_flash_spi_init.*(.text .text.* .literal .literal.*) - *libarch.a:spi_flash_hal_iram.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_encrypt_hal_iram.*(.text .text.* .literal .literal.*) - *libarch.a:spi_flash_hal_gpspi.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_chip*.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_wrap.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_os_func_noos.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_os_func_app.*(.literal .literal.* .text .text.*) - *libarch.a:flash_brownout_hook.*(.literal .literal.* .text .text.*) - *libarch.a:esp_cache_utils.*(.literal .literal.* .text .text.*) - *libarch.a:cache_utils.*(.literal .literal.* .text .text.*) - *libarch.a:memspi_host_driver.*(.literal .literal.* .text .text.*) - *libarch.a:critical_section.*(.literal .literal.* .text .text.*) - *libarch.a:os.*(.literal.nuttx_enter_critical .text.nuttx_enter_critical) - *libarch.a:os.*(.literal.nuttx_exit_critical .text.nuttx_exit_critical) - *libarch.a:*sleep_modes.*(.literal.esp_sleep_sub_mode_force_disable* .text.esp_sleep_sub_mode_force_disable*) - *libarch.a:intr_alloc.*(.literal.esp_intr_get_intno .text.esp_intr_get_intno) - *libarch.a:intr_alloc.*(.literal.esp_intr_get_cpu .text.esp_intr_get_cpu) - *libarch.a:interrupt.*(.literal.intr_handler_get .text.intr_handler_get) - *libarch.a:interrupt.*(.literal.intr_handler_get_arg .text.intr_handler_get_arg) - *libarch.a:interrupt.*(.literal.intr_get_item .text.intr_get_item) - *libarch.a:interrupt.*(.literal.intr_handler_get_arg .text.intr_handler_get_arg) + ARCHLIB:*brownout_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*cpu.*(.text .text.* .literal .literal.*) + ARCHLIB:*gpio_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*periph_ctrl.*(.text .text.* .literal .literal.*) + ARCHLIB:*clk.*(.text .text.* .literal .literal.*) + ARCHLIB:*efuse_hal.*(.literal.is_eco0 .text.is_eco0) + ARCHLIB:*efuse_utility.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_clk.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_clk_tree.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_clk_tree_common.*(.text .text.* .literal .literal.*) + ARCHLIB:*clk_tree_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*rtc_init.*(.text .text.* .literal .literal.*) + ARCHLIB:*rtc_clk.*(.text .text.* .literal .literal.*) + ARCHLIB:*rtc_clk_init.*(.text .text.* .literal .literal.*) + ARCHLIB:*rtc_sleep.*(.text .text.* .literal .literal.*) + ARCHLIB:*rtc_time.*(.text .text.* .literal .literal.*) + ARCHLIB:*regi2c_ctrl.*(.text .text.* .literal .literal.*) + ARCHLIB:*uart_hal_iram.*(.text .text.* .literal .literal.*) + ARCHLIB:*wdt_hal_iram.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_banner_wrap.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_init.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_common.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_common_loader.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_console.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_console_loader.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_esp32s3.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_flash.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_flash_config_esp32s3.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_clock_init.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_clock_loader.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_efuse.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_panic.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_mem.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_random.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_random*.*(.literal.bootloader_random_disable .text.bootloader_random_disable) + ARCHLIB:*bootloader_random*.*(.literal.bootloader_random_enable .text.bootloader_random_enable) + ARCHLIB:*bootloader_random_esp32s3.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_image_format.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_soc.*(.text .text.* .literal .literal.*) + ARCHLIB:*bootloader_sha.*(.text .text.* .literal .literal.*) + ARCHLIB:*flash_encrypt.*(.text .text.* .literal .literal.*) + ARCHLIB:*cache_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*uart_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*mpu_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*mmu_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*efuse_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*uart_periph.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_uart.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_sys.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_spiflash.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_cache_esp32s2_esp32s3.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_wdt.*(.text .text.* .literal .literal.*) + ARCHLIB:*log.*(.text .text.* .literal .literal.*) + ARCHLIB:*log_lock.*(.literal .literal.* .text .text.*) + ARCHLIB:*log_print.*(.literal .literal.* .text .text.*) + ARCHLIB:*log_timestamp.*(.literal.esp_log_early_timestamp .text.esp_log_early_timestamp) + ARCHLIB:*log_timestamp.*(.literal.esp_log_timestamp .text.esp_log_timestamp) + ARCHLIB:*log_timestamp_common.*(.literal .literal.* .text .text.*) + ARCHLIB:*log_write.*(.literal.esp_log_write .text.esp_log_write) + ARCHLIB:*log_write.*(.literal.esp_log_writev .text.esp_log_writev) + ARCHLIB:*cpu_region_protect.*(.text .text.* .literal .literal.*) + ARCHLIB:*mspi_timing_tuning.*(.text .text.* .literal .literal.*) + ARCHLIB:*mspi_timing_config.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_cache_esp32s2_esp32s3.*(.literal .text .literal.* .text.*) + ARCHLIB:*flash_qio_mode.*(.text .text.* .literal .literal.*) + ARCHLIB:*spi_flash_wrap.*(.text .text.* .literal .literal.*) + ARCHLIB:*spi_flash_oct_flash_init.*(.text .text.* .literal .literal.*) + ARCHLIB:*spi_flash_hpm_enable.*(.text .text.* .literal .literal.*) + ARCHLIB:esp_spiflash.*(.literal .text .literal.* .text.*) + ARCHLIB:esp_flash_api.*(.text .text.* .literal .literal.*) + ARCHLIB:esp_flash_spi_init.*(.text .text.* .literal .literal.*) + ARCHLIB:spi_flash_hal_iram.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_encrypt_hal_iram.*(.text .text.* .literal .literal.*) + ARCHLIB:spi_flash_hal_gpspi.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_chip*.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_wrap.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_os_func_noos.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_os_func_app.*(.literal .literal.* .text .text.*) + ARCHLIB:flash_brownout_hook.*(.literal .literal.* .text .text.*) + ARCHLIB:esp_cache_utils.*(.literal .literal.* .text .text.*) + ARCHLIB:cache_utils.*(.literal .literal.* .text .text.*) + ARCHLIB:memspi_host_driver.*(.literal .literal.* .text .text.*) + ARCHLIB:critical_section.*(.literal .literal.* .text .text.*) + ARCHLIB:os.*(.literal.nuttx_enter_critical .text.nuttx_enter_critical) + ARCHLIB:os.*(.literal.nuttx_exit_critical .text.nuttx_exit_critical) + ARCHLIB:*sleep_modes.*(.literal.esp_sleep_sub_mode_force_disable* .text.esp_sleep_sub_mode_force_disable*) + ARCHLIB:intr_alloc.*(.literal.esp_intr_get_intno .text.esp_intr_get_intno) + ARCHLIB:intr_alloc.*(.literal.esp_intr_get_cpu .text.esp_intr_get_cpu) + ARCHLIB:interrupt.*(.literal.intr_handler_get .text.intr_handler_get) + ARCHLIB:interrupt.*(.literal.intr_handler_get_arg .text.intr_handler_get_arg) + ARCHLIB:interrupt.*(.literal.intr_get_item .text.intr_get_item) + ARCHLIB:interrupt.*(.literal.intr_handler_get_arg .text.intr_handler_get_arg) *libc.a:*lib_instrument.*(.text .text.* .literal .literal.*) *libc.a:arch_atomic.*(.text .text.* .literal .literal.*) @@ -342,7 +355,25 @@ SECTIONS _iram_text = ABSOLUTE(.); } >iram0_0_seg - /* Marks the end of IRAM code segment */ + #ifdef CONFIG_BUILD_KERNEL + /* The vector table the World Controller gives to WORLD1, the unprivileged + * world. It needs the 1 KB alignment a vector base requires, and it is + * kept in a region of its own so that the permission control can make it + * the only instruction memory a user task may fetch. + * + * It has to land in Internal SRAM1, past 0x40378000: the permission control + * splits SRAM0 into two 16 KB blocks and can say nothing finer, while SRAM1 + * is divided by split lines at 256-byte granularity. Following the IRAM + * code puts it there; esp32s3_isolation_permissions() checks that it did. + */ + + .world1.vectors : ALIGN(1024) + { + KEEP (*(.world1_vectors.text)); + } >iram0_0_seg AT>ROM +#endif + + /* Marks the end of IRAM code segment */ .iram0.text_end (NOLOAD) : { @@ -452,7 +483,7 @@ SECTIONS esp32s3_region.*(.rodata .rodata.*) *libphy.a:(.rodata .rodata.*) - *libarch.a:xtensa_context.*(.rodata .rodata.*) + ARCHLIB:xtensa_context.*(.rodata .rodata.*) #if defined(CONFIG_STACK_CANARIES) && \ (defined(CONFIG_ESP32S3_SPIFLASH) || \ defined(CONFIG_ESP32S3_SPIRAM)) @@ -460,89 +491,89 @@ SECTIONS #endif *libsched.a:*sched_get_stackinfo.*(.rodata .rodata.*) - *libarch.a:*esp_loader.*(.rodata .rodata.*) - *libarch.a:esp32s3_spiflash.*(.rodata .rodata.*) - *libarch.a:esp_spiflash.*(.rodata .rodata.*) - *libarch.a:*efuse_utility.*(.rodata .rodata.*) - *libarch.a:*brownout.*(.rodata .rodata.*) - *libarch.a:*cpu.*(.rodata .rodata.*) - *libarch.a:*gpio_hal.*(.rodata .rodata.*) - *libarch.a:*periph_ctrl.*(.rodata .rodata.*) - *libarch.a:*clk.*(.rodata .rodata.*) - *libarch.a:*esp_clk.*(.rodata .rodata.*) - *libarch.a:*esp_clk_tree.*(.rodata .rodata.*) - *libarch.a:*esp_clk_tree_common.*(.rodata .rodata.*) - *libarch.a:*clk_tree_hal.*(.rodata .rodata.*) - *libarch.a:*rtc_init.*(.rodata .rodata.*) - *libarch.a:*rtc_clk.*(.rodata .rodata.*) - *libarch.a:*rtc_clk_init.*(.rodata .rodata.*) - *libarch.a:*rtc_sleep.*(.rodata .rodata.*) - *libarch.a:*rtc_time.*(.rodata .rodata.*) - *libarch.a:*regi2c_ctrl.*(.rodata .rodata.*) - *libarch.a:*uart_hal_iram.*(.rodata .rodata.*) - *libarch.a:*wdt_hal_iram.*(.rodata .rodata.*) - *libarch.a:*bootloader_banner_wrap.*(.rodata .rodata.*) - *libarch.a:*bootloader_init.*(.rodata .rodata.*) - *libarch.a:*bootloader_common.*(.rodata .rodata.*) - *libarch.a:*bootloader_common_loader.*(.rodata .rodata.*) - *libarch.a:*bootloader_console.*(.rodata .rodata.*) - *libarch.a:*bootloader_console_loader.*(.rodata .rodata.*) - *libarch.a:*bootloader_esp32s3.*(.rodata .rodata.*) - *libarch.a:*bootloader_flash.*(.rodata .rodata.*) - *libarch.a:*bootloader_flash_config_esp32s3.*(.rodata .rodata.*) - *libarch.a:*bootloader_clock_init.*(.rodata .rodata.*) - *libarch.a:*bootloader_clock_loader.*(.rodata .rodata.*) - *libarch.a:*bootloader_efuse.*(.rodata .rodata.*) - *libarch.a:*bootloader_panic.*(.rodata .rodata.*) - *libarch.a:*bootloader_mem.*(.rodata .rodata.*) - *libarch.a:*bootloader_random.*(.rodata .rodata.*) - *libarch.a:*bootloader_random_esp32s3.*(.rodata .rodata.*) - *libarch.a:*esp_image_format.*(.rodata .rodata.*) - *libarch.a:*bootloader_soc.*(.rodata .rodata.*) - *libarch.a:*bootloader_sha.*(.rodata .rodata.*) - *libarch.a:*flash_encrypt.*(.rodata .rodata.*) - *libarch.a:*cache_hal.*(.rodata .rodata.*) - *libarch.a:*uart_hal.*(.rodata .rodata.*) - *libarch.a:*mpu_hal.*(.rodata .rodata.*) - *libarch.a:*mmu_hal.*(.rodata .rodata.*) - *libarch.a:*uart_periph.*(.rodata .rodata.*) - *libarch.a:*esp_rom_uart.*(.rodata .rodata.*) - *libarch.a:*esp_rom_sys.*(.rodata .rodata.*) - *libarch.a:*esp_rom_spiflash.*(.rodata .rodata.*) - *libarch.a:*esp_rom_cache_esp32s2_esp32s3.*(.rodata .rodata.*) - *libarch.a:*esp_rom_wdt.*(.rodata .rodata.*) - *libarch.a:*efuse_hal.*(.rodata .rodata.*) - *libarch.a:*log.*(.rodata .rodata.*) - *libarch.a:*log_noos.*(.rodata .rodata.*) - *libarch.a:*cpu_region_protect.*(.rodata .rodata.*) - *libarch.a:*mspi_timing_tuning.*(.rodata .rodata.*) - *libarch.a:*mspi_timing_config.*(.rodata .rodata.*) + ARCHLIB:*esp_loader.*(.rodata .rodata.*) + ARCHLIB:esp32s3_spiflash.*(.rodata .rodata.*) + ARCHLIB:esp_spiflash.*(.rodata .rodata.*) + ARCHLIB:*efuse_utility.*(.rodata .rodata.*) + ARCHLIB:*brownout.*(.rodata .rodata.*) + ARCHLIB:*cpu.*(.rodata .rodata.*) + ARCHLIB:*gpio_hal.*(.rodata .rodata.*) + ARCHLIB:*periph_ctrl.*(.rodata .rodata.*) + ARCHLIB:*clk.*(.rodata .rodata.*) + ARCHLIB:*esp_clk.*(.rodata .rodata.*) + ARCHLIB:*esp_clk_tree.*(.rodata .rodata.*) + ARCHLIB:*esp_clk_tree_common.*(.rodata .rodata.*) + ARCHLIB:*clk_tree_hal.*(.rodata .rodata.*) + ARCHLIB:*rtc_init.*(.rodata .rodata.*) + ARCHLIB:*rtc_clk.*(.rodata .rodata.*) + ARCHLIB:*rtc_clk_init.*(.rodata .rodata.*) + ARCHLIB:*rtc_sleep.*(.rodata .rodata.*) + ARCHLIB:*rtc_time.*(.rodata .rodata.*) + ARCHLIB:*regi2c_ctrl.*(.rodata .rodata.*) + ARCHLIB:*uart_hal_iram.*(.rodata .rodata.*) + ARCHLIB:*wdt_hal_iram.*(.rodata .rodata.*) + ARCHLIB:*bootloader_banner_wrap.*(.rodata .rodata.*) + ARCHLIB:*bootloader_init.*(.rodata .rodata.*) + ARCHLIB:*bootloader_common.*(.rodata .rodata.*) + ARCHLIB:*bootloader_common_loader.*(.rodata .rodata.*) + ARCHLIB:*bootloader_console.*(.rodata .rodata.*) + ARCHLIB:*bootloader_console_loader.*(.rodata .rodata.*) + ARCHLIB:*bootloader_esp32s3.*(.rodata .rodata.*) + ARCHLIB:*bootloader_flash.*(.rodata .rodata.*) + ARCHLIB:*bootloader_flash_config_esp32s3.*(.rodata .rodata.*) + ARCHLIB:*bootloader_clock_init.*(.rodata .rodata.*) + ARCHLIB:*bootloader_clock_loader.*(.rodata .rodata.*) + ARCHLIB:*bootloader_efuse.*(.rodata .rodata.*) + ARCHLIB:*bootloader_panic.*(.rodata .rodata.*) + ARCHLIB:*bootloader_mem.*(.rodata .rodata.*) + ARCHLIB:*bootloader_random.*(.rodata .rodata.*) + ARCHLIB:*bootloader_random_esp32s3.*(.rodata .rodata.*) + ARCHLIB:*esp_image_format.*(.rodata .rodata.*) + ARCHLIB:*bootloader_soc.*(.rodata .rodata.*) + ARCHLIB:*bootloader_sha.*(.rodata .rodata.*) + ARCHLIB:*flash_encrypt.*(.rodata .rodata.*) + ARCHLIB:*cache_hal.*(.rodata .rodata.*) + ARCHLIB:*uart_hal.*(.rodata .rodata.*) + ARCHLIB:*mpu_hal.*(.rodata .rodata.*) + ARCHLIB:*mmu_hal.*(.rodata .rodata.*) + ARCHLIB:*uart_periph.*(.rodata .rodata.*) + ARCHLIB:*esp_rom_uart.*(.rodata .rodata.*) + ARCHLIB:*esp_rom_sys.*(.rodata .rodata.*) + ARCHLIB:*esp_rom_spiflash.*(.rodata .rodata.*) + ARCHLIB:*esp_rom_cache_esp32s2_esp32s3.*(.rodata .rodata.*) + ARCHLIB:*esp_rom_wdt.*(.rodata .rodata.*) + ARCHLIB:*efuse_hal.*(.rodata .rodata.*) + ARCHLIB:*log.*(.rodata .rodata.*) + ARCHLIB:*log_noos.*(.rodata .rodata.*) + ARCHLIB:*cpu_region_protect.*(.rodata .rodata.*) + ARCHLIB:*mspi_timing_tuning.*(.rodata .rodata.*) + ARCHLIB:*mspi_timing_config.*(.rodata .rodata.*) #ifdef CONFIG_ESP32S3_SPIRAM_MODE_QUAD - *libarch.a:esp32s3_psram_quad.*(.rodata .rodata.*) + ARCHLIB:esp32s3_psram_quad.*(.rodata .rodata.*) #endif #ifdef CONFIG_ESP32S3_SPIRAM_MODE_OCT - *libarch.a:esp32s3_psram_octal.*(.rodata .rodata.*) + ARCHLIB:esp32s3_psram_octal.*(.rodata .rodata.*) #endif - *libarch.a:*flash_qio_mode.*(.rodata .rodata.*) - *libarch.a:*spi_flash_wrap.*(.rodata .rodata.*) - *libarch.a:*spi_flash_oct_flash_init.*(.rodata .rodata.*) - *libarch.a:*spi_flash_hpm_enable.*(.rodata .rodata.*) - *libarch.a:esp_flash_api.*(.rodata .rodata.*) - *libarch.a:esp_flash_spi_init.*(.rodata .rodata.*) - *libarch.a:spi_flash_hal_iram.*(.rodata .rodata.*) - *libarch.a:spi_flash_encrypt_hal_iram.*(.rodata .rodata.*) - *libarch.a:spi_flash_hal_gpspi.*(.rodata .rodata.*) - *libarch.a:spi_flash_chip*.*(.rodata .rodata.*) - *libarch.a:spi_flash_wrap.*(.rodata .rodata.*) - *libarch.a:spi_flash_os_func_noos.*(.rodata .rodata.*) - *libarch.a:spi_flash_os_func_app.*(.rodata .rodata.*) - *libarch.a:flash_brownout_hook.*(.rodata .rodata.*) - *libarch.a:esp_cache_utils.*(.rodata .rodata.*) - *libarch.a:cache_utils.*(.rodata .rodata.*) - *libarch.a:memspi_host_driver.*(.rodata .rodata.*) - *libarch.a:critical_section.*(.rodata .rodata.*) - *libarch.a:os.*(.rodata.g_int_flags_count .rodata.g_int_flags) - *libarch.a:*sleep_modes.*(.rodata.esp_sleep_sub_mode_force_disable*) + ARCHLIB:*flash_qio_mode.*(.rodata .rodata.*) + ARCHLIB:*spi_flash_wrap.*(.rodata .rodata.*) + ARCHLIB:*spi_flash_oct_flash_init.*(.rodata .rodata.*) + ARCHLIB:*spi_flash_hpm_enable.*(.rodata .rodata.*) + ARCHLIB:esp_flash_api.*(.rodata .rodata.*) + ARCHLIB:esp_flash_spi_init.*(.rodata .rodata.*) + ARCHLIB:spi_flash_hal_iram.*(.rodata .rodata.*) + ARCHLIB:spi_flash_encrypt_hal_iram.*(.rodata .rodata.*) + ARCHLIB:spi_flash_hal_gpspi.*(.rodata .rodata.*) + ARCHLIB:spi_flash_chip*.*(.rodata .rodata.*) + ARCHLIB:spi_flash_wrap.*(.rodata .rodata.*) + ARCHLIB:spi_flash_os_func_noos.*(.rodata .rodata.*) + ARCHLIB:spi_flash_os_func_app.*(.rodata .rodata.*) + ARCHLIB:flash_brownout_hook.*(.rodata .rodata.*) + ARCHLIB:esp_cache_utils.*(.rodata .rodata.*) + ARCHLIB:cache_utils.*(.rodata .rodata.*) + ARCHLIB:memspi_host_driver.*(.rodata .rodata.*) + ARCHLIB:critical_section.*(.rodata .rodata.*) + ARCHLIB:os.*(.rodata.g_int_flags_count .rodata.g_int_flags) + ARCHLIB:*sleep_modes.*(.rodata.esp_sleep_sub_mode_force_disable*) *libc.a:arch_atomic.*(.rodata .rodata.*) @@ -655,15 +686,15 @@ SECTIONS _srodata = ABSOLUTE(.); *(EXCLUDE_FILE (esp32s3_start.* esp32s3_region.* - *libarch.a:*esp_loader.* - *libarch.a:esp32s3_spiflash.* - *libarch.a:*cache_hal.* *libarch.a:*mmu_hal.* - *libarch.a:*mpu_hal.*) .rodata) + ARCHLIB:*esp_loader.* + ARCHLIB:esp32s3_spiflash.* + ARCHLIB:*cache_hal.* ARCHLIB:*mmu_hal.* + ARCHLIB:*mpu_hal.*) .rodata) *(EXCLUDE_FILE (esp32s3_start.* esp32s3_region.* - *libarch.a:*esp_loader.* - *libarch.a:esp32s3_spiflash.* - *libarch.a:*cache_hal.* *libarch.a:*mmu_hal.* - *libarch.a:*mpu_hal.*) .rodata.*) + ARCHLIB:*esp_loader.* + ARCHLIB:esp32s3_spiflash.* + ARCHLIB:*cache_hal.* ARCHLIB:*mmu_hal.* + ARCHLIB:*mpu_hal.*) .rodata.*) #ifdef CONFIG_ESPRESSIF_WIRELESS *(.rodata_wlog_verbose.*) diff --git a/boards/xtensa/esp32s3/common/scripts/kernel-space.ld b/boards/xtensa/esp32s3/common/scripts/kernel-space.ld index 942d3d4753fbf..1e33d5df35b0c 100644 --- a/boards/xtensa/esp32s3/common/scripts/kernel-space.ld +++ b/boards/xtensa/esp32s3/common/scripts/kernel-space.ld @@ -190,6 +190,7 @@ SECTIONS *libkarch.a:*uart_hal.*(.text .text.* .literal .literal.*) *libkarch.a:*mpu_hal.*(.text .text.* .literal .literal.*) *libkarch.a:*mmu_hal.*(.text .text.* .literal .literal.*) + *libkarch.a:*esp_mmu_map.*(.text .text.* .literal .literal.*) *libkarch.a:*efuse_hal.*(.text .text.* .literal .literal.*) *libkarch.a:*uart_periph.*(.text .text.* .literal .literal.*) *libkarch.a:*esp_rom_uart.*(.text .text.* .literal .literal.*) @@ -217,6 +218,7 @@ SECTIONS *libkarch.a:*mspi_timing_tuning.*(.text .text.* .literal .literal.*) *libkarch.a:*mspi_timing_config.*(.text .text.* .literal .literal.*) + *libkarch.a:esp32s3_spi_timing.*(.text .text.* .literal .literal.*) *libkarch.a:*spi_flash_oct_flash_init.*(.text .text.* .literal .literal.*) *libkarch.a:*spi_flash_hpm_enable.*(.text .text.* .literal .literal.*) #endif diff --git a/boards/xtensa/esp32s3/common/scripts/legacy_sections.ld b/boards/xtensa/esp32s3/common/scripts/legacy_sections.ld index cdff5755c48c5..26d4a0890a7bd 100644 --- a/boards/xtensa/esp32s3/common/scripts/legacy_sections.ld +++ b/boards/xtensa/esp32s3/common/scripts/legacy_sections.ld @@ -22,6 +22,19 @@ #include +/* A kernel build archives the architecture objects into libkarch.a rather + * than libarch.a, so the placement rules below have to name the right one. + * Getting this wrong is silent and fatal: the octal-flash bring-up functions + * stay in mapped flash and are called while that mapping is being + * reconfigured. + */ + +#ifdef CONFIG_BUILD_KERNEL +# define ARCHLIB *libkarch.a +#else +# define ARCHLIB *libarch.a +#endif + /* Default entry point: */ ENTRY(__start); @@ -78,19 +91,19 @@ SECTIONS *(.iram1 .iram1.*) - *libarch.a:esp32s3_cpuindex.*(.literal .text .literal.* .text.*) - *libarch.a:esp32s3_user.*(.literal .text .literal.* .text.*) - *libarch.a:esp32s3_spiflash.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_assert.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_cpuint.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_cpupause.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_irqdispatch.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_modifyreg32.*(.literal .text .literal.* .text.*) - *libarch.a:xtensa_testset.*(.literal .text .literal.* .text.*) - *libarch.a:esp_app_desc.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_cpuindex.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_user.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_spiflash.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_assert.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_cpuint.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_cpupause.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_irqdispatch.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_modifyreg32.*(.literal .text .literal.* .text.*) + ARCHLIB:xtensa_testset.*(.literal .text .literal.* .text.*) + ARCHLIB:esp_app_desc.*(.literal .text .literal.* .text.*) - *libarch.a:*esp_rom_spiflash.*(.literal .text .literal.* .text.*) - *libarch.a:*esp_rom_cache_esp32s2_esp32s3.*(.literal .text .literal.* .text.*) + ARCHLIB:*esp_rom_spiflash.*(.literal .text .literal.* .text.*) + ARCHLIB:*esp_rom_cache_esp32s2_esp32s3.*(.literal .text .literal.* .text.*) *libc.a:sq_remlast.*(.literal .text .literal.* .text.*) @@ -112,11 +125,11 @@ SECTIONS *libc.a:arch_atomic.*(.literal .text .literal.* .text.*) #ifdef CONFIG_ESP32S3_SPEED_UP_ISR - *libarch.a:xtensa_switchcontext.*(.literal.up_switch_context .text.up_switch_context) + ARCHLIB:xtensa_switchcontext.*(.literal.up_switch_context .text.up_switch_context) - *libarch.a:esp32s3_timerisr.*(.literal.systimer_isr .text.systimer_isr) - *libarch.a:esp32s3_idle.*(.literal.up_idle .text.up_idle) - *libarch.a:esp32s3_dma.*(.literal.esp32s3_dma_load .text.esp32s3_dma_load \ + ARCHLIB:esp32s3_timerisr.*(.literal.systimer_isr .text.systimer_isr) + ARCHLIB:esp32s3_idle.*(.literal.up_idle .text.up_idle) + ARCHLIB:esp32s3_dma.*(.literal.esp32s3_dma_load .text.esp32s3_dma_load \ .literal.esp32s3_dma_enable .text.esp32s3_dma_enable) *libsched.a:sched_processtimer.*(.literal.nxsched_process_timer .text.nxsched_process_timer) @@ -132,33 +145,33 @@ SECTIONS *libc.a:sq_remfirst.*(.literal.sq_remfirst .text.sq_remfirst) #endif - *libarch.a:esp32s3_spi_timing.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_spi_timing.*(.literal .text .literal.* .text.*) #ifdef CONFIG_ESP32S3_SPIRAM_MODE_QUAD - *libarch.a:esp32s3_psram_quad.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_psram_quad.*(.literal .text .literal.* .text.*) #endif #ifdef CONFIG_ESP32S3_SPIRAM_MODE_OCT - *libarch.a:esp32s3_psram_octal.*(.literal .text .literal.* .text.*) + ARCHLIB:esp32s3_psram_octal.*(.literal .text .literal.* .text.*) #endif #if defined(CONFIG_STACK_CANARIES) && \ (defined(CONFIG_ESP32S3_SPIFLASH) || \ defined(CONFIG_ESP32S3_SPIRAM)) *libc.a:lib_stackchk.*(.literal .text .literal.* .text.*) #endif - *libarch.a:*cache_hal.*(.text .text.* .literal .literal.*) - *libarch.a:*esp_rom_cache_esp32s2_esp32s3.*(.literal .text .literal.* .text.*) - *libarch.a:esp_cache.*(.literal .literal.* .text .text.*) - *libarch.a:cache_utils.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_hal_iram.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_hal_gpspi.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_encrypt_hal_iram.*(.text .text.* .literal .literal.*) - *libarch.a:spi_flash_chip*.*(.literal .literal.* .text .text.*) - *libarch.a:spi_flash_wrap.*(.literal .literal.* .text .text.*) - *libarch.a:esp_spiflash.*(.literal .text .literal.* .text.*) - *libarch.a:esp_flash_spi_init.*(.text .text.* .literal .literal.*) - *libarch.a:esp_flash_api.*(.text .text.* .literal .literal.*) - *libarch.a:spi_flash_os_func*.*(.literal .literal.* .text .text.*) - *libarch.a:*flash_brownout_hook.*(.literal .literal.* .text .text.*) - *libarch.a:memspi_host_driver.*(.literal .literal.* .text .text.*) + ARCHLIB:*cache_hal.*(.text .text.* .literal .literal.*) + ARCHLIB:*esp_rom_cache_esp32s2_esp32s3.*(.literal .text .literal.* .text.*) + ARCHLIB:esp_cache.*(.literal .literal.* .text .text.*) + ARCHLIB:cache_utils.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_hal_iram.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_hal_gpspi.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_encrypt_hal_iram.*(.text .text.* .literal .literal.*) + ARCHLIB:spi_flash_chip*.*(.literal .literal.* .text .text.*) + ARCHLIB:spi_flash_wrap.*(.literal .literal.* .text .text.*) + ARCHLIB:esp_spiflash.*(.literal .text .literal.* .text.*) + ARCHLIB:esp_flash_spi_init.*(.text .text.* .literal .literal.*) + ARCHLIB:esp_flash_api.*(.text .text.* .literal .literal.*) + ARCHLIB:spi_flash_os_func*.*(.literal .literal.* .text .text.*) + ARCHLIB:*flash_brownout_hook.*(.literal .literal.* .text .text.*) + ARCHLIB:memspi_host_driver.*(.literal .literal.* .text .text.*) *(.wifirxiram .wifirxiram.*) *(.wifi0iram .wifi0iram.*) @@ -242,27 +255,27 @@ SECTIONS *(.dram1 .dram1.*) *libphy.a:(.rodata .rodata.*) - *libarch.a:xtensa_context.*(.rodata .rodata.*) - *libarch.a:*cache_hal.*(.rodata .rodata.*) + ARCHLIB:xtensa_context.*(.rodata .rodata.*) + ARCHLIB:*cache_hal.*(.rodata .rodata.*) #if defined(CONFIG_STACK_CANARIES) && \ (defined(CONFIG_ESP32S3_SPIFLASH) || \ defined(CONFIG_ESP32S3_SPIRAM)) *libc.a:lib_stackchk.*(.rodata .rodata.*) #endif - *libarch.a:esp_cache.*(.rodata .rodata.*) - *libarch.a:cache_utils.*(.rodata .rodata.*) - *libarch.a:spi_flash_hal_iram.*(.rodata .rodata.*) - *libarch.a:spi_flash_hal_gpspi.*(.rodata .rodata.*) - *libarch.a:spi_flash_encrypt_hal_iram.*(.rodata .rodata.*) - *libarch.a:spi_flash_chip*.*(.rodata .rodata.*) - *libarch.a:spi_flash_wrap.*(.rodata .rodata.*) - *libarch.a:esp_spiflash.*(.rodata .rodata.*) - *libarch.a:esp_flash_spi_init.*(.rodata .rodata.*) - *libarch.a:esp_flash_api.*(.rodata .rodata.*) - *libarch.a:spi_flash_os_func*.*(.rodata .rodata.*) - *libarch.a:flash_brownout_hook.*(.rodata .rodata.*) - *libarch.a:memspi_host_driver.*(.rodata .rodata.*) + ARCHLIB:esp_cache.*(.rodata .rodata.*) + ARCHLIB:cache_utils.*(.rodata .rodata.*) + ARCHLIB:spi_flash_hal_iram.*(.rodata .rodata.*) + ARCHLIB:spi_flash_hal_gpspi.*(.rodata .rodata.*) + ARCHLIB:spi_flash_encrypt_hal_iram.*(.rodata .rodata.*) + ARCHLIB:spi_flash_chip*.*(.rodata .rodata.*) + ARCHLIB:spi_flash_wrap.*(.rodata .rodata.*) + ARCHLIB:esp_spiflash.*(.rodata .rodata.*) + ARCHLIB:esp_flash_spi_init.*(.rodata .rodata.*) + ARCHLIB:esp_flash_api.*(.rodata .rodata.*) + ARCHLIB:spi_flash_os_func*.*(.rodata .rodata.*) + ARCHLIB:flash_brownout_hook.*(.rodata .rodata.*) + ARCHLIB:memspi_host_driver.*(.rodata .rodata.*) *libc.a:arch_atomic.*(.rodata .rodata.*) _edata = ABSOLUTE(.); . = ALIGN(4); diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/.gitignore b/boards/xtensa/esp32s3/esp32s3-devkit/.gitignore new file mode 100644 index 0000000000000..2f370e942d567 --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/.gitignore @@ -0,0 +1 @@ +src/romfs_boot.c diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/kernel_n8r2/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/kernel_n8r2/defconfig new file mode 100644 index 0000000000000..881e0d84a6cdc --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/kernel_n8r2/defconfig @@ -0,0 +1,100 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="xtensa" +CONFIG_ARCH_ADDRENV=y +CONFIG_ARCH_BOARD="esp32s3-devkit" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32S3_DEVKIT=y +CONFIG_ARCH_CHIP="esp32s3" +CONFIG_ARCH_CHIP_ESP32S3=y +CONFIG_ARCH_CHIP_ESP32S3WROOM1N8R2=y +CONFIG_ARCH_DATA_NPAGES=2 +CONFIG_ARCH_DATA_VBASE=0x3d000000 +CONFIG_ARCH_HEAP_NPAGES=2 +CONFIG_ARCH_HEAP_VBASE=0x3d200000 +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_IRQ_TO_NDX=y +CONFIG_ARCH_KERNEL_STACKSIZE=8192 +CONFIG_ARCH_KMAP_NPAGES=2 +CONFIG_ARCH_KMAP_VBASE=0x3d400000 +CONFIG_ARCH_MINIMAL_VECTORTABLE_DYNAMIC=y +CONFIG_ARCH_NUSER_INTERRUPTS=2 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_TEXT_NPAGES=2 +CONFIG_ARCH_TEXT_VBASE=0x42c00000 +CONFIG_ARCH_USE_MMU=y +CONFIG_ARCH_XTENSA=y +CONFIG_BINFMT_ELF_EXECUTABLE=y +CONFIG_BOARD_INITTHREAD_STACKSIZE=2048 +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILD_KERNEL=y +CONFIG_DEBUG_BINFMT=y +CONFIG_DEBUG_BINFMT_ERROR=y +CONFIG_DEBUG_BINFMT_INFO=y +CONFIG_DEBUG_BINFMT_WARN=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SCHED=y +CONFIG_DEBUG_SCHED_ERROR=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_TASK_STACKSIZE=8192 +CONFIG_ELF=y +CONFIG_ESP32S3_PAGEFAULT=y +CONFIG_ARCH_PGPOOL_PBASE=0x80000 +CONFIG_ARCH_PGPOOL_SIZE=1572864 +CONFIG_ESP32S3_PSRAM_8M=y +CONFIG_ESP32S3_SPIFLASH=y +CONFIG_ESP32S3_SPIRAM=y +CONFIG_ESP32S3_UART0=y +CONFIG_ESP32S3_WCL=y +CONFIG_FS_PROCFS=y +CONFIG_FS_ROMFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_HOST_MACOS=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INIT_FILEPATH="/system/bin/init" +CONFIG_INIT_MOUNT=y +CONFIG_INIT_MOUNT_FLAGS=0x1 +CONFIG_INIT_MOUNT_TARGET="/system/bin" +CONFIG_INTELHEX_BINARY=y +CONFIG_IRQ_WORK_STACKSIZE=2048 +CONFIG_LIBC_ENVPATH=y +CONFIG_LIBC_EXECFUNCS=y +CONFIG_LINE_MAX=64 +CONFIG_MM_PGALLOC=y +CONFIG_MM_PGSIZE=65536 +CONFIG_NSH_DISABLE_LOSMART=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_FILE_APPS=y +CONFIG_NSH_READLINE=y +CONFIG_PATH_INITIAL="/system/bin" +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PTHREAD_STACK_DEFAULT=2048 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HAVE_PARENT=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_LPWORKSTACKSIZE=2048 +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYSLOG_BUFFER=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_NSH_PROGNAME="init" +CONFIG_TESTING_GETPRIME=y +CONFIG_TESTING_OSTEST=y +CONFIG_TESTING_OSTEST_FPUTESTDISABLE=y +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/kernel_oct/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/kernel_oct/defconfig new file mode 100644 index 0000000000000..5e11bbe7726e4 --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/kernel_oct/defconfig @@ -0,0 +1,102 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="xtensa" +CONFIG_ARCH_ADDRENV=y +CONFIG_ARCH_BOARD="esp32s3-devkit" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32S3_DEVKIT=y +CONFIG_ARCH_CHIP="esp32s3" +CONFIG_ARCH_CHIP_ESP32S3=y +CONFIG_ARCH_CHIP_ESP32S3WROOM2N32R8V=y +CONFIG_ARCH_DATA_NPAGES=8 +CONFIG_ARCH_DATA_VBASE=0x3d000000 +CONFIG_ARCH_HEAP_NPAGES=8 +CONFIG_ARCH_HEAP_VBASE=0x3d200000 +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_IRQ_TO_NDX=y +CONFIG_ARCH_KERNEL_STACKSIZE=8192 +CONFIG_ARCH_KMAP_NPAGES=2 +CONFIG_ARCH_KMAP_VBASE=0x3d400000 +CONFIG_ARCH_MINIMAL_VECTORTABLE_DYNAMIC=y +CONFIG_ARCH_NUSER_INTERRUPTS=2 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_TEXT_NPAGES=8 +CONFIG_ARCH_TEXT_VBASE=0x42c00000 +CONFIG_ARCH_USE_MMU=y +CONFIG_ARCH_XTENSA=y +CONFIG_BINFMT_ELF_EXECUTABLE=y +CONFIG_BOARD_INITTHREAD_STACKSIZE=2048 +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILD_KERNEL=y +CONFIG_DEBUG_BINFMT=y +CONFIG_DEBUG_BINFMT_ERROR=y +CONFIG_DEBUG_BINFMT_INFO=y +CONFIG_DEBUG_BINFMT_WARN=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SCHED=y +CONFIG_DEBUG_SCHED_ERROR=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEFAULT_TASK_STACKSIZE=8192 +CONFIG_ELF=y +CONFIG_ESP32S3_FLASH_MODE_OCT=y +CONFIG_ESP32S3_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESP32S3_PAGEFAULT=y +CONFIG_ESP32S3_SPIFLASH=y +CONFIG_ARCH_PGPOOL_PBASE=0x350000 +CONFIG_ARCH_PGPOOL_SIZE=4194304 +CONFIG_ESP32S3_SPIRAM=y +CONFIG_ESP32S3_SPIRAM_MODE_OCT=y +CONFIG_ESP32S3_UART0=y +CONFIG_ESP32S3_WCL=y +CONFIG_FS_PROCFS=y +CONFIG_FS_ROMFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_HOST_MACOS=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INIT_FILEPATH="/system/bin/init" +CONFIG_INIT_MOUNT=y +CONFIG_INIT_MOUNT_FLAGS=0x1 +CONFIG_INIT_MOUNT_TARGET="/system/bin" +CONFIG_INTELHEX_BINARY=y +CONFIG_IRQ_WORK_STACKSIZE=2048 +CONFIG_LIBC_ENVPATH=y +CONFIG_LIBC_EXECFUNCS=y +CONFIG_LINE_MAX=64 +CONFIG_MM_PGALLOC=y +CONFIG_MM_PGSIZE=65536 +CONFIG_NSH_DISABLE_LOSMART=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_FILE_APPS=y +CONFIG_NSH_READLINE=y +CONFIG_PATH_INITIAL="/system/bin" +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PTHREAD_STACK_DEFAULT=2048 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HAVE_PARENT=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_LPWORKSTACKSIZE=2048 +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYSLOG_BUFFER=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_NSH_PROGNAME="init" +CONFIG_TESTING_GETPRIME=y +CONFIG_TESTING_OSTEST=y +CONFIG_TESTING_OSTEST_FPUTESTDISABLE=y +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/scripts/gnu-elf.ld b/boards/xtensa/esp32s3/esp32s3-devkit/scripts/gnu-elf.ld new file mode 100644 index 0000000000000..68d481c7c571d --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/scripts/gnu-elf.ld @@ -0,0 +1,166 @@ +/**************************************************************************** + * boards/xtensa/esp32s3/esp32s3-devkit/scripts/gnu-elf.ld + * + * 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. + * + ****************************************************************************/ + +/* Link script for the user programs of a kernel build. A fully linked + * executable is loaded at the addresses it was linked for -- binfmt takes + * the entry point straight out of the ELF header -- so these must match the + * address environment the ESP32-S3 gives a user process: + * + * .text goes to CONFIG_ARCH_TEXT_VBASE, a slot of the instruction-bus + * cache-MMU window (0x42000000-0x44000000). + * .data goes to CONFIG_ARCH_DATA_VBASE plus one page: up_addrenv_create() + * keeps the first page of the data window for the OS reserve (the + * heap bookkeeping and the signal trampoline pointer). The window + * is a slot of the data-bus range (0x3C000000-0x3E000000). + * + * Both windows have to sit above the kernel's own PSRAM window, which the + * two buses share entry for entry: 0x42C00000 and 0x3C000000+0xC00000 are + * the same cache-MMU entry, so a user window that overlapped it would take + * away the kernel's view of the pages underneath. The kernel window starts + * wherever the flash mappings end, so it moves as the image grows; + * up_allocate_pgheap() checks the two against each other at boot. + * + * .rodata sits with the data, not with the text: the ESP32-S3 selects + * CONFIG_ARCH_HAVE_TEXT_HEAP_WORD_ALIGNED_READ, so the loader places every + * section that is not executable into the data region, and unaligned reads + * through the instruction bus are not reliable anyway. + * + * The loader packs allocatable sections back to back in section-header + * order, honouring only each section's own alignment, so the layout below + * must not introduce gaps of its own. + * + * Keep these addresses in step with CONFIG_ARCH_TEXT_VBASE, + * CONFIG_ARCH_DATA_VBASE and CONFIG_MM_PGSIZE in the board defconfig. + */ + +SECTIONS +{ + . = 0x42c00000; + + .text : + { + _stext = . ; + *(.literal .text .literal.* .text.*) + *(.gnu.warning) + *(.stub) + *(.jcr) + + /* C++ support: The .init and .fini sections contain specific logic + * to manage static constructors and destructors. + */ + + *(.gnu.linkonce.t.*) + *(.init) + *(.fini) + . = ALIGN(4); + _etext = . ; + } + + . = 0x3d010000; + + .rodata : + { + _srodata = . ; + *(.rodata) + *(.rodata1) + *(.rodata.*) + *(.gnu.linkonce.r*) + . = ALIGN(4); + _erodata = . ; + } + + /* Placed explicitly, and here, because the loader assigns it to the data + * region -- it is allocatable but not executable -- and packs it directly + * after .rodata. + */ + + .eh_frame : + { + KEEP (*(.eh_frame)) + *(.eh_frame.*) + } + + .data : + { + _sdata = . ; + *(.data) + *(.data1) + *(.data.*) + *(.gnu.linkonce.d*) + . = ALIGN(4); + _edata = . ; + } + + .init_array : + { + _sinit = .; + _sctors = .; + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP(*(.init_array .ctors)) + . = ALIGN(4); + _einit = .; + _ectors = .; + } + + .fini_array : + { + _sfini = .; + _sdtors = .; + KEEP (*(.dtors)) + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + . = ALIGN(4); + _efini = .; + _edtors = .; + } + + .bss : + { + _sbss = . ; + *(.bss) + *(.bss.*) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.b*) + *(COMMON) + . = ALIGN(4); + _ebss = . ; + } + + .got : + { + *(.got*) + } + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/.gitignore b/boards/xtensa/esp32s3/esp32s3-devkit/src/.gitignore new file mode 100644 index 0000000000000..a33bdfee20028 --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/.gitignore @@ -0,0 +1 @@ +romfs_boot.c diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/Make.defs b/boards/xtensa/esp32s3/esp32s3-devkit/src/Make.defs index 496bb5d4323ae..6b51a1941245e 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/src/Make.defs +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/Make.defs @@ -22,6 +22,33 @@ CSRCS = esp32s3_boot.c esp32s3_bringup.c +# A kernel build boots its init process out of a ROMFS image built from +# apps/bin by apps/tools/mkromfsimg.sh. Until that has been generated, +# romfs_stub.c supplies an empty placeholder so the kernel still builds. +# +# The placeholder is always compiled, and defines the image away once the +# real one exists, rather than being swapped out for it: both define +# romfs_img, so an object left behind by an earlier build would otherwise +# still satisfy the link and quietly produce a kernel with no init process. + +ifeq ($(CONFIG_BUILD_KERNEL),y) +CSRCS += romfs_stub.c +ifneq ($(wildcard $(BOARD_DIR)$(DELIM)src$(DELIM)romfs_boot.c),) +CSRCS += romfs_boot.c +CFLAGS += -DHAVE_ROMFS_BOOT + +# Defining the placeholder away is a change in CFLAGS, and make cannot see +# one: an object compiled before the image was generated is newer than its +# source and is not rebuilt. It still defines romfs_img, and being the +# earlier member of libboard.a it is the one that satisfies the link -- so +# the kernel boots with a one-byte ROMFS and cannot start init, in a build +# where nothing failed. Tie the placeholder to the generated image so that +# it is recompiled whenever the image is regenerated. + +romfs_stub$(OBJEXT): $(BOARD_DIR)$(DELIM)src$(DELIM)romfs_boot.c +endif +endif + ifeq ($(CONFIG_BOARDCTL_RESET),y) CSRCS += esp32s3_reset.c endif diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c index 7be79bcc86f8c..c3234c6b8d423 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c @@ -163,8 +163,22 @@ # endif #endif +#ifdef CONFIG_BUILD_KERNEL +# include +# include "romfs.h" +#endif + #include "esp32s3-devkit.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +# define SECTORSIZE 512 +# define NSECTORS(b) (((b) + SECTORSIZE - 1) / SECTORSIZE) +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -189,6 +203,31 @@ int esp32s3_bringup(void) bool i2s_enable_rx; #endif +#ifdef CONFIG_BUILD_KERNEL + /* A kernel build has no built-in applications: the init process and every + * other user program is a separate ELF living in the boot ROMFS image. + * Publish it as a RAM disk here so that nx_start_application() can mount + * it at CONFIG_INIT_MOUNT_TARGET and exec CONFIG_INIT_FILEPATH from it. + * An unpopulated image means romfs_boot.c was never generated -- see + * apps/tools/mkromfsimg.sh -- so there is nothing to register. + */ + + if (NSECTORS(romfs_img_len) > 1) + { + ret = romdisk_register(0, romfs_img, NSECTORS(romfs_img_len), + SECTORSIZE); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to register boot ROMFS: %d\n", ret); + } + } + else + { + syslog(LOG_ERR, "ERROR: Boot ROMFS image is empty; the init process " + "cannot be started\n"); + } +#endif + #if defined(CONFIG_ESP32S3_SPIRAM) && \ defined(CONFIG_ESP32S3_SPIRAM_BANKSWITCH_ENABLE) ret = esp_himem_init(); diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/romfs.h b/boards/xtensa/esp32s3/esp32s3-devkit/src/romfs.h new file mode 100644 index 0000000000000..658fad3143862 --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/romfs.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * boards/xtensa/esp32s3/esp32s3-devkit/src/romfs.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 __BOARDS_XTENSA_ESP32S3_ESP32S3_DEVKIT_SRC_ROMFS_H +#define __BOARDS_XTENSA_ESP32S3_ESP32S3_DEVKIT_SRC_ROMFS_H + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* The boot ROMFS image holding the user-space programs of a kernel build. + * The real image is generated from apps/bin by apps/tools/mkromfsimg.sh into + * romfs_boot.c; romfs_stub.c provides a weak, empty placeholder so that the + * kernel still links before that step has been run. + */ + +extern const unsigned char romfs_img[]; +extern const unsigned int romfs_img_len; + +#endif /* __BOARDS_XTENSA_ESP32S3_ESP32S3_DEVKIT_SRC_ROMFS_H */ diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/romfs_stub.c b/boards/xtensa/esp32s3/esp32s3-devkit/src/romfs_stub.c new file mode 100644 index 0000000000000..f01bd6f1c383d --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/romfs_stub.c @@ -0,0 +1,56 @@ +/**************************************************************************** + * boards/xtensa/esp32s3/esp32s3-devkit/src/romfs_stub.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 + +#ifndef HAVE_ROMFS_BOOT + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Placeholder for the boot ROMFS image of a kernel build. It lets the + * kernel link before apps/tools/mkromfsimg.sh has generated the real + * romfs_boot.c. A kernel built against this placeholder has no user-space + * programs and so cannot start its init process; esp32s3_bringup() says so + * on the console. + * + * HAVE_ROMFS_BOOT is defined by src/Make.defs once the generated image is in + * place, and this file then contributes nothing. + */ + +const unsigned char aligned_data(4) romfs_img[] = +{ + 0x00 +}; + +const unsigned int romfs_img_len = 1; + +#endif /* !HAVE_ROMFS_BOOT */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/include/nuttx/pgalloc.h b/include/nuttx/pgalloc.h index 33f6a937b4593..bec03d9b4e719 100644 --- a/include/nuttx/pgalloc.h +++ b/include/nuttx/pgalloc.h @@ -69,6 +69,12 @@ #elif CONFIG_MM_PGSIZE == 16384 # define MM_PGSIZE 16384 # define MM_PGSHIFT 14 +#elif CONFIG_MM_PGSIZE == 32768 +# define MM_PGSIZE 32768 +# define MM_PGSHIFT 15 +#elif CONFIG_MM_PGSIZE == 65536 +# define MM_PGSIZE 65536 +# define MM_PGSHIFT 16 #else # error CONFIG_MM_PGSIZE not supported #endif diff --git a/mm/Kconfig b/mm/Kconfig index aebba1d0130fe..e0b8441a6ae71 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -301,9 +301,10 @@ config MM_PGSIZE int "Page Size" default 4096 ---help--- - The MMU page size. Must be one of {1024, 2048, 4096, 8192, or - 16384}. This is easily extensible, but only those values are - currently support. + The MMU page size. Must be one of {1024, 2048, 4096, 8192, 16384, + 32768, or 65536}. This is easily extensible, but only those values + are currently support. 64 KB (65536) matches the ESP32-S3 cache-MMU + page size. config DEBUG_PGALLOC bool "Page Allocator Debug"