summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-14 13:36:11 +0100
committerMark Brown <broonie@kernel.org>2026-10-14 13:36:11 +0100
commitdc7bf241c0b573b997ae991af2425dcb61695db5 (patch)
treed4934de97479ea9a2a11969c612e9be3420cdbab
parent600ff6eaf5fa12f8010705ed3bf5de22cd6f216e (diff)
parent4ad423aa2108485f8bc5dde51fdf9c1ed979ee4b (diff)
downloadlinux-next-dc7bf241c0b573b997ae991af2425dcb61695db5.tar.gz
linux-next-dc7bf241c0b573b997ae991af2425dcb61695db5.zip
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/uml/linux.git
# Conflicts: # arch/um/Kconfig
-rw-r--r--Documentation/virt/index.rst1
-rw-r--r--Documentation/virt/uml/nommu-uml.rst54
-rw-r--r--arch/um/Kconfig38
-rw-r--r--arch/um/Makefile7
-rw-r--r--arch/um/drivers/Kconfig3
-rw-r--r--arch/um/drivers/cow_user.c34
-rw-r--r--arch/um/drivers/mconsole_kern.c14
-rw-r--r--arch/um/drivers/vector_kern.c37
-rw-r--r--arch/um/drivers/vector_user.c3
-rw-r--r--arch/um/drivers/virtio_pcidev.c19
-rw-r--r--arch/um/drivers/virtio_uml.c26
-rw-r--r--arch/um/include/asm/common.lds.S2
-rw-r--r--arch/um/include/asm/futex.h53
-rw-r--r--arch/um/include/asm/mmu.h8
-rw-r--r--arch/um/include/asm/mmu_context.h2
-rw-r--r--arch/um/include/asm/perf_event.h7
-rw-r--r--arch/um/include/asm/pgtable.h25
-rw-r--r--arch/um/include/asm/ptrace-generic.h6
-rw-r--r--arch/um/include/asm/tlbflush.h20
-rw-r--r--arch/um/include/asm/uaccess.h6
-rw-r--r--arch/um/include/shared/as-layout.h3
-rw-r--r--arch/um/include/shared/kern_util.h3
-rw-r--r--arch/um/include/shared/longjmp.h3
-rw-r--r--arch/um/include/shared/os.h6
-rw-r--r--arch/um/include/shared/skas/stub-data.h9
-rw-r--r--arch/um/kernel/Makefile6
-rw-r--r--arch/um/kernel/dyn.lds.S9
-rw-r--r--arch/um/kernel/mem-pgtable.c55
-rw-r--r--arch/um/kernel/mem.c47
-rw-r--r--arch/um/kernel/physmem.c7
-rw-r--r--arch/um/kernel/process.c23
-rw-r--r--arch/um/kernel/skas/Makefile16
-rw-r--r--arch/um/kernel/skas/mmu.c25
-rw-r--r--arch/um/kernel/skas/nommu.c91
-rw-r--r--arch/um/kernel/skas/process.c32
-rw-r--r--arch/um/kernel/skas/syscall.c5
-rw-r--r--arch/um/kernel/skas/uaccess.c134
-rw-r--r--arch/um/kernel/smp.c3
-rw-r--r--arch/um/kernel/sysrq.c2
-rw-r--r--arch/um/kernel/time.c3
-rw-r--r--arch/um/kernel/tlb.c7
-rw-r--r--arch/um/kernel/trap-mmu.c237
-rw-r--r--arch/um/kernel/trap-nommu.c13
-rw-r--r--arch/um/kernel/trap.c219
-rw-r--r--arch/um/kernel/um_arch.c11
-rw-r--r--arch/um/kernel/uml.lds.S11
-rw-r--r--arch/um/os-Linux/main.c17
-rw-r--r--arch/um/os-Linux/signal.c2
-rw-r--r--arch/um/os-Linux/skas/process.c11
-rw-r--r--arch/x86/um/Makefile3
-rw-r--r--arch/x86/um/asm/elf.h8
-rw-r--r--arch/x86/um/asm/required-features.h9
-rw-r--r--arch/x86/um/vdso/vma.c13
-rw-r--r--fs/Kconfig.binfmt2
-rw-r--r--lib/Kconfig.debug10
-rw-r--r--lib/kunit/Kconfig2
56 files changed, 906 insertions, 516 deletions
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
diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 94b8ff70f578..7245e3ada1c5 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -17,9 +17,9 @@ config UML
select ARCH_HAS_KCOV
select ARCH_HAS_STRNCPY_FROM_USER
select ARCH_HAS_STRNLEN_USER
- select ARCH_HAS_STRICT_KERNEL_RWX
+ select ARCH_HAS_STRICT_KERNEL_RWX if MMU
select HAVE_ARCH_AUDITSYSCALL
- select HAVE_ARCH_KASAN if X86_64
+ select HAVE_ARCH_KASAN if X86_64 && MMU
select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ASM_MODVERSIONS
@@ -27,6 +27,7 @@ config UML
select HAVE_DEBUG_KMEMLEAK
select HAVE_DEBUG_BUGVERBOSE
select HAVE_PAGE_SIZE_4KB
+ select HAVE_PERF_EVENTS
select NO_DMA if !UML_DMA_EMULATION
select OF_EARLY_FLATTREE if OF
select GENERIC_IRQ_SHOW
@@ -37,17 +38,22 @@ config UML
select ARCH_SUPPORTS_LTO_CLANG_THIN
select TRACE_IRQFLAGS_SUPPORT
select TTY # Needed for line.c
- select HAVE_ARCH_VMAP_STACK
+ select HAVE_ARCH_VMAP_STACK if MMU
select HAVE_RUST
select ARCH_HAS_UBSAN
select HAVE_ARCH_TRACEHOOK
select HAVE_SYSCALL_TRACEPOINTS
select THREAD_INFO_IN_TASK
select SPARSE_IRQ
+ select UACCESS_MEMCPY if !MMU
+ select GENERIC_STRNLEN_USER if !MMU
+ select GENERIC_STRNCPY_FROM_USER if !MMU
config MMU
- bool
+ bool "MMU support"
default y
+ help
+ Turning this off allows a nommu build, say Y.
config UML_DMA_EMULATION
bool
@@ -209,27 +215,15 @@ config MCONSOLE
It is safe to say 'Y' here.
-config MAGIC_SYSRQ
- bool "Magic SysRq key"
- depends on MCONSOLE
- help
- If you say Y here, you will have some control over the system even
- if the system crashes for example during kernel debugging (e.g., you
- will be able to flush the buffer cache to disk, reboot the system
- immediately or dump some status information). A key for each of the
- possible requests is provided.
-
- This is the feature normally accomplished by pressing a key
- while holding SysRq (Alt+PrintScreen).
-
- On UML, this is accomplished by sending a "sysrq" command with
- mconsole, followed by the letter for the requested command.
-
- The keys are documented in <file:Documentation/admin-guide/sysrq.rst>. Don't say Y
- unless you really know what this hack does.
+config ARCH_FORCE_MAX_ORDER
+ int "Order of maximal physically contiguous allocations" if EXPERT
+ default "10" if MMU
+ default "16" if !MMU
config KERNEL_STACK_ORDER
int "Kernel stack size order"
+ default 3 if !MMU
+ range 3 10 if !MMU
default 2 if 64BIT
range 2 10 if 64BIT
default 1 if !64BIT
diff --git a/arch/um/Makefile b/arch/um/Makefile
index 937639edc295..d930c5f25fb9 100644
--- a/arch/um/Makefile
+++ b/arch/um/Makefile
@@ -134,9 +134,10 @@ LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc
# Avoid binutils 2.39+ warnings by marking the stack non-executable and
# ignorning warnings for the kallsyms sections.
LDFLAGS_EXECSTACK = -z noexecstack
-ifeq ($(CONFIG_LD_IS_BFD),y)
-LDFLAGS_EXECSTACK += $(call ld-option,--no-warn-rwx-segments)
-endif
+# check --no-warn-rwx-segments with CC-as-linker since that gets used
+# for vmlinux and is where we need the option, in particular with LLVM=1
+# it might be different links (ld.bfd vs. ld.lld)
+LDFLAGS_EXECSTACK += $(call try-run,$(CC) -Wl$(comma)--no-warn-rwx-segments -shared -x c /dev/null -o "$$TMP",--no-warn-rwx-segments)
LD_FLAGS_CMDLINE = $(foreach opt,$(KBUILD_LDFLAGS) $(LDFLAGS_EXECSTACK),-Wl,$(opt))
diff --git a/arch/um/drivers/Kconfig b/arch/um/drivers/Kconfig
index 6a0354ca032f..655e5a270c97 100644
--- a/arch/um/drivers/Kconfig
+++ b/arch/um/drivers/Kconfig
@@ -159,6 +159,7 @@ config UML_RTC
config UML_PCI
bool
+ depends on MMU
select FORCE_PCI
select IRQ_MSI_LIB
select UML_IOMEM_EMULATION
@@ -169,6 +170,7 @@ config UML_PCI
config UML_PCI_OVER_VIRTIO
bool "Enable PCI over VIRTIO device simulation"
# in theory, just VIRTIO is enough, but that causes recursion
+ depends on MMU
depends on VIRTIO_UML
select UML_PCI
@@ -183,6 +185,7 @@ config UML_PCI_OVER_VIRTIO_DEVICE_ID
config UML_PCI_OVER_VFIO
bool "Enable VFIO-based PCI passthrough"
+ depends on MMU
select UML_PCI
help
This driver provides support for VFIO-based PCI passthrough.
diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c
index dc1d1bcd85ec..bb9f05ac70cf 100644
--- a/arch/um/drivers/cow_user.c
+++ b/arch/um/drivers/cow_user.c
@@ -279,7 +279,7 @@ int file_reader(__u64 offset, char *buf, int len, void *arg)
return pread(fd, buf, len, offset);
}
-/* XXX Need to sanity-check the values read from the header */
+/* Sanity checks are performed after reading each header version below. */
int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
__u32 *version_out, char **backing_file_out,
@@ -325,6 +325,12 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
*sectorsize_out = header->v1.sectorsize;
*bitmap_offset_out = sizeof(header->v1);
*align_out = *sectorsize_out;
+ if (!memchr(header->v1.backing_file, '\0',
+ sizeof(header->v1.backing_file))) {
+ cow_printf("%s - V1 backing_file not terminated\n",
+ __func__);
+ goto out;
+ }
file = header->v1.backing_file;
}
else if (version == 2) {
@@ -338,6 +344,12 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
*sectorsize_out = be32toh(header->v2.sectorsize);
*bitmap_offset_out = sizeof(header->v2);
*align_out = *sectorsize_out;
+ if (!memchr(header->v2.backing_file, '\0',
+ sizeof(header->v2.backing_file))) {
+ cow_printf("%s - V2 backing_file not terminated\n",
+ __func__);
+ goto out;
+ }
file = header->v2.backing_file;
}
/* This is very subtle - see above at union cow_header definition */
@@ -354,8 +366,15 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
if (*align_out == 0) {
cow_printf("read_cow_header - invalid COW header, "
"align == 0\n");
+ goto out;
}
*bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out);
+ if (!memchr(header->v3.backing_file, '\0',
+ sizeof(header->v3.backing_file))) {
+ cow_printf("%s - V3 backing_file not terminated\n",
+ __func__);
+ goto out;
+ }
file = header->v3.backing_file;
}
else if (version == 3) {
@@ -385,14 +404,27 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
if (*align_out == 0) {
cow_printf("read_cow_header - invalid COW header, "
"align == 0\n");
+ goto out;
}
*bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out);
+ if (!memchr(header->v3_b.backing_file, '\0',
+ sizeof(header->v3_b.backing_file))) {
+ cow_printf("%s - V3 backing_file not terminated\n",
+ __func__);
+ goto out;
+ }
file = header->v3_b.backing_file;
}
else {
cow_printf("read_cow_header - invalid COW version\n");
goto out;
}
+
+ if (*sectorsize_out <= 0) {
+ cow_printf("%s - invalid sectorsize %d\n", __func__,
+ *sectorsize_out);
+ goto out;
+ }
err = -ENOMEM;
*backing_file_out = cow_strdup(file);
if (*backing_file_out == NULL) {
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index e2a9e8879f58..f01b7d8a07bc 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -106,9 +106,9 @@ void mconsole_version(struct mc_request *req)
{
char version[256];
- sprintf(version, "%s %s %s %s %s", utsname()->sysname,
- utsname()->nodename, utsname()->release, utsname()->version,
- utsname()->machine);
+ snprintf(version, sizeof(version), "%s %s %s %s %s",
+ utsname()->sysname, utsname()->nodename, utsname()->release,
+ utsname()->version, utsname()->machine);
mconsole_reply(req, version, 0, 0);
}
@@ -117,10 +117,12 @@ void mconsole_log(struct mc_request *req)
int len;
char *ptr = req->request.data;
- ptr += strlen("log ");
+ ptr += strlen(req->cmd->command);
+ ptr = skip_spaces(ptr);
- len = req->len - (ptr - req->request.data);
- printk(KERN_WARNING "%.*s", len, ptr);
+ len = req->request.len - (ptr - req->request.data);
+ if (len > 0)
+ printk(KERN_WARNING "%.*s", len, ptr);
mconsole_reply(req, "", 0, 0);
}
diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index 8a70b3a62553..e902e78c5ff6 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -111,19 +111,26 @@ static void vector_reset_stats(struct vector_private *vp)
* in vector_poll.
*/
- spin_lock(&vp->rx_queue->head_lock);
+ if (vp->rx_queue)
+ spin_lock(&vp->rx_queue->head_lock);
+
vp->estats.rx_queue_max = 0;
vp->estats.rx_queue_running_average = 0;
vp->estats.rx_encaps_errors = 0;
vp->estats.sg_ok = 0;
vp->estats.sg_linearized = 0;
- spin_unlock(&vp->rx_queue->head_lock);
+
+ if (vp->rx_queue)
+ spin_unlock(&vp->rx_queue->head_lock);
+
/* TX stats are modified with TX head_lock held
* in vector_send.
*/
- spin_lock(&vp->tx_queue->head_lock);
+ if (vp->tx_queue)
+ spin_lock(&vp->tx_queue->head_lock);
+
vp->estats.tx_timeout_count = 0;
vp->estats.tx_restart_queue = 0;
vp->estats.tx_kicks = 0;
@@ -131,7 +138,10 @@ static void vector_reset_stats(struct vector_private *vp)
vp->estats.tx_flow_control_xoff = 0;
vp->estats.tx_queue_max = 0;
vp->estats.tx_queue_running_average = 0;
- spin_unlock(&vp->tx_queue->head_lock);
+
+ if (vp->tx_queue)
+ spin_unlock(&vp->tx_queue->head_lock);
+
}
static int get_mtu(struct arglist *def)
@@ -471,8 +481,6 @@ static void destroy_queue(struct vector_queue *qi)
struct vector_private *vp = netdev_priv(qi->dev);
struct mmsghdr *mmsg_vector;
- if (qi == NULL)
- return;
/* deallocate any skbuffs - we rely on any unused to be
* set to NULL.
*/
@@ -1166,15 +1174,15 @@ static int vector_poll(struct napi_struct *napi, int budget)
if ((vp->options & VECTOR_TX) != 0)
tx_enqueued = (vector_send(vp->tx_queue) > 0);
- spin_lock(&vp->rx_queue->head_lock);
- if ((vp->options & VECTOR_RX) > 0)
+ if ((vp->options & VECTOR_RX) > 0) {
+ spin_lock(&vp->rx_queue->head_lock);
err = vector_mmsg_rx(vp, budget);
- else {
+ spin_unlock(&vp->rx_queue->head_lock);
+ } else {
err = vector_legacy_rx(vp);
if (err > 0)
err = 1;
}
- spin_unlock(&vp->rx_queue->head_lock);
if (err > 0)
work_done += err;
@@ -1560,13 +1568,16 @@ static void vector_setup_etheraddr(struct net_device *dev, char *str)
"Attempt to assign an invalid ethernet address to a device disallowed\n");
goto random;
}
+
+ eth_hw_addr_set(dev, addr);
+
if (!is_local_ether_addr(addr)) {
+ addr[0] |= 0x02;
netdev_warn(dev, "Warning: Assigning a globally valid ethernet address to a device\n");
netdev_warn(dev, "You should set the 2nd rightmost bit in the first byte of the MAC,\n");
- netdev_warn(dev, "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n",
- addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4], addr[5]);
+ netdev_warn(dev, "i.e. %pM\n", addr);
}
- eth_hw_addr_set(dev, addr);
+
return;
random:
diff --git a/arch/um/drivers/vector_user.c b/arch/um/drivers/vector_user.c
index 2ea67e6fd067..3bee634f2102 100644
--- a/arch/um/drivers/vector_user.c
+++ b/arch/um/drivers/vector_user.c
@@ -93,6 +93,9 @@ struct arglist *uml_parse_vector_ifspec(char *arg)
len = strlen(arg);
for (pos = 0; pos < len; pos++) {
if (next_starts) {
+ if (result->numargs >= MAXVARGS)
+ goto cleanup;
+
if (parsing_token) {
result->tokens[result->numargs] = arg + pos;
} else {
diff --git a/arch/um/drivers/virtio_pcidev.c b/arch/um/drivers/virtio_pcidev.c
index 2bf5a645db45..53cffd373890 100644
--- a/arch/um/drivers/virtio_pcidev.c
+++ b/arch/um/drivers/virtio_pcidev.c
@@ -490,13 +490,24 @@ static int virtio_pcidev_init_vqs(struct virtio_pcidev_device *dev)
return 0;
}
+static void virtio_pcidev_del_vqs(struct virtio_device *vdev,
+ struct virtio_pcidev_device *dev)
+{
+ void *msg;
+
+ while ((msg = virtqueue_detach_unused_buf(dev->irq_vq)))
+ kfree(msg);
+
+ vdev->config->del_vqs(vdev);
+}
+
static void __virtio_pcidev_virtio_platform_remove(struct virtio_device *vdev,
struct virtio_pcidev_device *dev)
{
um_pci_platform_device_unregister(&dev->pdev);
virtio_reset_device(vdev);
- vdev->config->del_vqs(vdev);
+ virtio_pcidev_del_vqs(vdev, dev);
kfree(dev);
}
@@ -526,7 +537,7 @@ err_unregister:
um_pci_platform_device_unregister(&dev->pdev);
err_reset:
virtio_reset_device(vdev);
- vdev->config->del_vqs(vdev);
+ virtio_pcidev_del_vqs(vdev, dev);
err_free:
kfree(dev);
return err;
@@ -569,7 +580,7 @@ static int virtio_pcidev_virtio_probe(struct virtio_device *vdev)
err_reset:
virtio_reset_device(vdev);
- vdev->config->del_vqs(vdev);
+ virtio_pcidev_del_vqs(vdev, dev);
err_free:
kfree(dev);
return err;
@@ -591,9 +602,9 @@ static void virtio_pcidev_virtio_remove(struct virtio_device *vdev)
/* Stop all virtqueues */
virtio_reset_device(vdev);
+ virtio_pcidev_del_vqs(vdev, dev);
dev->cmd_vq = NULL;
dev->irq_vq = NULL;
- vdev->config->del_vqs(vdev);
kfree(dev);
}
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index 7425a8548141..30899ddbeae2 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -568,7 +568,7 @@ static void vhost_user_get_config(struct virtio_uml_device *vu_dev,
goto free;
}
- rc = vhost_user_recv_resp(vu_dev, msg, msg_size);
+ rc = vhost_user_recv_resp(vu_dev, msg, payload_size);
if (rc) {
vu_err(vu_dev,
"receiving VHOST_USER_GET_CONFIG response failed: %d\n",
@@ -1274,14 +1274,7 @@ static void virtio_uml_remove(struct platform_device *pdev)
/* Command line device list */
-static void vu_cmdline_release_dev(struct device *d)
-{
-}
-
-static struct device vu_cmdline_parent = {
- .init_name = "virtio-uml-cmdline",
- .release = vu_cmdline_release_dev,
-};
+static struct device *vu_cmdline_parent;
static DEFINE_MUTEX(vu_cmdline_lock);
static bool vu_cmdline_parent_registered;
@@ -1333,11 +1326,10 @@ static int vu_cmdline_set_device(const char *device)
return -EINVAL;
if (!vu_cmdline_parent_registered) {
- err = device_register(&vu_cmdline_parent);
- if (err) {
+ vu_cmdline_parent = __root_device_register("virtio-uml-cmdline", NULL);
+ if (IS_ERR(vu_cmdline_parent)) {
pr_err("Failed to register parent device!\n");
- put_device(&vu_cmdline_parent);
- return err;
+ return PTR_ERR(vu_cmdline_parent);
}
vu_cmdline_parent_registered = true;
}
@@ -1352,7 +1344,7 @@ static int vu_cmdline_set_device(const char *device)
pr_info("Registering device virtio-uml.%d id=%d at %s\n",
vu_cmdline_id, virtio_device_id, socket_path);
- pdev = platform_device_register_data(&vu_cmdline_parent, "virtio-uml",
+ pdev = platform_device_register_data(vu_cmdline_parent, "virtio-uml",
vu_cmdline_id++, &pdata,
sizeof(pdata));
err = PTR_ERR_OR_ZERO(pdev);
@@ -1393,7 +1385,7 @@ static int vu_cmdline_get(char *buffer, const struct kernel_param *kp)
buffer[0] = '\0';
if (vu_cmdline_parent_registered)
- device_for_each_child(&vu_cmdline_parent, buffer,
+ device_for_each_child(vu_cmdline_parent, buffer,
vu_cmdline_get_device);
return strlen(buffer) + 1;
}
@@ -1417,9 +1409,9 @@ static void vu_unregister_cmdline_devices(void)
guard(mutex)(&vu_cmdline_lock);
if (vu_cmdline_parent_registered) {
- device_for_each_child(&vu_cmdline_parent, NULL,
+ device_for_each_child(vu_cmdline_parent, NULL,
vu_unregister_cmdline_device);
- device_unregister(&vu_cmdline_parent);
+ root_device_unregister(vu_cmdline_parent);
vu_cmdline_parent_registered = false;
}
}
diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S
index fd481ac371de..6585cb93e6eb 100644
--- a/arch/um/include/asm/common.lds.S
+++ b/arch/um/include/asm/common.lds.S
@@ -11,6 +11,8 @@
RO_DATA(4096)
+ .lrodata : { *(.lrodata .lrodata.[0-9a-zA-Z_]* .lrodata..L*) }
+
.unprotected : { *(.unprotected) }
. = ALIGN(4096);
PROVIDE (_unprotected_end = .);
diff --git a/arch/um/include/asm/futex.h b/arch/um/include/asm/futex.h
index 780aa6bfc050..07ab81f09281 100644
--- a/arch/um/include/asm/futex.h
+++ b/arch/um/include/asm/futex.h
@@ -7,8 +7,61 @@
#include <asm/errno.h>
+#ifdef CONFIG_MMU
int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr);
int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval);
+#else
+#include <linux/atomic.h>
+#include <asm-generic/futex.h>
+
+#ifdef CONFIG_SMP
+static inline int
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
+{
+ u32 oldval, newval;
+ u32 *p = (u32 __force *)uaddr;
+
+ do {
+ oldval = READ_ONCE(*p);
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ newval = oparg;
+ break;
+ case FUTEX_OP_ADD:
+ newval = oldval + oparg;
+ break;
+ case FUTEX_OP_OR:
+ newval = oldval | oparg;
+ break;
+ case FUTEX_OP_ANDN:
+ newval = oldval & ~oparg;
+ break;
+ case FUTEX_OP_XOR:
+ newval = oldval ^ oparg;
+ break;
+ default:
+ return -ENOSYS;
+ }
+ } while (!try_cmpxchg(p, &oldval, newval));
+
+ *oval = oldval;
+ return 0;
+}
+
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
+ u32 oldval, u32 newval)
+{
+ u32 *p = (u32 __force *)uaddr;
+
+ *uval = cmpxchg(p, oldval, newval);
+
+ return 0;
+}
+#endif /* CONFIG_SMP */
+
+#endif /* CONFIG_MMU */
#endif
diff --git a/arch/um/include/asm/mmu.h b/arch/um/include/asm/mmu.h
index 07d48738b402..c0b9ce3215c4 100644
--- a/arch/um/include/asm/mmu.h
+++ b/arch/um/include/asm/mmu.h
@@ -21,6 +21,14 @@ typedef struct mm_context {
spinlock_t sync_tlb_lock;
unsigned long sync_tlb_range_from;
unsigned long sync_tlb_range_to;
+
+#ifndef CONFIG_MMU
+ unsigned long end_brk;
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+ unsigned long exec_fdpic_loadmap;
+ unsigned long interp_fdpic_loadmap;
+#endif
+#endif /* !CONFIG_MMU */
} mm_context_t;
#define INIT_MM_CONTEXT(mm) \
diff --git a/arch/um/include/asm/mmu_context.h b/arch/um/include/asm/mmu_context.h
index c727e56ba116..528b217da285 100644
--- a/arch/um/include/asm/mmu_context.h
+++ b/arch/um/include/asm/mmu_context.h
@@ -18,11 +18,13 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
{
}
+#ifdef CONFIG_MMU
#define init_new_context init_new_context
extern int init_new_context(struct task_struct *task, struct mm_struct *mm);
#define destroy_context destroy_context
extern void destroy_context(struct mm_struct *mm);
+#endif
#include <asm-generic/mmu_context.h>
diff --git a/arch/um/include/asm/perf_event.h b/arch/um/include/asm/perf_event.h
new file mode 100644
index 000000000000..9f774e2db198
--- /dev/null
+++ b/arch/um/include/asm/perf_event.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __UM_PERF_EVENT_H
+#define __UM_PERF_EVENT_H
+
+/* nothing here - clearly no real hardware support */
+
+#endif /* __UM_PERF_EVENT_H */
diff --git a/arch/um/include/asm/pgtable.h b/arch/um/include/asm/pgtable.h
index 88ea8434364d..6304026a2b2e 100644
--- a/arch/um/include/asm/pgtable.h
+++ b/arch/um/include/asm/pgtable.h
@@ -13,6 +13,7 @@
#define _PAGE_PRESENT 0x001
#define _PAGE_NEEDSYNC 0x002
+#define _PAGE_EXEC 0x004
#define _PAGE_RW 0x020
#define _PAGE_USER 0x040
#define _PAGE_ACCESSED 0x080
@@ -55,14 +56,19 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
-#define __PAGE_KERNEL_EXEC \
- (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
-#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
-#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
-#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC)
+
+#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
+#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
+#define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_RW)
+#define PAGE_SHARED_EXEC __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_EXEC)
+#define PAGE_COPY __pgprot(_PAGE_BASE)
+#define PAGE_COPY_EXEC __pgprot(_PAGE_BASE | _PAGE_EXEC)
+#define PAGE_READONLY __pgprot(_PAGE_BASE)
+#define PAGE_READONLY_EXEC __pgprot(_PAGE_BASE | _PAGE_EXEC)
+
+#define _PAGE_KERNEL (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
+#define PAGE_KERNEL __pgprot(_PAGE_KERNEL)
+#define PAGE_KERNEL_EXEC __pgprot(_PAGE_KERNEL | _PAGE_EXEC)
/*
* The i386 can't do page protection for execute, and considers that the same
@@ -117,7 +123,8 @@ static inline int pte_read(pte_t pte)
static inline int pte_exec(pte_t pte)
{
- return !pte_get_bits(pte, _PAGE_PROTNONE);
+ return pte_get_bits(pte, _PAGE_EXEC) &&
+ !pte_get_bits(pte, _PAGE_PROTNONE);
}
static inline int pte_write(pte_t pte)
diff --git a/arch/um/include/asm/ptrace-generic.h b/arch/um/include/asm/ptrace-generic.h
index 86d74f9d33cf..62e9916078ec 100644
--- a/arch/um/include/asm/ptrace-generic.h
+++ b/arch/um/include/asm/ptrace-generic.h
@@ -29,6 +29,12 @@ struct pt_regs {
#define PTRACE_OLDSETOPTIONS 21
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+#define PTRACE_GETFDPIC 31
+#define PTRACE_GETFDPIC_EXEC 0
+#define PTRACE_GETFDPIC_INTERP 1
+#endif
+
struct task_struct;
extern long subarch_ptrace(struct task_struct *child, long request,
diff --git a/arch/um/include/asm/tlbflush.h b/arch/um/include/asm/tlbflush.h
index 13a3009942be..f73b568a0859 100644
--- a/arch/um/include/asm/tlbflush.h
+++ b/arch/um/include/asm/tlbflush.h
@@ -30,6 +30,8 @@
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
*/
+#ifdef CONFIG_MMU
+
extern int um_tlb_sync(struct mm_struct *mm);
extern void flush_tlb_all(void);
@@ -56,4 +58,22 @@ static inline void flush_tlb_kernel_range(unsigned long start,
um_tlb_sync(&init_mm);
}
+#else /* !CONFIG_MMU */
+
+/*
+ * With NOMMU the kernel and userspace share a single host address space,
+ * so there is nothing to synchronise and all TLB flushes are no-ops.
+ */
+static inline int um_tlb_sync(struct mm_struct *mm) { return 0; }
+static inline void flush_tlb_all(void) { }
+static inline void flush_tlb_mm(struct mm_struct *mm) { }
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long address) { }
+static inline void flush_tlb_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end) { }
+static inline void flush_tlb_kernel_range(unsigned long start,
+ unsigned long end) { }
+
+#endif /* CONFIG_MMU */
+
#endif
diff --git a/arch/um/include/asm/uaccess.h b/arch/um/include/asm/uaccess.h
index 4417c8b1d37a..e493596f6663 100644
--- a/arch/um/include/asm/uaccess.h
+++ b/arch/um/include/asm/uaccess.h
@@ -18,6 +18,7 @@
#define __addr_range_nowrap(addr, size) \
((unsigned long) (addr) <= ((unsigned long) (addr) + (size)))
+#ifdef CONFIG_MMU
extern unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n);
extern unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n);
extern unsigned long __clear_user(void __user *mem, unsigned long len);
@@ -29,8 +30,6 @@ static inline int __access_ok(const void __user *ptr, unsigned long size);
#define INLINE_COPY_USER
-#include <asm-generic/uaccess.h>
-
static inline int __access_ok(const void __user *ptr, unsigned long size)
{
unsigned long addr = (unsigned long)ptr;
@@ -62,5 +61,8 @@ do { \
barrier(); \
current->thread.segv_continue = NULL; \
} while (0)
+#endif
+
+#include <asm-generic/uaccess.h>
#endif
diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h
index 02ef258e3395..7c4ccb6d9d37 100644
--- a/arch/um/include/shared/as-layout.h
+++ b/arch/um/include/shared/as-layout.h
@@ -29,6 +29,7 @@
#ifndef __ASSEMBLER__
+#include <linux/compiler_types.h>
#include <sysdep/ptrace.h>
struct task_struct;
@@ -46,7 +47,7 @@ extern unsigned long brk_start;
extern unsigned long stub_start;
-extern int linux_main(int argc, char **argv, char **envp);
+extern void __noreturn linux_main(int argc, char **argv, char **envp);
extern void uml_finishsetup(void);
struct siginfo;
diff --git a/arch/um/include/shared/kern_util.h b/arch/um/include/shared/kern_util.h
index 9812efd14ec0..7950d58a4ae8 100644
--- a/arch/um/include/shared/kern_util.h
+++ b/arch/um/include/shared/kern_util.h
@@ -6,6 +6,7 @@
#ifndef __KERN_UTIL_H__
#define __KERN_UTIL_H__
+#include <linux/compiler_types.h>
#include <sysdep/ptrace.h>
#include <sysdep/faultinfo.h>
@@ -37,7 +38,7 @@ extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs
extern void uml_pm_wake(void);
-extern int start_uml(void);
+extern void __noreturn start_uml(void);
extern void uml_cleanup(void);
extern void do_uml_exitcalls(void);
diff --git a/arch/um/include/shared/longjmp.h b/arch/um/include/shared/longjmp.h
index c53e43d980c8..b35ed756e3e4 100644
--- a/arch/um/include/shared/longjmp.h
+++ b/arch/um/include/shared/longjmp.h
@@ -2,11 +2,12 @@
#ifndef __UML_LONGJMP_H
#define __UML_LONGJMP_H
+#include <linux/compiler_types.h>
#include <sysdep/archsetjmp.h>
#include <os.h>
extern int setjmp(jmp_buf);
-extern void longjmp(jmp_buf, int);
+extern void __noreturn longjmp(jmp_buf, int);
#define UML_LONGJMP(buf, val) do { \
longjmp(*buf, val); \
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index b26e94292fc1..0067c5208c4c 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -15,6 +15,7 @@
#ifndef __UM_HOST__
#include <linux/types.h>
#else
+#include <stdbool.h>
#include <sys/types.h>
#endif
@@ -196,6 +197,9 @@ extern int create_mem_file(unsigned long long len);
/* tlb.c */
extern void report_enomem(void);
+/* main.c */
+extern void __noreturn os_exit(bool reboot);
+
/* process.c */
pid_t os_reap_child(void);
extern void os_alarm_process(int pid);
@@ -295,7 +299,7 @@ extern int start_userspace(struct mm_id *mm_id);
extern void userspace(struct uml_pt_regs *regs);
extern void new_thread(void *stack, jmp_buf *buf, void (*handler)(void));
extern void switch_threads(jmp_buf *me, jmp_buf *you);
-extern int start_idle_thread(void *stack, jmp_buf *switch_buf);
+extern void __noreturn start_idle_thread(void *stack, jmp_buf *switch_buf);
extern void initial_thread_cb_skas(void (*proc)(void *),
void *arg);
extern void halt_skas(void);
diff --git a/arch/um/include/shared/skas/stub-data.h b/arch/um/include/shared/skas/stub-data.h
index 27db38e95df9..1230a325debc 100644
--- a/arch/um/include/shared/skas/stub-data.h
+++ b/arch/um/include/shared/skas/stub-data.h
@@ -9,11 +9,20 @@
#define __STUB_DATA_H
#include <linux/compiler_types.h>
+#include <asm/unistd.h>
#include <as-layout.h>
#include <sysdep/tls.h>
#include <sysdep/stub-data.h>
#include <mm_id.h>
+#ifndef __NR_close_range
+# if defined(__alpha__)
+# define __NR_close_range 546
+# else
+# define __NR_close_range 436
+# endif
+#endif
+
#define FUTEX_IN_CHILD 0
#define FUTEX_IN_KERN 1
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile
index be60bc451b3f..b087b4278173 100644
--- a/arch/um/kernel/Makefile
+++ b/arch/um/kernel/Makefile
@@ -16,9 +16,13 @@ always-$(KBUILD_BUILTIN) := vmlinux.lds
obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \
physmem.o process.o ptrace.o reboot.o sigio.o \
- signal.o sysrq.o time.o tlb.o trap.o \
+ signal.o sysrq.o time.o trap.o \
um_arch.o umid.o kmsg_dump.o capflags.o skas/
obj-y += load_file.o
+obj-$(CONFIG_MMU) += mem-pgtable.o tlb.o trap-mmu.o
+ifneq ($(CONFIG_MMU),y)
+obj-y += trap-nommu.o
+endif
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
obj-$(CONFIG_GPROF) += gprof_syms.o
diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S
index ad3cefeff2ac..31adf9451507 100644
--- a/arch/um/kernel/dyn.lds.S
+++ b/arch/um/kernel/dyn.lds.S
@@ -20,9 +20,12 @@ SECTIONS
__binary_start = .;
. = ALIGN(4096); /* Init code and data */
_text = .;
+ __init_begin = .;
INIT_TEXT_SECTION(PAGE_SIZE)
+ init.data : { INIT_DATA }
. = ALIGN(PAGE_SIZE);
+ __init_end = .;
/* Read-only sections, merged into text segment: */
.hash : { *(.hash) }
@@ -98,10 +101,6 @@ SECTIONS
#include <asm/common.lds.S>
- __init_begin = .;
- init.data : { INIT_DATA }
- __init_end = .;
-
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
@@ -117,6 +116,7 @@ SECTIONS
.data : {
INIT_TASK_DATA(KERNEL_STACK_SIZE)
DATA_DATA
+ *(.ldata .ldata.[0-9a-zA-Z_]* .ldata..L*)
*(.data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
@@ -159,6 +159,7 @@ SECTIONS
__bss_start = .;
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
+ *(.lbss .lbss.[0-9a-zA-Z_]* .lbss..L*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
diff --git a/arch/um/kernel/mem-pgtable.c b/arch/um/kernel/mem-pgtable.c
new file mode 100644
index 000000000000..e33894431acd
--- /dev/null
+++ b/arch/um/kernel/mem-pgtable.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ */
+
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/memblock.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <as-layout.h>
+#include <init.h>
+#include <kern.h>
+#include <kern_util.h>
+#include <mem_user.h>
+#include <os.h>
+#include <um_malloc.h>
+
+
+/* Allocate and free page tables. */
+
+pgd_t *pgd_alloc(struct mm_struct *mm)
+{
+ pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL);
+
+ if (pgd) {
+ memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ memcpy(pgd + USER_PTRS_PER_PGD,
+ swapper_pg_dir + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ }
+ return pgd;
+}
+
+static const pgprot_t protection_map[16] = {
+ [VM_NONE] = PAGE_NONE,
+ [VM_READ] = PAGE_READONLY,
+ [VM_WRITE] = PAGE_COPY,
+ [VM_WRITE | VM_READ] = PAGE_COPY,
+ [VM_EXEC] = PAGE_READONLY_EXEC,
+ [VM_EXEC | VM_READ] = PAGE_READONLY_EXEC,
+ [VM_EXEC | VM_WRITE] = PAGE_COPY_EXEC,
+ [VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY_EXEC,
+ [VM_SHARED] = PAGE_NONE,
+ [VM_SHARED | VM_READ] = PAGE_READONLY,
+ [VM_SHARED | VM_WRITE] = PAGE_SHARED,
+ [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED,
+ [VM_SHARED | VM_EXEC] = PAGE_READONLY_EXEC,
+ [VM_SHARED | VM_EXEC | VM_READ] = PAGE_READONLY_EXEC,
+ [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_SHARED_EXEC,
+ [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED_EXEC
+};
+DECLARE_VM_GET_PAGE_PROT
diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c
index 1eef0e42ef5d..2219827ac3ad 100644
--- a/arch/um/kernel/mem.c
+++ b/arch/um/kernel/mem.c
@@ -6,7 +6,6 @@
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/memblock.h>
-#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -65,7 +64,8 @@ void __init arch_mm_preinit(void)
* to be turned on.
*/
brk_end = PAGE_ALIGN((unsigned long) sbrk(0));
- map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
+ map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1,
+ !IS_ENABLED(CONFIG_MMU));
memblock_free((void *)brk_end, uml_reserved - brk_end);
uml_reserved = brk_end;
min_low_pfn = PFN_UP(__pa(uml_reserved));
@@ -83,26 +83,17 @@ void __init arch_zone_limits_init(unsigned long *max_zone_pfns)
}
/*
- * This can't do anything because nothing in the kernel image can be freed
- * since it's not in kernel physical memory.
+ * We could munmap() this instead, but then libc allocations could
+ * land in this area and stray initdata access could erroneously
+ * succeed - just mprotect() it to reliably catch bad accesses.
*/
-
void free_initmem(void)
{
-}
+ unsigned long start = PAGE_ALIGN((unsigned long)__init_begin);
+ unsigned long end = round_down((unsigned long)__init_end, PAGE_SIZE);
-/* Allocate and free page tables. */
-
-pgd_t *pgd_alloc(struct mm_struct *mm)
-{
- pgd_t *pgd = __pgd_alloc(mm, 0);
-
- if (pgd)
- memcpy(pgd + USER_PTRS_PER_PGD,
- swapper_pg_dir + USER_PTRS_PER_PGD,
- (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
-
- return pgd;
+ if (end > start)
+ os_protect_memory((void *)start, end - start, 0, 0, 0);
}
void *uml_kmalloc(int size, int flags)
@@ -110,26 +101,6 @@ void *uml_kmalloc(int size, int flags)
return kmalloc(size, flags);
}
-static const pgprot_t protection_map[16] = {
- [VM_NONE] = PAGE_NONE,
- [VM_READ] = PAGE_READONLY,
- [VM_WRITE] = PAGE_COPY,
- [VM_WRITE | VM_READ] = PAGE_COPY,
- [VM_EXEC] = PAGE_READONLY,
- [VM_EXEC | VM_READ] = PAGE_READONLY,
- [VM_EXEC | VM_WRITE] = PAGE_COPY,
- [VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY,
- [VM_SHARED] = PAGE_NONE,
- [VM_SHARED | VM_READ] = PAGE_READONLY,
- [VM_SHARED | VM_WRITE] = PAGE_SHARED,
- [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED,
- [VM_SHARED | VM_EXEC] = PAGE_READONLY,
- [VM_SHARED | VM_EXEC | VM_READ] = PAGE_READONLY,
- [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_SHARED,
- [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED
-};
-DECLARE_VM_GET_PAGE_PROT
-
void mark_rodata_ro(void)
{
unsigned long rodata_start = PFN_ALIGN(__start_rodata);
diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c
index ae6ca373c261..ae9b8f3144b8 100644
--- a/arch/um/kernel/physmem.c
+++ b/arch/um/kernel/physmem.c
@@ -110,6 +110,13 @@ int phys_mapping(unsigned long phys, unsigned long long *offset_out)
}
EXPORT_SYMBOL(phys_mapping);
+void report_enomem(void)
+{
+ printk(KERN_ERR "UML ran out of memory on the host side! "
+ "This can happen due to a memory limitation or "
+ "vm.max_map_count has been reached.\n");
+}
+
static int __init uml_mem_setup(char *line, int *add)
{
char *retptr;
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index 63b38a3f73f7..dfca9e0aeec9 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -276,16 +276,12 @@ unsigned long arch_align_stack(unsigned long sp)
}
#endif
-unsigned long __get_wchan(struct task_struct *p)
+static unsigned long ___get_wchan(struct task_struct *p)
{
unsigned long stack_page, sp, ip;
bool seen_sched = 0;
stack_page = (unsigned long) task_stack_page(p);
- /* Bail if the process has no kernel stack for some reason */
- if (stack_page == 0)
- return 0;
-
sp = p->thread.switch_buf->JB_SP;
/*
* Bail if the stack pointer is below the bottom of the kernel
@@ -306,4 +302,21 @@ unsigned long __get_wchan(struct task_struct *p)
}
return 0;
+
+}
+
+unsigned long __get_wchan(struct task_struct *p)
+{
+ unsigned long ret;
+
+ /* Bail if the process has no kernel stack for some reason */
+ if (!try_get_task_stack(p))
+ return 0;
+
+ ret = ___get_wchan(p);
+
+ put_task_stack(p);
+
+ return ret;
+
}
diff --git a/arch/um/kernel/skas/Makefile b/arch/um/kernel/skas/Makefile
index 3384be42691f..2cf82eba52f8 100644
--- a/arch/um/kernel/skas/Makefile
+++ b/arch/um/kernel/skas/Makefile
@@ -3,8 +3,12 @@
# Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
#
-obj-y := stub.o mmu.o process.o syscall.o uaccess.o \
+obj-y := stub.o process.o syscall.o \
stub_exe_embed.o
+obj-$(CONFIG_MMU) += mmu.o uaccess.o
+ifneq ($(CONFIG_MMU),y)
+obj-y += nommu.o
+endif
# Stub executable
@@ -40,8 +44,14 @@ targets += stub_exe.dbg stub_exe $(stub_exe_objs-y)
CFLAGS_stub.o := $(CFLAGS_NO_HARDENING)
CFLAGS_stub_exe.o := $(CFLAGS_NO_HARDENING)
-# Clang will call memset() from __builtin_alloca() when stack variable
-# initialization is enabled, which is used in stub_exe.c.
+# stub_signal_restorer() must not touch stack pointer, clang doesn't
+# always omit the frame pointer automatically
+CFLAGS_stub.o += -fomit-frame-pointer
+CFLAGS_stub_exe.o += -fomit-frame-pointer
+
+# Clang's stack var initialisation can use memset() or similar and that
+# cannot be done for this code since we embed/move the stub
+CFLAGS_stub.o += $(call cc-option, -ftrivial-auto-var-init=uninitialized)
CFLAGS_stub_exe.o += $(call cc-option, -ftrivial-auto-var-init=uninitialized)
UNPROFILE_OBJS := stub.o stub_exe.o
diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c
index b5017096028b..facb88d1e393 100644
--- a/arch/um/kernel/skas/mmu.c
+++ b/arch/um/kernel/skas/mmu.c
@@ -12,6 +12,7 @@
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
#include <as-layout.h>
#include <os.h>
#include <skas.h>
@@ -40,6 +41,30 @@ void exit_turnstile(struct mm_id *mm_id)
mutex_unlock(__get_turnstile(mm_id));
}
+unsigned long current_stub_stack(void)
+{
+ if (current->mm == NULL)
+ return 0;
+
+ return current->mm->context.id.stack;
+}
+
+struct mm_id *current_mm_id(void)
+{
+ if (current->mm == NULL)
+ return NULL;
+
+ return &current->mm->context.id;
+}
+
+void current_mm_sync(void)
+{
+ if (current->mm == NULL)
+ return;
+
+ um_tlb_sync(current->mm);
+}
+
int init_new_context(struct task_struct *task, struct mm_struct *mm)
{
struct mm_id *new_id = &mm->context.id;
diff --git a/arch/um/kernel/skas/nommu.c b/arch/um/kernel/skas/nommu.c
new file mode 100644
index 000000000000..80d5c4eaa96b
--- /dev/null
+++ b/arch/um/kernel/skas/nommu.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/cpumask.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/mutex.h>
+#include <linux/panic.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
+#include <as-layout.h>
+#include <mem.h>
+#include <os.h>
+#include <skas.h>
+#include <mm_id.h>
+
+struct nommu_runner {
+ struct mm_id mm_id;
+ struct mutex turnstile;
+};
+
+static struct nommu_runner nommu_runners[NR_CPUS];
+
+static struct nommu_runner *this_runner(void)
+{
+ return &nommu_runners[raw_smp_processor_id()];
+}
+
+struct mutex *__get_turnstile(struct mm_id *mm_id)
+{
+ return &container_of(mm_id, struct nommu_runner, mm_id)->turnstile;
+}
+
+void enter_turnstile(struct mm_id *mm_id)
+{
+ mutex_lock(__get_turnstile(mm_id));
+}
+
+void exit_turnstile(struct mm_id *mm_id)
+{
+ mutex_unlock(__get_turnstile(mm_id));
+}
+
+unsigned long current_stub_stack(void)
+{
+ return this_runner()->mm_id.stack;
+}
+
+struct mm_id *current_mm_id(void)
+{
+ return &this_runner()->mm_id;
+}
+
+void current_mm_sync(void)
+{
+}
+
+static int __init nommu_start_runners(void)
+{
+ unsigned long long offset;
+ struct nommu_runner *r;
+ int cpu, err, fd;
+
+ fd = phys_mapping(uml_reserved - uml_physmem, &offset);
+
+ for_each_possible_cpu(cpu) {
+ r = &nommu_runners[cpu];
+ mutex_init(&r->turnstile);
+
+ r->mm_id.stack = __get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ ilog2(STUB_DATA_PAGES));
+ if (!r->mm_id.stack)
+ panic("OOM allocating userspace stack for CPU %d", cpu);
+
+ err = start_userspace(&r->mm_id);
+ if (err < 0)
+ panic("userspace startup for CPU %d failed: %d",
+ cpu, err);
+
+ map(&r->mm_id, uml_reserved, high_physmem - uml_reserved,
+ UM_PROT_READ | UM_PROT_WRITE | UM_PROT_EXEC, fd, offset);
+
+ err = syscall_stub_flush(&r->mm_id);
+ if (err < 0)
+ panic("physmem map/flush failed for CPU%d: %d",
+ cpu, err);
+ }
+
+ return 0;
+}
+arch_initcall(nommu_start_runners);
diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c
index 4a7673b0261a..f60541df209f 100644
--- a/arch/um/kernel/skas/process.c
+++ b/arch/um/kernel/skas/process.c
@@ -9,8 +9,6 @@
#include <linux/sched/task.h>
#include <linux/smp-internal.h>
-#include <asm/tlbflush.h>
-
#include <as-layout.h>
#include <kern.h>
#include <os.h>
@@ -29,7 +27,7 @@ static int __init start_kernel_proc(void *unused)
char cpu_irqstacks[NR_CPUS][THREAD_SIZE] __aligned(THREAD_SIZE);
-int __init start_uml(void)
+void __init start_uml(void)
{
stack_protections((unsigned long) &cpu_irqstacks[0]);
set_sigstack(cpu_irqstacks[0], THREAD_SIZE);
@@ -38,32 +36,8 @@ int __init start_uml(void)
init_task.thread.request.thread.proc = start_kernel_proc;
init_task.thread.request.thread.arg = NULL;
- return start_idle_thread(task_stack_page(&init_task),
- &init_task.thread.switch_buf);
-}
-
-unsigned long current_stub_stack(void)
-{
- if (current->mm == NULL)
- return 0;
-
- return current->mm->context.id.stack;
-}
-
-struct mm_id *current_mm_id(void)
-{
- if (current->mm == NULL)
- return NULL;
-
- return &current->mm->context.id;
-}
-
-void current_mm_sync(void)
-{
- if (current->mm == NULL)
- return;
-
- um_tlb_sync(current->mm);
+ start_idle_thread(task_stack_page(&init_task),
+ &init_task.thread.switch_buf);
}
static DEFINE_SPINLOCK(initial_jmpbuf_spinlock);
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index bc117733e8ef..914b90af42ed 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -35,12 +35,13 @@ void handle_syscall(struct uml_pt_regs *r)
/*
* If no time passes, then sched_yield may not actually yield, causing
* broken spinlock implementations in userspace (ASAN) to hang for long
- * periods of time.
+ * periods of time. Advance the time-travel clock a bit so sched_yield()
+ * in a loop (like ping does for small intervals) also works correctly.
*/
if ((time_travel_mode == TT_MODE_INFCPU ||
time_travel_mode == TT_MODE_EXTERNAL) &&
syscall == __NR_sched_yield)
- tt_extra_sched_jiffies += 1;
+ time_travel_ndelay(1000);
if (syscall >= 0 && syscall < __NR_syscalls) {
unsigned long ret;
diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
index caef1deef795..f48f0e7c2497 100644
--- a/arch/um/kernel/skas/uaccess.c
+++ b/arch/um/kernel/skas/uaccess.c
@@ -231,116 +231,90 @@ long strnlen_user(const char __user *str, long len)
}
EXPORT_SYMBOL(strnlen_user);
-/**
- * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
- * argument and comparison of the previous
- * futex value with another constant.
- *
- * @op: operation to execute
- * @oparg: argument to operation
- * @oval: old value at uaddr
- * @uaddr: pointer to user space address
- *
- * Return:
- * 0 - On success
- * -EFAULT - User access resulted in a page fault
- * -EAGAIN - Atomic operation was unable to complete due to contention
- * -ENOSYS - Operation not supported
- */
-
int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
- int oldval, ret;
- struct page *page;
unsigned long addr = (unsigned long) uaddr;
+ u32 *p, oldval, newval;
+ struct page *page;
pte_t *pte;
- ret = -EFAULT;
if (!access_ok(uaddr, sizeof(*uaddr)))
return -EFAULT;
- preempt_disable();
- pte = maybe_map(addr, 1);
- if (pte == NULL)
- goto out_inuser;
-
- page = pte_page(*pte);
-#ifdef CONFIG_64BIT
- pagefault_disable();
- addr = (unsigned long) page_address(page) +
- (((unsigned long) addr) & ~PAGE_MASK);
-#else
- addr = (unsigned long) kmap_atomic(page) +
- ((unsigned long) addr & ~PAGE_MASK);
-#endif
- uaddr = (u32 *) addr;
- oldval = *uaddr;
-
- ret = 0;
switch (op) {
case FUTEX_OP_SET:
- *uaddr = oparg;
- break;
case FUTEX_OP_ADD:
- *uaddr += oparg;
- break;
case FUTEX_OP_OR:
- *uaddr |= oparg;
- break;
case FUTEX_OP_ANDN:
- *uaddr &= ~oparg;
- break;
case FUTEX_OP_XOR:
- *uaddr ^= oparg;
break;
default:
- ret = -ENOSYS;
+ return -ENOSYS;
+ }
+
+ preempt_disable();
+ pte = maybe_map(addr, 1);
+ if (pte == NULL) {
+ preempt_enable();
+ return -EFAULT;
}
+
+ page = pte_page(*pte);
#ifdef CONFIG_64BIT
- pagefault_enable();
+ pagefault_disable();
+ p = page_address(page) + (addr & ~PAGE_MASK);
#else
- kunmap_atomic((void *)addr);
+ p = kmap_atomic(page) + (addr & ~PAGE_MASK);
#endif
-out_inuser:
- preempt_enable();
+ do {
+ oldval = READ_ONCE(*p);
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ newval = oparg;
+ break;
+ case FUTEX_OP_ADD:
+ newval = oldval + oparg;
+ break;
+ case FUTEX_OP_OR:
+ newval = oldval | oparg;
+ break;
+ case FUTEX_OP_ANDN:
+ newval = oldval & ~oparg;
+ break;
+ case FUTEX_OP_XOR:
+ newval = oldval ^ oparg;
+ break;
+ }
+ } while (!try_cmpxchg(p, &oldval, newval));
- if (ret == 0)
- *oval = oldval;
+ *oval = oldval;
- return ret;
+#ifdef CONFIG_64BIT
+ pagefault_enable();
+#else
+ kunmap_atomic((void *)addr);
+#endif
+ preempt_enable();
+ return 0;
}
-EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
-
-/**
- * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
- * uaddr with newval if the current value is
- * oldval.
- * @uval: pointer to store content of @uaddr
- * @uaddr: pointer to user space address
- * @oldval: old value
- * @newval: new value to store to @uaddr
- *
- * Return:
- * 0 - On success
- * -EFAULT - User access resulted in a page fault
- * -EAGAIN - Atomic operation was unable to complete due to contention
- */
int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
- u32 oldval, u32 newval)
+ u32 oldval, u32 newval)
{
struct page *page;
pte_t *pte;
- int ret = -EFAULT;
if (!access_ok(uaddr, sizeof(*uaddr)))
return -EFAULT;
preempt_disable();
pte = maybe_map((unsigned long) uaddr, 1);
- if (pte == NULL)
- goto out_inatomic;
+ if (pte == NULL) {
+ preempt_enable();
+ return -EFAULT;
+ }
page = pte_page(*pte);
#ifdef CONFIG_64BIT
@@ -350,19 +324,13 @@ int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK);
#endif
- *uval = *uaddr;
-
- ret = cmpxchg(uaddr, oldval, newval);
+ *uval = cmpxchg(uaddr, oldval, newval);
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic(uaddr);
#endif
- ret = 0;
-
-out_inatomic:
preempt_enable();
- return ret;
+ return 0;
}
-EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);
diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c
index f1e52b7348fb..92ea1beaed8b 100644
--- a/arch/um/kernel/smp.c
+++ b/arch/um/kernel/smp.c
@@ -20,6 +20,7 @@
#include <init.h>
#include <kern.h>
#include <os.h>
+#include <skas.h>
#include <smp.h>
enum {
@@ -66,7 +67,7 @@ static void ipi_handler(int vector, struct uml_pt_regs *regs)
irq_enter();
if (current->mm)
- os_alarm_process(current->mm->context.id.pid);
+ os_alarm_process(current_mm_id()->pid);
switch (vector) {
case UML_IPI_RES:
diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c
index 13ee5666668d..5a2c28e09294 100644
--- a/arch/um/kernel/sysrq.c
+++ b/arch/um/kernel/sysrq.c
@@ -18,7 +18,7 @@ static void _print_addr(void *data, unsigned long address, int reliable)
{
const char *loglvl = data;
- printk("%s [<%08lx>] %s%pS\n", loglvl, address, reliable ? "" : "? ",
+ printk("%s [<%08lx>] %s%pB\n", loglvl, address, reliable ? "" : "? ",
(void *)address);
}
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index b344a36b44eb..d620b6d6310a 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -19,6 +19,7 @@
#include <asm/param.h>
#include <kern_util.h>
#include <os.h>
+#include <skas.h>
#include <linux/delay.h>
#include <linux/time-internal.h>
#include <linux/um_timetravel.h>
@@ -873,7 +874,7 @@ static irqreturn_t um_timer(int irq, void *dev)
if (time_travel_mode != TT_MODE_INFCPU &&
time_travel_mode != TT_MODE_EXTERNAL &&
get_current()->mm)
- os_alarm_process(get_current()->mm->context.id.pid);
+ os_alarm_process(current_mm_id()->pid);
evt->event_handler(evt);
diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c
index 1f175716b474..012bcc76168d 100644
--- a/arch/um/kernel/tlb.c
+++ b/arch/um/kernel/tlb.c
@@ -40,13 +40,6 @@ static int kern_unmap(struct mm_id *mm_idp,
return os_unmap_memory((void *)virt, len);
}
-void report_enomem(void)
-{
- printk(KERN_ERR "UML ran out of memory on the host side! "
- "This can happen due to a memory limitation or "
- "vm.max_map_count has been reached.\n");
-}
-
static inline int update_pte_range(pmd_t *pmd, unsigned long addr,
unsigned long end,
struct vm_ops *ops)
diff --git a/arch/um/kernel/trap-mmu.c b/arch/um/kernel/trap-mmu.c
new file mode 100644
index 000000000000..467c100aafde
--- /dev/null
+++ b/arch/um/kernel/trap-mmu.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ */
+
+#include <linux/mm.h>
+#include <linux/sched/signal.h>
+#include <linux/hardirq.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/sched/debug.h>
+#include <asm/current.h>
+#include <asm/tlbflush.h>
+#include <arch.h>
+#include <as-layout.h>
+#include <kern_util.h>
+#include <os.h>
+#include <skas.h>
+
+/*
+ * NOTE: UML does not have exception tables. As such, this is almost a copy
+ * of the code in mm/memory.c, only adjusting the logic to simply check whether
+ * we are coming from the kernel instead of doing an additional lookup in the
+ * exception table.
+ * We can do this simplification because we never get here if the exception was
+ * fixable.
+ */
+static inline bool get_mmap_lock_carefully(struct mm_struct *mm, bool is_user)
+{
+ if (likely(mmap_read_trylock(mm)))
+ return true;
+
+ if (!is_user)
+ return false;
+
+ return !mmap_read_lock_killable(mm);
+}
+
+static inline bool mmap_upgrade_trylock(struct mm_struct *mm)
+{
+ /*
+ * We don't have this operation yet.
+ *
+ * It should be easy enough to do: it's basically a
+ * atomic_long_try_cmpxchg_acquire()
+ * from RWSEM_READER_BIAS -> RWSEM_WRITER_LOCKED, but
+ * it also needs the proper lockdep magic etc.
+ */
+ return false;
+}
+
+static inline bool upgrade_mmap_lock_carefully(struct mm_struct *mm, bool is_user)
+{
+ mmap_read_unlock(mm);
+ if (!is_user)
+ return false;
+
+ return !mmap_write_lock_killable(mm);
+}
+
+/*
+ * Helper for page fault handling.
+ *
+ * This is kind of equivalend to "mmap_read_lock()" followed
+ * by "find_extend_vma()", except it's a lot more careful about
+ * the locking (and will drop the lock on failure).
+ *
+ * For example, if we have a kernel bug that causes a page
+ * fault, we don't want to just use mmap_read_lock() to get
+ * the mm lock, because that would deadlock if the bug were
+ * to happen while we're holding the mm lock for writing.
+ *
+ * So this checks the exception tables on kernel faults in
+ * order to only do this all for instructions that are actually
+ * expected to fault.
+ *
+ * We can also actually take the mm lock for writing if we
+ * need to extend the vma, which helps the VM layer a lot.
+ */
+static struct vm_area_struct *
+um_lock_mm_and_find_vma(struct mm_struct *mm,
+ unsigned long addr, bool is_user)
+{
+ struct vm_area_struct *vma;
+
+ if (!get_mmap_lock_carefully(mm, is_user))
+ return NULL;
+
+ vma = find_vma(mm, addr);
+ if (likely(vma && (vma->vm_start <= addr)))
+ return vma;
+
+ /*
+ * Well, dang. We might still be successful, but only
+ * if we can extend a vma to do so.
+ */
+ if (!vma || !(vma->vm_flags & VM_GROWSDOWN)) {
+ mmap_read_unlock(mm);
+ return NULL;
+ }
+
+ /*
+ * We can try to upgrade the mmap lock atomically,
+ * in which case we can continue to use the vma
+ * we already looked up.
+ *
+ * Otherwise we'll have to drop the mmap lock and
+ * re-take it, and also look up the vma again,
+ * re-checking it.
+ */
+ if (!mmap_upgrade_trylock(mm)) {
+ if (!upgrade_mmap_lock_carefully(mm, is_user))
+ return NULL;
+
+ vma = find_vma(mm, addr);
+ if (!vma)
+ goto fail;
+ if (vma->vm_start <= addr)
+ goto success;
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ goto fail;
+ }
+
+ if (expand_stack_locked(vma, addr))
+ goto fail;
+
+success:
+ mmap_write_downgrade(mm);
+ return vma;
+
+fail:
+ mmap_write_unlock(mm);
+ return NULL;
+}
+
+/*
+ * Note this is constrained to return 0, -EFAULT, -EACCES, -ENOMEM by
+ * segv().
+ */
+int handle_page_fault(unsigned long address, unsigned long ip,
+ int is_write, int is_user, int *code_out)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ pmd_t *pmd;
+ pte_t *pte;
+ int err = -EFAULT;
+ unsigned int flags = FAULT_FLAG_DEFAULT;
+
+ *code_out = SEGV_MAPERR;
+
+ /*
+ * If the fault was with pagefaults disabled, don't take the fault, just
+ * fail.
+ */
+ if (faulthandler_disabled())
+ goto out_nosemaphore;
+
+ if (is_user)
+ flags |= FAULT_FLAG_USER;
+retry:
+ vma = um_lock_mm_and_find_vma(mm, address, is_user);
+ if (!vma)
+ goto out_nosemaphore;
+
+ *code_out = SEGV_ACCERR;
+ if (is_write) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto out;
+ flags |= FAULT_FLAG_WRITE;
+ } else {
+ /* Don't require VM_READ|VM_EXEC for write faults! */
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto out;
+ }
+
+ do {
+ vm_fault_t fault;
+
+ fault = handle_mm_fault(vma, address, flags, NULL);
+
+ if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+ goto out_nosemaphore;
+
+ /* The fault is fully completed (including releasing mmap lock) */
+ if (fault & VM_FAULT_COMPLETED)
+ return 0;
+
+ if (unlikely(fault & VM_FAULT_ERROR)) {
+ if (fault & VM_FAULT_OOM) {
+ goto out_of_memory;
+ } else if (fault & VM_FAULT_SIGSEGV) {
+ goto out;
+ } else if (fault & VM_FAULT_SIGBUS) {
+ err = -EACCES;
+ goto out;
+ }
+ BUG();
+ }
+ if (fault & VM_FAULT_RETRY) {
+ flags |= FAULT_FLAG_TRIED;
+
+ goto retry;
+ }
+
+ pmd = pmd_off(mm, address);
+ pte = pte_offset_kernel(pmd, address);
+ } while (!pte_present(*pte));
+ err = 0;
+ /*
+ * The below warning was added in place of
+ * pte_mkyoung(); if (is_write) pte_mkdirty();
+ * If it's triggered, we'd see normally a hang here (a clean pte is
+ * marked read-only to emulate the dirty bit).
+ * However, the generic code can mark a PTE writable but clean on a
+ * concurrent read fault, triggering this harmlessly. So comment it out.
+ */
+#if 0
+ WARN_ON(!pte_young(*pte) || (is_write && !pte_dirty(*pte)));
+#endif
+
+out:
+ mmap_read_unlock(mm);
+out_nosemaphore:
+ return err;
+
+out_of_memory:
+ /*
+ * We ran out of memory, call the OOM killer, and return the userspace
+ * (which will retry the fault, or kill us if we got oom-killed).
+ */
+ mmap_read_unlock(mm);
+ if (!is_user)
+ goto out_nosemaphore;
+ pagefault_out_of_memory();
+ return 0;
+}
diff --git a/arch/um/kernel/trap-nommu.c b/arch/um/kernel/trap-nommu.c
new file mode 100644
index 000000000000..891a9cefa935
--- /dev/null
+++ b/arch/um/kernel/trap-nommu.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <shared/kern_util.h>
+
+int handle_page_fault(unsigned long address, unsigned long ip,
+ int is_write, int is_user, int *code_out)
+{
+ /* everything that's valid is already mapped in NOMMU */
+ *code_out = SEGV_MAPERR;
+ return -EFAULT;
+}
diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c
index 177615820a4c..ab60bab8d987 100644
--- a/arch/um/kernel/trap.c
+++ b/arch/um/kernel/trap.c
@@ -17,225 +17,6 @@
#include <os.h>
#include <skas.h>
-/*
- * NOTE: UML does not have exception tables. As such, this is almost a copy
- * of the code in mm/memory.c, only adjusting the logic to simply check whether
- * we are coming from the kernel instead of doing an additional lookup in the
- * exception table.
- * We can do this simplification because we never get here if the exception was
- * fixable.
- */
-static inline bool get_mmap_lock_carefully(struct mm_struct *mm, bool is_user)
-{
- if (likely(mmap_read_trylock(mm)))
- return true;
-
- if (!is_user)
- return false;
-
- return !mmap_read_lock_killable(mm);
-}
-
-static inline bool mmap_upgrade_trylock(struct mm_struct *mm)
-{
- /*
- * We don't have this operation yet.
- *
- * It should be easy enough to do: it's basically a
- * atomic_long_try_cmpxchg_acquire()
- * from RWSEM_READER_BIAS -> RWSEM_WRITER_LOCKED, but
- * it also needs the proper lockdep magic etc.
- */
- return false;
-}
-
-static inline bool upgrade_mmap_lock_carefully(struct mm_struct *mm, bool is_user)
-{
- mmap_read_unlock(mm);
- if (!is_user)
- return false;
-
- return !mmap_write_lock_killable(mm);
-}
-
-/*
- * Helper for page fault handling.
- *
- * This is kind of equivalend to "mmap_read_lock()" followed
- * by "find_extend_vma()", except it's a lot more careful about
- * the locking (and will drop the lock on failure).
- *
- * For example, if we have a kernel bug that causes a page
- * fault, we don't want to just use mmap_read_lock() to get
- * the mm lock, because that would deadlock if the bug were
- * to happen while we're holding the mm lock for writing.
- *
- * So this checks the exception tables on kernel faults in
- * order to only do this all for instructions that are actually
- * expected to fault.
- *
- * We can also actually take the mm lock for writing if we
- * need to extend the vma, which helps the VM layer a lot.
- */
-static struct vm_area_struct *
-um_lock_mm_and_find_vma(struct mm_struct *mm,
- unsigned long addr, bool is_user)
-{
- struct vm_area_struct *vma;
-
- if (!get_mmap_lock_carefully(mm, is_user))
- return NULL;
-
- vma = find_vma(mm, addr);
- if (likely(vma && (vma->vm_start <= addr)))
- return vma;
-
- /*
- * Well, dang. We might still be successful, but only
- * if we can extend a vma to do so.
- */
- if (!vma || !(vma->vm_flags & VM_GROWSDOWN)) {
- mmap_read_unlock(mm);
- return NULL;
- }
-
- /*
- * We can try to upgrade the mmap lock atomically,
- * in which case we can continue to use the vma
- * we already looked up.
- *
- * Otherwise we'll have to drop the mmap lock and
- * re-take it, and also look up the vma again,
- * re-checking it.
- */
- if (!mmap_upgrade_trylock(mm)) {
- if (!upgrade_mmap_lock_carefully(mm, is_user))
- return NULL;
-
- vma = find_vma(mm, addr);
- if (!vma)
- goto fail;
- if (vma->vm_start <= addr)
- goto success;
- if (!(vma->vm_flags & VM_GROWSDOWN))
- goto fail;
- }
-
- if (expand_stack_locked(vma, addr))
- goto fail;
-
-success:
- mmap_write_downgrade(mm);
- return vma;
-
-fail:
- mmap_write_unlock(mm);
- return NULL;
-}
-
-/*
- * Note this is constrained to return 0, -EFAULT, -EACCES, -ENOMEM by
- * segv().
- */
-int handle_page_fault(unsigned long address, unsigned long ip,
- int is_write, int is_user, int *code_out)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- pmd_t *pmd;
- pte_t *pte;
- int err = -EFAULT;
- unsigned int flags = FAULT_FLAG_DEFAULT;
-
- *code_out = SEGV_MAPERR;
-
- /*
- * If the fault was with pagefaults disabled, don't take the fault, just
- * fail.
- */
- if (faulthandler_disabled())
- goto out_nosemaphore;
-
- if (is_user)
- flags |= FAULT_FLAG_USER;
-retry:
- vma = um_lock_mm_and_find_vma(mm, address, is_user);
- if (!vma)
- goto out_nosemaphore;
-
- *code_out = SEGV_ACCERR;
- if (is_write) {
- if (!(vma->vm_flags & VM_WRITE))
- goto out;
- flags |= FAULT_FLAG_WRITE;
- } else {
- /* Don't require VM_READ|VM_EXEC for write faults! */
- if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
- goto out;
- }
-
- do {
- vm_fault_t fault;
-
- fault = handle_mm_fault(vma, address, flags, NULL);
-
- if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
- goto out_nosemaphore;
-
- /* The fault is fully completed (including releasing mmap lock) */
- if (fault & VM_FAULT_COMPLETED)
- return 0;
-
- if (unlikely(fault & VM_FAULT_ERROR)) {
- if (fault & VM_FAULT_OOM) {
- goto out_of_memory;
- } else if (fault & VM_FAULT_SIGSEGV) {
- goto out;
- } else if (fault & VM_FAULT_SIGBUS) {
- err = -EACCES;
- goto out;
- }
- BUG();
- }
- if (fault & VM_FAULT_RETRY) {
- flags |= FAULT_FLAG_TRIED;
-
- goto retry;
- }
-
- pmd = pmd_off(mm, address);
- pte = pte_offset_kernel(pmd, address);
- } while (!pte_present(*pte));
- err = 0;
- /*
- * The below warning was added in place of
- * pte_mkyoung(); if (is_write) pte_mkdirty();
- * If it's triggered, we'd see normally a hang here (a clean pte is
- * marked read-only to emulate the dirty bit).
- * However, the generic code can mark a PTE writable but clean on a
- * concurrent read fault, triggering this harmlessly. So comment it out.
- */
-#if 0
- WARN_ON(!pte_young(*pte) || (is_write && !pte_dirty(*pte)));
-#endif
-
-out:
- mmap_read_unlock(mm);
-out_nosemaphore:
- return err;
-
-out_of_memory:
- /*
- * We ran out of memory, call the OOM killer, and return the userspace
- * (which will retry the fault, or kill us if we got oom-killed).
- */
- mmap_read_unlock(mm);
- if (!is_user)
- goto out_nosemaphore;
- pagefault_out_of_memory();
- return 0;
-}
-
static void show_segv_info(struct uml_pt_regs *regs)
{
struct task_struct *tsk = current;
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index e4ee693961e4..3dbe3acc4833 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -44,7 +44,12 @@ static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 };
static void __init add_arg(char *arg)
{
- if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) {
+ size_t len = strlen(command_line) + strlen(arg) + 1;
+
+ if (command_line[0])
+ len++;
+
+ if (len > COMMAND_LINE_SIZE) {
os_warn("add_arg: Too many command line arguments!\n");
exit(1);
}
@@ -303,7 +308,7 @@ static unsigned long __init get_top_address(char **envp)
return PAGE_ALIGN(top_addr + 1);
}
-int __init linux_main(int argc, char **argv, char **envp)
+void __init linux_main(int argc, char **argv, char **envp)
{
unsigned long avail, diff;
unsigned long virtmem_size, max_physmem;
@@ -396,7 +401,7 @@ int __init linux_main(int argc, char **argv, char **envp)
os_flush_stdout();
- return start_uml();
+ start_uml();
}
int __init __weak read_initrd(void)
diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S
index 30aa24348d60..f6e100143558 100644
--- a/arch/um/kernel/uml.lds.S
+++ b/arch/um/kernel/uml.lds.S
@@ -28,8 +28,13 @@ SECTIONS
. = ALIGN(PAGE_SIZE);
_text = .;
+ __init_begin = .;
INIT_TEXT_SECTION(0)
+ init.data : { INIT_DATA }
+ . = ALIGN(PAGE_SIZE);
+ __init_end = .;
+
.text :
{
_stext = .;
@@ -70,14 +75,11 @@ SECTIONS
#include <asm/common.lds.S>
- __init_begin = .;
- init.data : { INIT_DATA }
- __init_end = .;
-
.data :
{
INIT_TASK_DATA(KERNEL_STACK_SIZE)
DATA_DATA
+ *(.ldata .ldata.[0-9a-zA-Z_]* .ldata..L*)
*(.gnu.linkonce.d*)
CONSTRUCTORS
}
@@ -107,6 +109,7 @@ SECTIONS
PROVIDE(_bss_start = .);
SBSS(0)
BSS(0)
+ .lbss : { *(.lbss .lbss.[0-9a-zA-Z_]* .lbss..L*) }
__bss_stop = .;
_end = .;
PROVIDE (end = .);
diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c
index 7e114862a723..ea41188ef2d0 100644
--- a/arch/um/os-Linux/main.c
+++ b/arch/um/os-Linux/main.c
@@ -100,10 +100,11 @@ static void __init setup_env_path(void)
}
}
+static char **new_argv;
+
int __init main(int argc, char **argv, char **envp)
{
- char **new_argv;
- int ret, i, err;
+ int ret, i;
/* Disable randomization and re-exec if it was changed successfully */
ret = personality(PER_LINUX | ADDR_NO_RANDOMIZE);
@@ -150,7 +151,12 @@ int __init main(int argc, char **argv, char **envp)
scan_elf_aux(envp);
change_sig(SIGPIPE, 0);
- ret = linux_main(argc, argv, envp);
+ linux_main(argc, argv, envp);
+}
+
+void os_exit(bool reboot)
+{
+ int err;
/*
* Disable SIGPROF - I have no idea why libc doesn't do this or turn
@@ -183,12 +189,11 @@ int __init main(int argc, char **argv, char **envp)
os_info("\n");
/* Reboot */
- if (ret) {
+ if (reboot) {
execvp(new_argv[0], new_argv);
perror("Failed to exec kernel");
- ret = 1;
}
- return uml_exitcode;
+ exit(uml_exitcode);
}
extern void *__real_malloc(int);
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index 6c993bc8c78e..57f0a5e46afb 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -453,7 +453,7 @@ void unblock_signals_hard(void)
* call _within_ e.g. vu_req_read_message(), where we observed this
* issue, which loops. Thus, if the inner call handles the recorded
* pending signals, we can get out of the inner call with the real
- * signal hander no longer blocked, and still have a race. Thus don't
+ * signal handler no longer blocked, and still have a race. Thus don't
* handle unblocking in the inner call, if it happens, but only in
* the outermost call - 'unblocking' serves as an ownership for the
* signals_blocked_pending decrement.
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index d6c22f8aa06d..bca05a5a7a83 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -816,7 +816,7 @@ static __thread void (*cb_proc)(void *arg);
static __thread void *cb_arg;
static __thread jmp_buf *cb_back;
-int start_idle_thread(void *stack, jmp_buf *switch_buf)
+void start_idle_thread(void *stack, jmp_buf *switch_buf)
{
int n;
@@ -843,21 +843,16 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf)
break;
case INIT_JMP_HALT:
kmalloc_ok = 0;
- return 0;
+ os_exit(false);
case INIT_JMP_REBOOT:
kmalloc_ok = 0;
- return 1;
+ os_exit(true);
default:
printk(UM_KERN_ERR "Bad sigsetjmp return in %s - %d\n",
__func__, n);
fatal_sigsegv();
}
longjmp(*switch_buf, 1);
-
- /* unreachable */
- printk(UM_KERN_ERR "impossible long jump!");
- fatal_sigsegv();
- return 0;
}
void initial_thread_cb_skas(void (*proc)(void *), void *arg)
diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile
index f9ea75bf43ac..100b68f601f9 100644
--- a/arch/x86/um/Makefile
+++ b/arch/x86/um/Makefile
@@ -47,4 +47,7 @@ include/generated/user_constants.h: $(obj)/user-offsets.s FORCE
UNPROFILE_OBJS := stub_segv.o
CFLAGS_stub_segv.o := $(CFLAGS_NO_HARDENING)
+# see arch/um/kernel/skas/Makefile - this goes into the stub
+CFLAGS_stub_segv.o += $(call cc-option, -ftrivial-auto-var-init=uninitialized)
+
include $(srctree)/arch/um/scripts/Makefile.rules
diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h
index 22d0111b543b..388fe669886c 100644
--- a/arch/x86/um/asm/elf.h
+++ b/arch/x86/um/asm/elf.h
@@ -9,6 +9,7 @@
#include <skas.h>
#define CORE_DUMP_USE_REGSET
+#define ELF_FDPIC_CORE_EFLAGS 0
#ifdef CONFIG_X86_32
@@ -158,8 +159,11 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm,
extern unsigned long um_vdso_addr;
#define AT_SYSINFO_EHDR 33
-#define ARCH_DLINFO NEW_AUX_ENT(AT_SYSINFO_EHDR, um_vdso_addr)
-
+#define ARCH_DLINFO \
+do { \
+ NEW_AUX_ENT(AT_SYSINFO_EHDR, um_vdso_addr); \
+ NEW_AUX_ENT(AT_MINSIGSTKSZ, 0); \
+} while (0)
#endif
typedef unsigned long elf_greg_t;
diff --git a/arch/x86/um/asm/required-features.h b/arch/x86/um/asm/required-features.h
deleted file mode 100644
index dfb967b2d2f3..000000000000
--- a/arch/x86/um/asm/required-features.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef __UM_REQUIRED_FEATURES_H
-#define __UM_REQUIRED_FEATURES_H
-
-/*
- * Nothing to see, just need something for the i386 and x86_64 asm
- * headers to include.
- */
-
-#endif
diff --git a/arch/x86/um/vdso/vma.c b/arch/x86/um/vdso/vma.c
index 51a2b9f2eca9..8e1f5ee44f4e 100644
--- a/arch/x86/um/vdso/vma.c
+++ b/arch/x86/um/vdso/vma.c
@@ -9,6 +9,7 @@
#include <asm/page.h>
#include <asm/elf.h>
#include <linux/init.h>
+#include <os.h>
unsigned long um_vdso_addr;
static struct page *um_vdso;
@@ -20,18 +21,25 @@ static int __init init_vdso(void)
{
BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
- um_vdso_addr = task_size - PAGE_SIZE;
-
um_vdso = alloc_page(GFP_KERNEL);
if (!um_vdso)
panic("Cannot allocate vdso\n");
copy_page(page_address(um_vdso), vdso_start);
+#ifdef CONFIG_MMU
+ um_vdso_addr = task_size - PAGE_SIZE;
+#else
+ /* this is fine with NOMMU as everything is accessible */
+ um_vdso_addr = (unsigned long)page_address(um_vdso);
+ os_protect_memory((void *)um_vdso_addr, vdso_end - vdso_start, 1, 0, 1);
+#endif
+
return 0;
}
subsys_initcall(init_vdso);
+#ifdef CONFIG_MMU
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct vm_area_struct *vma;
@@ -53,3 +61,4 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
return IS_ERR(vma) ? PTR_ERR(vma) : 0;
}
+#endif /* CONFIG_MMU */
diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index daeac4889d03..e36cd94513b9 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -58,7 +58,7 @@ config ARCH_USE_GNU_PROPERTY
config BINFMT_ELF_FDPIC
bool "Kernel support for FDPIC ELF binaries"
default y if !BINFMT_ELF
- depends on ARM || ((M68K || RISCV || SUPERH || XTENSA) && !MMU)
+ depends on ARM || ((M68K || RISCV || SUPERH || UML || XTENSA) && !MMU)
select ELFCORE
help
ELF FDPIC binaries are based on ELF, but allow the individual load
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index f28d8990d2bb..4dd9649fa049 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -649,7 +649,7 @@ menu "Generic Kernel Debugging Instruments"
config MAGIC_SYSRQ
bool "Magic SysRq key"
- depends on !UML
+ depends on !UML || MCONSOLE
help
If you say Y here, you will have some control over the system even
if the system crashes for example during kernel debugging (e.g., you
@@ -657,9 +657,11 @@ config MAGIC_SYSRQ
immediately or dump some status information). This is accomplished
by pressing various keys while holding SysRq (Alt+PrintScreen). It
also works on a serial console (on PC hardware at least), if you
- send a BREAK and then within 5 seconds a command keypress. The
- keys are documented in <file:Documentation/admin-guide/sysrq.rst>.
- Don't say Y unless you really know what this hack does.
+ send a BREAK and then within 5 seconds a command keypress. On UML,
+ this is accomplished by sending a "sysrq" command with mconsole,
+ followed by the letter for the requested command. The keys are
+ documented in <file:Documentation/admin-guide/sysrq.rst>. Don't say
+ Y unless you really know what this hack does.
config MAGIC_SYSRQ_DEFAULT_ENABLE
hex "Enable magic SysRq key functions by default"
diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig
index 94ff8e4089bf..cbda6189a308 100644
--- a/lib/kunit/Kconfig
+++ b/lib/kunit/Kconfig
@@ -133,7 +133,7 @@ config KUNIT_DEFAULT_TIMEOUT
config KUNIT_UML_PCI
bool "KUnit UML PCI Support"
- depends on UML
+ depends on UML && MMU
select UML_PCI
help
Enables the PCI subsystem on UML for use by KUnit tests.