Skip to content

[1/3] arch/xtensa: Provide vfork() - #19795

Draft
casaroli wants to merge 1 commit into
apache:masterfrom
casaroli:xtensa-split-1-vfork
Draft

[1/3] arch/xtensa: Provide vfork()#19795
casaroli wants to merge 1 commit into
apache:masterfrom
casaroli:xtensa-split-1-vfork

Conversation

@casaroli

@casaroli casaroli commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Split from #19772, part 1 of 3, as asked in review.

It gives Xtensa vfork() and nothing else. No MMU, no address environment, no chip code.

up_fork(bool vfork) is the architecture half of the primitive introduced by #19562. The child gets its own stack holding a relocated copy of the part of the parent's that is in use, and resumes where the caller would have returned.

Two paths reach it. A flat build calls in directly, so the caller's registers are taken at the call. A build with system calls arrives through xtensa_swint(), which has already recorded the caller's frame in xcp.sregs; that frame is used instead, because the caller is on the far side of the boundary.

The windowed ABI needs care in one place. A stack pointer has a base save area 16 bytes below it holding the caller's spilled a0a3, and the frame chain runs through it. SPILL_ALL_WINDOWS puts the register file into memory first, and the chain is then walked and rebased onto the child's copy, because a frame pointer that still points into the parent's stack would send the child back into memory it does not own.

Impact

Xtensa gains vfork(). Nothing else changes, and no board configuration changes.

fork() is not provided here. That needs an address environment and arrives in part three.

Testing

Host: macOS 15 on Apple Silicon, xtensa-esp32s3-elf-gcc and xtensa-esp32-elf-gcc, both 12.2.0.

Three boards ran ostest to the end.

board module core configuration result
ESP32-DevKitC V4 ESP32-D0WD-V3 rev 3.1 LX6 esp32-devkitc:ostest vfork() passes, status 0
ESP32-S3-DevKitC ESP32-S3-WROOM-1 N8R2, 8 MB, DIO LX7 esp32s3-devkit:ostest vfork() passes, status 0
ESP32-S3-DevKitC ESP32-S3-WROOM-2 N32R8V, 32 MB, octal LX7 esp32s3-devkit:ostest vfork() passes, status 0

The 32 MB octal part also needs ESP32S3_FLASH_MODE_OCT and ESP32S3_SPI_FLASH_USE_32BIT_ADDRESS.

user_main: vfork() test
vfork_test: Started
vfork_test: Child 5 ran and exited before the parent resumed
ostest_main: Exiting with status 0

Both Xtensa cores that NuttX supports are covered, and the LX6 has no MMU at all — which is the case this part is meant to serve.

tools/checkpatch.sh -c -u -m -g reports no errors.

depends-on: apache/nuttx-apps/pull/3685

@github-actions github-actions Bot added Arch: xtensa Issues related to the Xtensa architecture Size: L The size of the change in this PR is large labels Aug 11, 2026
@casaroli casaroli changed the title arch/xtensa: Provide vfork() [1/3] arch/xtensa: Provide vfork() Aug 11, 2026
@casaroli
casaroli marked this pull request as draft August 11, 2026 14:23
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

esp32-devkitc

Xtensa selected neither fork primitive, so vfork() was simply absent.  This
wires it onto the two-primitive semantics.

There is no assembly entry point and none is needed.  Every exception entry
already runs SPILL_ALL_WINDOWS, so the whole context of the calling thread is
in its exception frame and copying its stack copies a complete frame chain.
A flat build reaches that frame through SYS_save_context, issued inline so
that the recorded stack pointer belongs to a frame that stays alive for the
whole operation; a build with syscalls reaches it through xcp.sregs, recorded
by xtensa_swint() for the duration of the call.

The stack copy needs more than a relocated stack pointer here.  A windowed
ABI stores each frame's caller stack pointer absolutely, in the base save
area below the frame, so a copy taken at a different address still names the
parent throughout and the child's first retw would underflow onto the
parent's stack.  xtensa_fork_rebase() walks that chain and adds the
relocation offset to each link.  The copy also starts one base save area
below the stack pointer rather than at it, because the frame the child
resumes into keeps its caller's spilled a0-a3 there.

Ported from the per-architecture work, reduced to the two primitives.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
@casaroli

Copy link
Copy Markdown
Contributor Author

This work is now in #19798, together with the ESP32-S3 chip code and the user mode isolation that a kernel build needs.

Please review #19798 instead of this one.

This pull request stays open for reference, and it will be closed when #19798 is merged.

@github-actions

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The read-only Build run reported the following dependent PR(s) and fetched head SHA(s):

CI run: https://github.com/apache/nuttx/actions/runs/31531206452

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: xtensa Issues related to the Xtensa architecture Size: L The size of the change in this PR is large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant