nuttx/arch/x86_64/include
Marco Casaroli 067e30e14f arch/x86_64: Build fork() children from the caller's syscall frame.
In a kernel build vfork() is reached through a system call, so the return
address and stack pointer the architecture's entry point can see for itself
are the kernel's, not the caller's.  A child built from those resumes at a
kernel address, which is why x86_64 selected the fork family only for the
flat build.

x86_64_syscall() now publishes the caller's frame in xcp.sregs for the
duration of the stub call, and x86_64_fork() builds the child from it:

  x86_64_fork_syscall()  when xcp.sregs is non-NULL, so that the child
                         returns from the very same `syscall' instruction as
                         the parent, in user mode, on its own stack;
  x86_64_fork_direct()   otherwise, which is the flat build and any kernel
                         thread that calls the entry point as a plain
                         function.

The discriminator is xcp.sregs rather than TCB_FLAG_SYSCALL, which arm64 and
RISC-V use:  that flag also defers signal actions, x86_64 has never raised it,
and its kernel-build signal path does not survive being made to -- a
pre-existing problem that does not belong to this work.

Two properties of SYSCALL/SYSRET shape the child's frame.  The instruction
leaves the caller's RIP and RFLAGS in RCX and R11 rather than on a stack, so
they are moved into the RIP and RFLAGS slots of the interrupt frame the child
is resumed from; and the hardware never records the caller's CS and SS at all,
SYSRETQ reconstructing them from IA32_STAR, so the child's are filled in with
the user code and data selectors at RPL 3.  The frame is therefore not copied
wholesale:  the extended state and the general registers are inherited, while
the segment registers and the thread pointer stay as up_initial_state() left
them, the child's stack being a fresh allocation the parent's FS base does not
describe.

x86_64_fork_relocfp() is new and is not optional here.  A function returns
with `leave', which feeds the frame pointer into the stack pointer, so
relocating only the RBP the child resumes with gets it exactly one frame:
the next return loads a saved RBP still pointing into the parent's stack.

With that in place ARCH_X86_64 can select ARCH_HAVE_VFORK unconditionally.

Build-verified on qemu-intel64:knsh_romfs and qemu-intel64:ostest.  NuttX on
qemu-intel64 requires tsc-deadline and pcid, which TCG does not implement, so
it cannot be run on this host.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
..
intel64 arch/x86_64: Build fork() children from the caller's syscall frame. 2026-08-10 08:57:30 -03:00
qemu arch/x64: create qemu chip 2026-01-19 10:46:09 +08:00
.gitignore
acpi.h arch/x86_64: add poweroff functionality 2026-01-19 14:10:12 +08:00
arch.h arch/x86_64: use pause to reduce power consumption in spin wait loops 2026-01-30 08:09:44 -03:00
barriers.h arch/x86_64: use pause to reduce power consumption in spin wait loops 2026-01-30 08:09:44 -03:00
elf.h arch/x86_64:Adapt coredump 2025-01-22 16:35:32 +08:00
hpet.h arch/x86_64: migrate to SPDX identifier 2024-12-02 17:23:25 +08:00
inttypes.h arch/x86_64: migrate to SPDX identifier 2024-12-02 17:23:25 +08:00
io.h arch/x86_64: migrate to SPDX identifier 2024-12-02 17:23:25 +08:00
irq.h arch: rename STACK_ALIGNMENT to STACKFRAME_ALIGN across all architectures 2026-01-22 15:37:24 +08:00
limits.h arch/x86_64: migrate to SPDX identifier 2024-12-02 17:23:25 +08:00
multiboot2.h arch/x86_64: migrate to SPDX identifier 2024-12-18 17:51:57 +08:00
setjmp.h arch/x86_64: migrate to SPDX identifier 2024-12-02 17:23:25 +08:00
spinlock.h arch/x86_64:use UP_DSB, UP_DMB, UP_ISB as barrier standard API 2025-01-20 20:13:07 +08:00
syscall.h x86_64:fix syscall type mismatch 2025-12-18 06:31:43 +01:00
types.h arch/x86_64: migrate to SPDX identifier 2024-12-02 17:23:25 +08:00