Documentation: um: document nommu UML

Add a description of nommu UML, a bit about how it works and some
notes on how to use it. Partially based on Hajime's documentation.

Link: https://patch.msgid.link/20260723113633.19c6669dc151.Ibd7a532eef856a1ea39699a263dfa8a9ded9dc20@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-07-23 11:35:51 +02:00
parent 626ff2b34d
commit 92dae5e08d
2 changed files with 55 additions and 0 deletions

View File

@ -9,6 +9,7 @@ Virtualization Support
kvm/index
uml/user_mode_linux_howto_v2
uml/nommu-uml
paravirt_ops
guest-halt-polling
ne_overview

View File

@ -0,0 +1,54 @@
.. SPDX-License-Identifier: GPL-2.0
.. contents:: :local:
Execution model
===============
When emulating a system with an MMU, UML gives every ``mm_struct`` its own
host process with its own host address space, and keeps that address space
in sync with the kernel's page tables by issuing ``mmap()``/``munmap()``
into it.
NOMMU has no per-process address spaces: there is a single "physical" address
space that the kernel and every userspace process share. UML models this with
a **userspace runner** - an ordinary uml-userspace stub host process, started
exactly like an MMU userspace process, but with the whole of "physical" memory
(the physmem file) mapped into it at the kernel's addresses:
- The runner ``exec()``'s the stub just like the MMU case. The only extra step
is that, once it is up, the kernel maps the entire physmem file into it in one
go, at the same addresses the kernel uses.
- Because NOMMU never remaps, that single mapping is all a runner ever needs:
there is no per-mm host process, no TLB sync and no ``mmap()`` of individual
page-table entries into a child. Userspace code and data - placed in memory
by the loader - are directly executable and accessible.
- The runner processes hold no per-task state: the kernel loads a task's state
on every entry, so there's just one process per CPU.
Running a task's userspace therefore only means pointing a runner at that
task's code and letting it run until the next syscall or signal
Building and running
====================
Configure and build the ARCH=um kernel normally, but deselect CONFIG_MMU.
KUnit tests can be run in NOMMU mode with::
./tools/testing/kunit/kunit.py run \
--kconfig_add CONFIG_MMU=n --kconfig_add CONFIG_KUNIT_UML_PCI=n
Running a normal userspace requires NOMMU-aware binaries. There is no stock
x86_64 NOMMU distribution, but a prebuilt Alpine image with musl-libc and
busybox built for NOMMU is available and can be turned into a root image::
cid=$(docker create ghcr.io/thehajime/alpine:3.20.3-um-nommu)
docker export "$cid" > alpine.tar
docker rm "$cid"
mkdir alpine-root && tar xf alpine.tar -C alpine-root
mke2fs -q -F -t ext4 -d alpine-root alpine.ext4 1200M
Then boot it::
./linux ubd0=./alpine.ext4 root=/dev/ubda rw mem=1024m init=/sbin/init