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 #include #include +#include #include #include @@ -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 *