[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