Skip to content

M7: User mode #8

Open
Open

Description

@tamnd

The eighth milestone in spec/23-milestones.md. linux-user and bsd-user.

Why

User mode is what builds half the world's cross-architecture container images through binfmt_misc. It is a different product from system emulation with the same JIT underneath.

linux-user

  • Loader, syscall translation, signals, threads, /proc emulation
  • Page size mismatch handling
  • strace mode and the gdbstub in user mode
  • x86-64, aarch64, riscv64, arm, i386, ppc64le and s390x guests
  • Every other target QEMU supports in user mode

bsd-user and packaging

  • bsd-user on FreeBSD hosts
  • binfmt_misc registration and the static musl multi-call binary

Exit criterion

  1. LTP syscall tests pass at QEMU's rate or better for each target in the first group, with every regression triaged.
  2. A Debian arm64 and riscv64 chroot on an x86-64 host builds the packages listed in spec/10-user-mode.md.
  3. linux-user runs SPEC CPU2017 intrate no slower than QEMU linux-user with tier 1.

Depends on

M4.

Spec

spec/10-user-mode.md.

Activity

  1. added this to the M7: User mode milestone on Sep 30, 2026
  2. added
    status/gateHas an exit criterion that decides whether the milestone is done.
    kind/milestoneA milestone from spec/23-milestones.md.
    on Sep 30, 2026
  3. tamnd commented on Oct 9, 2026

    @tamnd
    OwnerAuthor

    Slice 1 merged in #197: qemu-x86_64 runs static and dynamic x86_64 programs on an x86_64 Linux host. It covers the guest address space and mmap engine in user-common, the ELF loader, the x86_64 cpu loop on the TCG JIT, and the common syscalls. Its stdout, stderr and exit codes matched QEMU 11.1 qemu-x86_64 on 46 programs: hello (static and dynamic), busybox applets, coreutils, dash, and fault and abort cases. No box is ticked yet, because the first one also needs signals, threads and /proc, which are slice 2.

  4. tamnd commented on Oct 9, 2026

    @tamnd
    OwnerAuthor

    Signal delivery for x86_64 guests landed in #203 (rt_sigframe, rt_sigreturn, safe_syscall, fault signals), compared against qemu-x86_64 11.1. Threads and /proc are next for this item.

  5. tamnd commented on Oct 9, 2026

    @tamnd
    OwnerAuthor

    Slice 2, threads: #207 is merged (c88dcf0). It adds guest threads for qemu-x86_64: clone with CLONE_VM, set_tid_address, CHILD_CLEARTID futex wake on exit, exit vs exit_group, and the jit's parallel mode. A pthread test and coreutils sort --parallel match qemu-x86_64 11.1. The /proc emulation is next, then the first box.

  6. tamnd commented on Oct 9, 2026

    @tamnd
    OwnerAuthor

    Slice 2, /proc: #209 is merged (5644f73). /proc/self/maps, smaps, stat, auxv and cmdline now describe the guest as QEMU's open_self_* functions write them, also as /proc// and through relative names. A property test, static and dynamic, matches qemu-x86_64 11.1 except that MAP_SHARED anonymous memory shows as private; a small follow-up makes it shared. With #203 (signals), #207 (threads) and #209, the first box is done and ticked. Next is slice 3: aarch64 and riscv64 guests.

  7. tamnd commented on Oct 9, 2026

    @tamnd
    OwnerAuthor

    Follow-up to #209: #211 is merged. MAP_SHARED anonymous memory is now shared on the host as well, so a guest's forked children share it as they do under Linux and QEMU. A fork test matches qemu-x86_64 11.1 and the native run. The /proc test from #209 now matches QEMU on every line.

  8. tamnd commented on Oct 9, 2026

    @tamnd
    OwnerAuthor

    The aarch64 guest landed in #216: target, sigframes, hwcaps, cpuinfo, generic syscall numbering and the target-arm user mode switch. It was compared against qemu-aarch64 11.1 with static and dynamic cross-built programs, busybox, and the signal, frame, thread, /proc and shm suites. The outputs match apart from the max model's feature set and QEMU's core dump line. A follow-up removes a sysconf unsafe that went over the linux-user budget. riscv64 is next.

  9. tamnd commented on Oct 10, 2026

    @tamnd
    OwnerAuthor

    #218 merged (f4268f9): /proc/cpuinfo now counts online CPUs from /sys/devices/system/cpu/online instead of calling sysconf, so ruvm-linux-user is back to 4 unsafe items, within its budget. riscv64 is next.

  10. tamnd commented on Oct 10, 2026

    @tamnd
    OwnerAuthor

    #228 merged (1b57a64): riscv64 guest. It adds the cpu loop with ecall, hwprobe and flush_icache, the rt_sigframe and ucontext with the F/D state, the riscv64 ELF hwcap and /proc/cpuinfo, -cpu, and the riscv sigaction layout. Static and dynamic hello, crash, signal, thread, fork and shm programs, a frame test, and busybox were run under ruvm and under qemu-riscv64 built from the 11.1 source, and their output was compared. It also fixes a shared GuestSpace bug: when ELF_ET_DYN_BASE fell outside the reservation, QEMU's fallback put it right on TASK_UNMAPPED_BASE, so both bases now fall back together. One difference is known: the isa string in /proc/cpuinfo leaves out zicfilp, zicfiss, zvfbfa, smctr, ssctr and ssdbltrp, because target-riscv does not implement them. Next is the direct guest_base memory path.

  11. tamnd commented on Oct 10, 2026

    @tamnd
    OwnerAuthor

    Merged #238 (f4e1080): the x86-64 backend now inlines 128-bit atomic guest loads and stores as one VMOVDQA or VMOVDQU, as QEMU does on hosts where that access is atomic, instead of sending every one of them through the slow path when the guest runs threads. On server2, sort of a 2 million line file under qemu-x86_64 went from 5148 ms to 1379 ms (QEMU 1417 ms), and a small CPU benchmark went from 4422 ms to 4044 ms (QEMU 2727 ms). The rest of that gap is the TLB lookup on every access, which the direct guest_base path is meant to remove next.

  12. tamnd commented on Oct 10, 2026

    @tamnd
    OwnerAuthor

    Merged #245 (2c2baad): two linux-user mremap fixes found while testing the direct guest_base path. A moving mremap of part of a merged mapping now splits the range at both ends first, and a failed host move_pages maps zero pages back over the destination the kernel already unmapped. New tests in user-common and sys. The x86-64 mremap programs and the x86-64 comparison suite give the same output as qemu-x86_64 11.1.

  13. 2 remaining items

  14. tamnd commented on Oct 13, 2026

    @tamnd
    OwnerAuthor

    Ticked the strace and gdbstub box. -strace landed in #280 and -g, the user mode gdbstub, in #288, both compared against QEMU's linux-user on server2.

  15. tamnd commented on Oct 13, 2026

    @tamnd
    OwnerAuthor

    The direct guest_base memory path for user mode merged as #292 (679991e). On an x86_64 host each guest load and store in the JIT is now a host access at guest_base plus the address when it is in bounds and aligned enough, and falls back to the old slow path otherwise. Host faults on direct accesses resume in that slow path, and pages with translated code stay read only on the host, as QEMU's page_protect does.

    Benchmarks on server2, best of 5 runs over three rounds, wall time in ms, all under qemu-x86_64 or ruvm as qemu-x86_64:

    sort -n, 2M lines sort -k1,1n -k2,2nr cpub
    before #238 4537 5347 4234
    main before #292 1541 2176 4076
    #292 1246 1849 3824
    qemu-x86_64 11.1 1163 1878 2887
    host 279 271 352

    Sort is within about 7% of QEMU, and sort -k is level with it. The CPU benchmark is still about 30% behind; that gap is in the generated code, not in memory accesses. The x86_64, aarch64 and riscv64 comparison suites and the thread suite give the same results as before against QEMU.

  16. tamnd commented on Oct 13, 2026

    @tamnd
    OwnerAuthor

    Plan for the remaining M7 guests (arm, i386, ppc64le, s390x).

    i386 runs on the existing x86 frontend, but the other three have no frontend in ruvm yet. target-arm is AArch64 only, and target-ppc and target-s390x are empty crates. Each of those needs a translator before linux-user can run its programs, so this is a series of PRs per target, not one PR per guest. In order:

    1. i386: the 32-bit guest in linux-user, with a compat layer that converts system call arguments and structures between a 32-bit guest and the 64-bit host (stat64, iovec, timespec in both widths, msghdr, sigaction and the rest). It also covers int 0x80, set_thread_area, the 32-bit signal frames and a 4 GiB guest space.
    2. arm: AArch32 in target-arm, starting with the A32 and T32 decoders and the integer instructions, then VFP, then Neon, each a PR. Then qemu-arm on the compat layer from step 1, with the kuser helper page and the Arm signal frames. Every target-arm PR also runs the AArch64 tests and the system-mode boot tests, so the AArch64 side does not regress.
    3. ppc64le: target-ppc for 64-bit little endian user mode, integer and branch first, then FP, then Altivec and VSX. Then the linux-user guest with the ppc system call numbers and constants.
    4. s390x: target-s390x for z/Architecture user mode, integer first, then FP and the vector facility. Then the linux-user guest, the first big-endian one, which byte swaps every structure that crosses the guest boundary.

    Each step is compared against QEMU 11.1 linux-user on the test servers, with small programs and risu-style instruction sequences for the frontends, and I'll comment here after each guest merges.

  17. tamnd commented on Oct 13, 2026

    @tamnd
    OwnerAuthor

    #297 is merged: -g now works for aarch64 and riscv64 programs. The register features match QEMU's user-mode gdbstub (core, FPU or SVE, pauth and TPIDR on aarch64; core, FPU, vector, privilege level and user CSRs on riscv64), and gdb-multiarch gives the same output as against qemu-aarch64 and qemu-riscv64 11.1 over breakpoints, register reads and writes, catch syscall, stepping, fork following and the target description. Still missing: aarch64's org.qemu.gdb.arm.sys.regs feature, and riscv64's cycle/time/instret, which read ruvm's counters rather than the host TSC.

    Next is i386 with the 32-bit compat syscall layer.

  18. tamnd commented on Oct 14, 2026

    @tamnd
    OwnerAuthor

    #313 is merged: qemu-i386 now runs static and dynamic i386 programs through the new 32-bit compat system call layer. Its output matches qemu-i386 11.1 on the tests/tcg i386 and multiarch programs, except for the documented one-page mmap offset from the missing vDSO. Signal frames for i386 come next, followed by sockets, IPC and timers.

  19. tamnd commented on Oct 14, 2026

    @tamnd
    OwnerAuthor

    #327 merged: the first AArch32 slice. target-arm decodes A32, T32 and T16 with the integer instructions, plus the VFP moves, loads and stores glibc needs at start-up. qemu-arm runs a static armhf hello with the same output as QEMU 11.1. The target-arm and machine-arm tests still pass.

    Next for AArch32: VFP arithmetic, the kuser helpers and commpage, and signal frames. Neon comes after that.

  20. tamnd commented on Oct 14, 2026

    @tamnd
    OwnerAuthor

    #331 merged: qemu-i386 delivers signals now, with the old and rt frames, the alternate stack and the old signal calls. A signal test and the tests/tcg signal and thread programs give the same output as QEMU 11.1. linux-test and signals still stop at socket() and timer_create(), which are ENOSYS on i386 for now.

    Next is AArch32 VFP arithmetic.

  21. tamnd commented on Oct 14, 2026

    @tamnd
    OwnerAuthor

    #332 merged (6187b2d): AArch32 VFP arithmetic and conversions. It covers half, single and double precision arithmetic, the fused forms, compares, VRINT, every VCVT form (integer, fixed point, f16, precision) and the v8 VSEL, VMAXNM/VMINNM and directed rounding. A static armhf test sweeps 19 special values through every form and checks FPSCR flags under each rounding mode and FZ, DN and AHP. Its output matches QEMU 11.1's qemu-arm exactly, built both as Thumb-2 and as A32. Still UNDEF: Neon, the BF16 VCVT and VJCVT. Next is the kuser helpers, the commpage and signal frames for qemu-arm.

  22. tamnd commented on Oct 14, 2026

    @tamnd
    OwnerAuthor

    M7 arm: #333 merged (320c580). qemu-arm now has the commpage and kuser helpers (version 5, barrier, cmpxchg, get_tls, cmpxchg64) and the sigframe and rt_sigframe signal frames, including the VFP frame and the Thumb return trampolines on the page where QEMU puts its vDSO. A static armhf signal and kuser test gives the same output as QEMU 11.1 qemu-arm, built for both Thumb and A32. Next is Neon.

  23. tamnd commented on Oct 15, 2026

    @tamnd
    OwnerAuthor

    M7 arm: #346 merged (d9f4ea1). This is AArch32 Neon slice 1, for A32 and T32. It has the integer data processing groups, every VLDn/VSTn form with QEMU's alignment checks, VMOV scalar, and VDUP. 10714 generated single instruction tests, run as A32 and as Thumb, match QEMU 11.1 qemu-arm exactly, and so does an intrinsics program at -O1 and -O3. The FP ops, VRECPE/VRSQRTE and crypto still UNDEF, and HWCAP_NEON is still clear.

    Signal test note: the signal tests still differ from QEMU, as they already did at #333. The CPSR GE bits differ, and the fault address can move by 0x10 with the environment size. ruvm doesn't provide AT_SYSINFO_EHDR yet, so the stack sits 8 bytes higher. Its masked HWCAP also makes glibc pick other string routines. Next is Neon slice 2: the FP ops, VRECPE/VRSQRTE, HWCAP_NEON and AT_SYSINFO_EHDR, so the signal tests match.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/user-modelinux-user and bsd-user.kind/milestoneA milestone from spec/23-milestones.md.priority/p2Real, and it waits.status/gateHas an exit criterion that decides whether the milestone is done.

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions