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

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

commit 1bc7cfeeb119efe939ada6f79906c13ba895c7b3
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Aug 10 15:17:25 2026 +0200

    arch/xtensa: Provide POSIX fork() on the ESP32-S3.
    
    up_addrenv_fork() duplicates an address environment into freshly allocated
    pages mapped at the same virtual addresses.  The text, data and heap regions
    of the source are walked one page at a time and copied into fresh pages hung
    off the child's own directory, using the two kmap slots that
    CONFIG_ARCH_KMAP_NPAGES reserves for exactly this.
    
    xtensa_fork.c already took both paths:  a child that keeps the parent's 
stack
    addresses needs no relocation, which is what a duplicated address 
environment
    gives it.  Only the hook and the Kconfig default were missing.
    
    fork() is offered on a kernel build, which is the only mode with per-process
    address environments.
    
    Verified on an ESP32-S3-WROOM-2 with esp32s3-devkit:kernel_oct.  ostest
    reports "Parent and child had independent memory" and exits with status 0.
    
    Assisted-by: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Marco Casaroli <[email protected]>
---
 arch/Kconfig                              |  25 +++--
 arch/xtensa/src/esp32s3/esp32s3_addrenv.c | 149 ++++++++++++++++++++++++++++++
 2 files changed, 164 insertions(+), 10 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 66117d4de2f..c98bc643e83 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -159,6 +159,7 @@ 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
@@ -1030,8 +1031,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
@@ -1041,14 +1040,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)"
@@ -1058,6 +1052,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/src/esp32s3/esp32s3_addrenv.c 
b/arch/xtensa/src/esp32s3/esp32s3_addrenv.c
index 1b15cf3635e..32c3390bb02 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_addrenv.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_addrenv.c
@@ -29,6 +29,7 @@
 #include <assert.h>
 #include <debug.h>
 #include <errno.h>
+#include <sched.h>
 #include <string.h>
 
 #include <nuttx/addrenv.h>
@@ -102,6 +103,7 @@ static int alloc_region(uintptr_t *pages, unsigned int 
maxpages, size_t size,
 
       if (paddr == 0)
         {
+          berr("ERROR: page pool exhausted at page %u of %u\n", i, npages);
           *count = i;
           return -ENOMEM;
         }
@@ -576,6 +578,153 @@ int up_addrenv_clone(const arch_addrenv_t *src, 
arch_addrenv_t *dest)
   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
  *

Reply via email to