casaroli opened a new pull request, #19775:
URL: https://github.com/apache/nuttx/pull/19775
> **Draft.** The evidence is emulation only. I keep this as a draft until
`fork()` runs on armv7-a hardware.
## Summary
`fork()` was withdrawn from every architecture by #19562, and each
architecture restores it with the correct behaviour. This pull request restores
it for armv7-a.
`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 page by page and copied into fresh pages hung off the L1
and L2 tables of the child.
The saved-syscall-frame half is already in master. #19562 added it, so
`arm_syscall()` stores the exception frame of the caller in `xcp.sregs` and
`arm_fork()` builds the child from it.
## The change that reaches beyond fork()
`up_initial_state()` now puts the register save area of a user process on
its **kernel** stack, not at the top of its user stack. RISC-V and ARM64
already do this, so armv7-a is being brought into line.
Two reasons. The area holds the CPSR that the thread is resumed with, so it
must not be user-writable. And a `fork()` child inherits the stack address of
its parent, so the top of "its" stack is occupied by the live frames of the
parent, including the exception frame that the child is built from. Zeroing
`XCPTCONTEXT_SIZE` bytes there would destroy both.
`arm_fork()` then lets the child run at the stack addresses of the parent. A
pointer to a stack local taken before `fork()` must name the same object in the
child that it named in the parent, so the child adopts the stack geometry of
the parent rather than a relocated copy. With the save area on the kernel stack
there is nothing left for `arm_fork_syscall()` to re-point.
**This touches every task on the architecture, not only forked ones.** It is
the part of this patch that most needs review.
## Impact
The change is specific to armv7-a. It adds capability and removes none.
`fork()` becomes available on armv7-a in a kernel build over an MMU.
`ARCH_HAVE_FORK` keeps `depends on ARCH_ADDRENV`, so a configuration without
address environments is unaffected. A protected build is excluded.
Cortex-M and Cortex-R are untouched, because `BUILD_KERNEL` needs
`ARCH_USE_MMU`.
`vfork()` does not change. No board configuration changes.
## Testing
Emulation only. QEMU 11.0.3 on macOS 15 with Apple Silicon, Arm GNU
`arm-none-eabi-gcc` 14.2.rel1.
| configuration | mode | result |
|---|---|---|
| `qemu-armv7a:knsh` | kernel | `vfork()` and `fork()` pass, `ostest` exits
0 |
| `qemu-armv7a:nsh` | flat | `vfork()` passes, `fork()` correctly absent |
```
qemu-system-arm -cpu cortex-a7 -nographic -machine virt,highmem=off \
-net none -semihosting -kernel nuttx
```
The kernel row needs `-semihosting`, because a kernel build loads its
applications over hostfs.
```
user_main: vfork() test
vfork_test: Child 6 ran and exited before the parent resumed
user_main: fork() test
fork_test: Child running independently (child)
fork_test: Parent and child had independent memory
ostest_main: Exiting with status 0
```
The flat row is there on purpose. `up_initial_state()` affects every task,
so a flat build had to be checked as well, even though it has no `fork()`.
`tools/checkpatch.sh -c -u -m -g` gives no errors.
### What is not tested
No armv7-a hardware. This is the reason for the draft status. A board with
an MMU, such as an i.MX6 or a SAMA5, would be the useful report.
The protected build is not tested. `fork()` is excluded from it in any case.
**A report from an armv7-a board is welcome.** Take this branch with
apache/nuttx-apps#3685, build a kernel configuration and run `ostest`. The fork
tests are the first output.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]