[PATCH v2 10/10] Documentation: um: document nommu UML
Johannes Berg
johannes at sipsolutions.net
Thu Jul 23 02:35:51 PDT 2026
From: Johannes Berg <johannes.berg at intel.com>
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.
Signed-off-by: Johannes Berg <johannes.berg at intel.com>
---
Documentation/virt/index.rst | 1 +
Documentation/virt/uml/nommu-uml.rst | 54 ++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
create mode 100644 Documentation/virt/uml/nommu-uml.rst
diff --git a/Documentation/virt/index.rst b/Documentation/virt/index.rst
index c1f0bbc37315..142134ed45c0 100644
--- a/Documentation/virt/index.rst
+++ b/Documentation/virt/index.rst
@@ -9,6 +9,7 @@ Virtualization Support
kvm/index
uml/user_mode_linux_howto_v2
+ uml/nommu-uml
paravirt_ops
guest-halt-polling
ne_overview
diff --git a/Documentation/virt/uml/nommu-uml.rst b/Documentation/virt/uml/nommu-uml.rst
new file mode 100644
index 000000000000..105e596ae1cc
--- /dev/null
+++ b/Documentation/virt/uml/nommu-uml.rst
@@ -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
--
2.53.0
More information about the linux-um
mailing list