* [PULL 00/13] user patch queue
@ 2026-07-10 23:01 Richard Henderson
2026-07-10 23:01 ` [PULL 01/13] linux-user: Introduce PGBRange Richard Henderson
` (13 more replies)
0 siblings, 14 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel
The following changes since commit ab2056a0b7c944b16b224a8feabd99023772ae91:
Merge tag 'pull-ufs-20260709' of https://gitlab.com/jeuk20.kim/qemu into staging (2026-07-09 12:04:14 +0200)
are available in the Git repository at:
https://gitlab.com/rth7680/qemu.git tags/pull-lu-20260710
for you to fetch changes up to de1a2257117fc893d0703a154683b05f7bee0b12:
common-user: Move guest_base, have_guest_base to probe-guest-base.c (2026-07-10 14:40:42 -0700)
----------------------------------------------------------------
Handle loading of ET_EXEC binaries at address 0.
Share probe_guest_base code between linux-user and bsd-user.
----------------------------------------------------------------
Richard Henderson (13):
linux-user: Introduce PGBRange
linux-user: Use PGBRange in load_elf_image
linux-user: Drop hiaddr out-of-range check in probe_guest_base
linux-user: Pass image_range to probe_guest_base
linux-user: Use PGBRange for commpage
common-user: Move mmap_min_addr from linux-user
common-user: Initialize mmap_min_addr for FreeBSD
include/user/guest-host: Include missing cpu.h
common-user: Move selfmap from util
common-user: Implement read_self_maps for FreeBSD
common-user: Move probe_guest_base from linux-user
bsd-user: Use probe_guest_base
common-user: Move guest_base, have_guest_base to probe-guest-base.c
include/user/guest-host.h | 1 +
include/user/mmap-min-addr.h | 12 ++
include/user/probe-guest-base.h | 37 ++++
include/{qemu => user}/selfmap.h | 0
linux-user/arm/target_elf.h | 2 +-
linux-user/hppa/target_elf.h | 2 +-
linux-user/user-internals.h | 17 +-
bsd-user/elfload.c | 37 ++--
bsd-user/main.c | 42 +---
common-user/mmap-min-addr.c | 50 +++++
common-user/probe-guest-base.c | 347 +++++++++++++++++++++++++++++++++
{util => common-user}/selfmap.c | 49 ++++-
linux-user/arm/elfload.c | 2 +-
linux-user/elfload.c | 400 +++------------------------------------
linux-user/flatload.c | 3 +-
linux-user/hppa/elfload.c | 4 +-
linux-user/main.c | 35 +---
linux-user/mmap.c | 1 +
linux-user/syscall.c | 2 +-
common-user/meson.build | 3 +
util/meson.build | 4 -
21 files changed, 564 insertions(+), 486 deletions(-)
create mode 100644 include/user/mmap-min-addr.h
create mode 100644 include/user/probe-guest-base.h
rename include/{qemu => user}/selfmap.h (100%)
create mode 100644 common-user/mmap-min-addr.c
create mode 100644 common-user/probe-guest-base.c
rename {util => common-user}/selfmap.c (68%)
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PULL 01/13] linux-user: Introduce PGBRange
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 02/13] linux-user: Use PGBRange in load_elf_image Richard Henderson
` (12 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller, Philippe Mathieu-Daudé
Create a structure to hold a beginning/end range for guest virtual
addresses, for use by probe_guest_base. Use vaddr for clarity.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/user-internals.h | 5 +++++
linux-user/elfload.c | 36 ++++++++++++++++++------------------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 0380d44fe9..b04ca43f02 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -75,6 +75,11 @@ void clone_fork_end(bool child);
void fork_start(void);
void fork_end(pid_t pid);
+typedef struct PGBRange {
+ vaddr lo;
+ vaddr hi;
+} PGBRange;
+
/**
* probe_guest_base:
* @image_name: the executable being loaded
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 8049c8ae62..2566c2f54c 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -828,15 +828,15 @@ static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
*/
typedef struct PGBAddrs {
- uintptr_t bounds[3][2]; /* start/last pairs */
+ PGBRange bounds[3];
int nbounds;
} PGBAddrs;
static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
{
for (int i = ga->nbounds - 1; i >= 0; --i) {
- if (pgb_try_mmap_skip_brk(ga->bounds[i][0] + base,
- ga->bounds[i][1] + base,
+ if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
+ ga->bounds[i].hi + base,
brk, i == 0 && reserved_va) <= 0) {
return false;
}
@@ -875,26 +875,26 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
n = 0;
if (reserved_va) {
- ga->bounds[n][0] = try_identity ? mmap_min_addr : 0;
- ga->bounds[n][1] = reserved_va;
+ ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
+ ga->bounds[n].hi = reserved_va;
n++;
/* LO_COMMPAGE and NULL handled by reserving from 0. */
} else {
/* Add any LO_COMMPAGE or NULL page. */
if (LO_COMMPAGE != -1) {
- ga->bounds[n][0] = 0;
- ga->bounds[n][1] = LO_COMMPAGE + TARGET_PAGE_SIZE - 1;
+ ga->bounds[n].lo = 0;
+ ga->bounds[n].hi = LO_COMMPAGE + TARGET_PAGE_SIZE - 1;
n++;
} else if (!try_identity) {
- ga->bounds[n][0] = 0;
- ga->bounds[n][1] = TARGET_PAGE_SIZE - 1;
+ ga->bounds[n].lo = 0;
+ ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
n++;
}
/* Add the guest image for ET_EXEC. */
if (guest_loaddr) {
- ga->bounds[n][0] = guest_loaddr;
- ga->bounds[n][1] = guest_hiaddr;
+ ga->bounds[n].lo = guest_loaddr;
+ ga->bounds[n].hi = guest_hiaddr;
n++;
}
}
@@ -909,8 +909,8 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
/* Add any HI_COMMPAGE not covered by reserved_va. */
if (reserved_va < HI_COMMPAGE) {
- ga->bounds[n][0] = HI_COMMPAGE & qemu_real_host_page_mask();
- ga->bounds[n][1] = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
+ ga->bounds[n].lo = HI_COMMPAGE & qemu_real_host_page_mask();
+ ga->bounds[n].hi = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
n++;
}
@@ -976,8 +976,8 @@ static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
IntervalTreeRoot *root)
{
for (int i = ga->nbounds - 1; i >= 0; --i) {
- uintptr_t s = base + ga->bounds[i][0];
- uintptr_t l = base + ga->bounds[i][1];
+ uintptr_t s = base + ga->bounds[i].lo;
+ uintptr_t l = base + ga->bounds[i].hi;
IntervalTreeNode *n;
if (l < s) {
@@ -1076,9 +1076,9 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
"guest address mapping requirements", image_name);
for (int i = 0; i < ga.nbounds; ++i) {
- error_printf(" %0*" PRIx64 "-%0*" PRIx64 "\n",
- w, (uint64_t)ga.bounds[i][0],
- w, (uint64_t)ga.bounds[i][1]);
+ error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
+ w, ga.bounds[i].lo,
+ w, ga.bounds[i].hi);
}
exit(EXIT_FAILURE);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 02/13] linux-user: Use PGBRange in load_elf_image
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
2026-07-10 23:01 ` [PULL 01/13] linux-user: Introduce PGBRange Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base Richard Henderson
` (11 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller, Philippe Mathieu-Daudé
Collect into range instead of loaddr+hiaddr.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/elfload.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 2566c2f54c..30d7beb562 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1278,7 +1278,8 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
char **pinterp_name)
{
g_autofree struct elf_phdr *phdr = NULL;
- abi_ulong load_addr, load_bias, loaddr, hiaddr, error, align;
+ PGBRange range = { -1, 0 };
+ abi_ulong load_addr, load_bias, error, align;
size_t reserve_size, align_size;
int i, prot_exec;
Error *err = NULL;
@@ -1318,19 +1319,18 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Find the maximum size of the image and allocate an appropriate
* amount of memory to handle that. Locate the interpreter, if any.
*/
- loaddr = -1, hiaddr = 0;
align = 0;
info->exec_stack = EXSTACK_DEFAULT;
for (i = 0; i < ehdr->e_phnum; ++i) {
struct elf_phdr *eppnt = phdr + i;
if (eppnt->p_type == PT_LOAD) {
abi_ulong a = eppnt->p_vaddr & TARGET_PAGE_MASK;
- if (a < loaddr) {
- loaddr = a;
+ if (a < range.lo) {
+ range.lo = a;
}
a = eppnt->p_vaddr + eppnt->p_memsz - 1;
- if (a > hiaddr) {
- hiaddr = a;
+ if (a > range.hi) {
+ range.hi = a;
}
++info->nsegs;
align |= eppnt->p_align;
@@ -1361,7 +1361,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
}
}
- load_addr = loaddr;
+ load_addr = range.lo;
align = pow2ceil(align);
@@ -1371,13 +1371,13 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Make sure that the low address does not conflict with
* MMAP_MIN_ADDR or the QEMU application itself.
*/
- probe_guest_base(image_name, loaddr, hiaddr);
+ probe_guest_base(image_name, range.lo, range.hi);
} else {
/*
* The binary is dynamic, but we still need to
* select guest_base. In this case we pass a size.
*/
- probe_guest_base(image_name, 0, hiaddr - loaddr);
+ probe_guest_base(image_name, 0, range.hi - range.lo);
/*
* Avoid collision with the loader by providing a different
@@ -1414,7 +1414,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* In both cases, we will overwrite pages in this range with mappings
* from the executable.
*/
- reserve_size = (size_t)hiaddr - loaddr + 1;
+ reserve_size = range.hi - range.lo + 1;
align_size = reserve_size;
if (ehdr->e_type != ET_EXEC && align > qemu_real_host_page_size()) {
@@ -1443,7 +1443,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
load_addr = align_addr;
}
- load_bias = load_addr - loaddr;
+ load_bias = load_addr - range.lo;
if (elf_is_fdpic(ehdr)) {
struct elf32_fdpic_loadseg *loadsegs = info->loadsegs =
@@ -1480,7 +1480,7 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
info->start_data = -1;
info->end_data = 0;
/* Usual start for brk is after all sections of the main executable. */
- info->brk = TARGET_PAGE_ALIGN(hiaddr + load_bias);
+ info->brk = TARGET_PAGE_ALIGN(range.hi + load_bias);
info->elf_flags = ehdr->e_flags;
#ifdef TARGET_MIPS
info->use_k0_tls = (ehdr->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_OCTEON;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
2026-07-10 23:01 ` [PULL 01/13] linux-user: Introduce PGBRange Richard Henderson
2026-07-10 23:01 ` [PULL 02/13] linux-user: Use PGBRange in load_elf_image Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 04/13] linux-user: Pass image_range to probe_guest_base Richard Henderson
` (10 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller
Since dropping 32-bit host support, a guest address cannot
overflow a host pointer. This means guest_hiaddr is unused
for relocatable images, so don't pass guest_hiaddr as size.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/elfload.c | 26 +++++++-------------------
linux-user/flatload.c | 3 +--
2 files changed, 8 insertions(+), 21 deletions(-)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 30d7beb562..1367657d58 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1092,20 +1092,11 @@ void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
/* Sanity check the guest binary. */
- if (reserved_va) {
- if (guest_hiaddr > reserved_va) {
- error_report("%s: requires more than reserved virtual "
- "address space (0x%" PRIx64 " > 0x%lx)",
- image_name, (uint64_t)guest_hiaddr, reserved_va);
- exit(EXIT_FAILURE);
- }
- } else {
- if (guest_hiaddr != (uintptr_t)guest_hiaddr) {
- error_report("%s: requires more virtual address space "
- "than the host can provide (0x%" PRIx64 ")",
- image_name, (uint64_t)guest_hiaddr + 1);
- exit(EXIT_FAILURE);
- }
+ if (reserved_va && guest_hiaddr > reserved_va) {
+ error_report("%s: requires more than reserved virtual "
+ "address space (0x%" PRIx64 " > 0x%lx)",
+ image_name, (uint64_t)guest_hiaddr, reserved_va);
+ exit(EXIT_FAILURE);
}
if (have_guest_base) {
@@ -1373,11 +1364,8 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
*/
probe_guest_base(image_name, range.lo, range.hi);
} else {
- /*
- * The binary is dynamic, but we still need to
- * select guest_base. In this case we pass a size.
- */
- probe_guest_base(image_name, 0, range.hi - range.lo);
+ /* The binary is dynamic; we still need to select guest_base. */
+ probe_guest_base(image_name, 0, 0);
/*
* Avoid collision with the loader by providing a different
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 4beb3ed1b9..8abdd2aef2 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -261,8 +261,7 @@ static int load_flat_file(struct linux_binprm * bprm,
/*
* Allocate the address space.
*/
- probe_guest_base(bprm->filename, 0,
- text_len + data_len + extra + indx_len - 1);
+ probe_guest_base(bprm->filename, 0, 0);
/*
* there are a couple of cases here, the separate code/data
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 04/13] linux-user: Pass image_range to probe_guest_base
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (2 preceding siblings ...)
2026-07-10 23:01 ` [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 05/13] linux-user: Use PGBRange for commpage Richard Henderson
` (9 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh, Philippe Mathieu-Daudé
Pass a PGBRange structure instead of separate guest_loaddr
and guest_hiaddr parameters. This allows NULL to indicate
that the image is relocatable, so that image_range->lo == 0
is a valid fixed setting.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/1890
Tested-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/user-internals.h | 13 ++++-------
linux-user/elfload.c | 46 +++++++++++++++++--------------------
linux-user/flatload.c | 2 +-
3 files changed, 27 insertions(+), 34 deletions(-)
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index b04ca43f02..d7f346b0d0 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -83,24 +83,21 @@ typedef struct PGBRange {
/**
* probe_guest_base:
* @image_name: the executable being loaded
- * @loaddr: the lowest fixed address within the executable
- * @hiaddr: the highest fixed address within the executable
+ * @image_range: the fixed addresses within the executable
*
* Creates the initial guest address space in the host memory space.
*
- * If @loaddr == 0, then no address in the executable is fixed, i.e.
- * it is fully relocatable. In that case @hiaddr is the size of the
- * executable minus one.
+ * If @image_range is NULL, then no address in the executable is fixed,
+ * i.e. it is fully relocatable.
*
* This function will not return if a valid value for guest_base
* cannot be chosen. On return, the executable loader can expect
*
- * target_mmap(loaddr, hiaddr - loaddr + 1, ...)
+ * target_mmap(i->lo, i->hi - i->lo + 1, ...)
*
* to succeed.
*/
-void probe_guest_base(const char *image_name,
- abi_ulong loaddr, abi_ulong hiaddr);
+void probe_guest_base(const char *image_name, const PGBRange *image_range);
/* syscall.c */
int host_to_target_waitstatus(int status);
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 1367657d58..19d2bad581 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -847,14 +847,13 @@ static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
/**
* pgb_addr_set:
* @ga: output set of guest addrs
- * @guest_loaddr: guest image low address
- * @guest_hiaddr: guest image high address
+ * @image_range: fixed guest image addresses
* @identity: create for identity mapping
*
* Fill in @ga with the image, COMMPAGE and NULL page.
*/
-static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
- abi_ulong guest_hiaddr, bool try_identity)
+static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
+ bool try_identity)
{
int n;
@@ -866,7 +865,7 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
if (LO_COMMPAGE != -1 && LO_COMMPAGE < mmap_min_addr) {
return false;
}
- if (guest_loaddr != 0 && guest_loaddr < mmap_min_addr) {
+ if (image_range && image_range->lo < mmap_min_addr) {
return false;
}
}
@@ -892,10 +891,8 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr,
}
/* Add the guest image for ET_EXEC. */
- if (guest_loaddr) {
- ga->bounds[n].lo = guest_loaddr;
- ga->bounds[n].hi = guest_hiaddr;
- n++;
+ if (image_range) {
+ ga->bounds[n++] = *image_range;
}
}
@@ -928,8 +925,8 @@ static void pgb_fail_in_use(const char *image_name)
exit(EXIT_FAILURE);
}
-static void pgb_fixed(const char *image_name, uintptr_t guest_loaddr,
- uintptr_t guest_hiaddr, uintptr_t align)
+static void pgb_fixed(const char *image_name, const PGBRange *image_range,
+ uintptr_t align)
{
PGBAddrs ga;
uintptr_t brk = (uintptr_t)sbrk(0);
@@ -941,7 +938,7 @@ static void pgb_fixed(const char *image_name, uintptr_t guest_loaddr,
exit(EXIT_FAILURE);
}
- if (!pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, !guest_base)
+ if (!pgb_addr_set(&ga, image_range, !guest_base)
|| !pgb_try_mmap_set(&ga, guest_base, brk)) {
pgb_fail_in_use(image_name);
}
@@ -1026,15 +1023,15 @@ static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
return pgb_try_mmap_set(ga, base, brk) ? base : -1;
}
-static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
- uintptr_t guest_hiaddr, uintptr_t align)
+static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
+ uintptr_t align)
{
IntervalTreeRoot *root;
uintptr_t brk, ret;
PGBAddrs ga;
/* Try the identity map first. */
- if (pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, true)) {
+ if (pgb_addr_set(&ga, image_range, true)) {
brk = (uintptr_t)sbrk(0);
if (pgb_try_mmap_set(&ga, 0, brk)) {
guest_base = 0;
@@ -1046,7 +1043,7 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
* Rebuild the address set for non-identity map.
* This differs in the mapping of the guest NULL page.
*/
- pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, false);
+ pgb_addr_set(&ga, image_range, false);
root = read_self_maps();
@@ -1085,24 +1082,23 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr,
guest_base = ret;
}
-void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
- abi_ulong guest_hiaddr)
+void probe_guest_base(const char *image_name, const PGBRange *image_range)
{
/* In order to use host shmat, we must be able to honor SHMLBA. */
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
/* Sanity check the guest binary. */
- if (reserved_va && guest_hiaddr > reserved_va) {
+ if (reserved_va && image_range && image_range->hi > reserved_va) {
error_report("%s: requires more than reserved virtual "
- "address space (0x%" PRIx64 " > 0x%lx)",
- image_name, (uint64_t)guest_hiaddr, reserved_va);
+ "address space (0x%" VADDR_PRIx " > 0x%lx)",
+ image_name, image_range->hi, reserved_va);
exit(EXIT_FAILURE);
}
if (have_guest_base) {
- pgb_fixed(image_name, guest_loaddr, guest_hiaddr, align);
+ pgb_fixed(image_name, image_range, align);
} else {
- pgb_dynamic(image_name, guest_loaddr, guest_hiaddr, align);
+ pgb_dynamic(image_name, image_range, align);
}
/* Reserve and initialize the commpage. */
@@ -1362,10 +1358,10 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Make sure that the low address does not conflict with
* MMAP_MIN_ADDR or the QEMU application itself.
*/
- probe_guest_base(image_name, range.lo, range.hi);
+ probe_guest_base(image_name, &range);
} else {
/* The binary is dynamic; we still need to select guest_base. */
- probe_guest_base(image_name, 0, 0);
+ probe_guest_base(image_name, NULL);
/*
* Avoid collision with the loader by providing a different
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 8abdd2aef2..45d69040c6 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -261,7 +261,7 @@ static int load_flat_file(struct linux_binprm * bprm,
/*
* Allocate the address space.
*/
- probe_guest_base(bprm->filename, 0, 0);
+ probe_guest_base(bprm->filename, NULL);
/*
* there are a couple of cases here, the separate code/data
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 05/13] linux-user: Use PGBRange for commpage
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (3 preceding siblings ...)
2026-07-10 23:01 ` [PULL 04/13] linux-user: Pass image_range to probe_guest_base Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 06/13] common-user: Move mmap_min_addr from linux-user Richard Henderson
` (8 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh, Helge Deller, Philippe Mathieu-Daudé
This simplifies check for no commpage to a NULL pointer
check, rather than reserved values for LO_COMMPAGE and
HI_COMMPAGE.
Unify {LO,HI}_COMMPAGE into a single COMMPAGE define.
Acked-by: Warner Losh <imp@bsdimp.com>
Tested-by: Helge Deller <deller@gmx.de>
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
linux-user/arm/target_elf.h | 2 +-
linux-user/hppa/target_elf.h | 2 +-
linux-user/arm/elfload.c | 2 +-
linux-user/elfload.c | 64 ++++++++++++++----------------------
linux-user/hppa/elfload.c | 4 +--
5 files changed, 30 insertions(+), 44 deletions(-)
diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h
index 12cdc8e5a7..531b486bf9 100644
--- a/linux-user/arm/target_elf.h
+++ b/linux-user/arm/target_elf.h
@@ -20,7 +20,7 @@
#define HAVE_ELF_CORE_DUMP 1
#define HAVE_VDSO_IMAGE_INFO 1
-#define HI_COMMPAGE ((intptr_t)0xffff0f00u)
+#define COMMPAGE ((intptr_t)0xffff0f00u)
/*
* See linux kernel: arch/arm/include/asm/elf.h, where
diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h
index e1c5033242..22547b1437 100644
--- a/linux-user/hppa/target_elf.h
+++ b/linux-user/hppa/target_elf.h
@@ -34,7 +34,7 @@ typedef struct target_elf_gregset_t {
abi_ulong pad[16]; /* pad to 80 elements [64..79] */
} target_elf_gregset_t;
-#define LO_COMMPAGE 0
+#define COMMPAGE 0
#define STACK_GROWS_DOWN 0
#define STACK_ALIGNMENT 64
#define VDSO_HEADER "vdso.c.inc"
diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c
index 13b589ea2c..ce69bf0542 100644
--- a/linux-user/arm/elfload.c
+++ b/linux-user/arm/elfload.c
@@ -220,7 +220,7 @@ bool init_guest_commpage(void)
return true;
}
- commpage = HI_COMMPAGE & -host_page_size;
+ commpage = COMMPAGE & -host_page_size;
want = g2h_untagged(commpage);
addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE |
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 19d2bad581..557ed20dbf 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -757,17 +757,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
return sp;
}
-#if defined(HI_COMMPAGE)
-#define LO_COMMPAGE -1
-#elif defined(LO_COMMPAGE)
-#define HI_COMMPAGE 0
-#else
-#define HI_COMMPAGE 0
-#define LO_COMMPAGE -1
-#ifndef HAVE_GUEST_COMMPAGE
+#if !defined(COMMPAGE) && !defined(HAVE_GUEST_COMMPAGE)
bool init_guest_commpage(void) { return true; }
#endif
-#endif
/**
* pgb_try_mmap:
@@ -853,7 +845,7 @@ static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
* Fill in @ga with the image, COMMPAGE and NULL page.
*/
static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
- bool try_identity)
+ const PGBRange *commpage_range, bool try_identity)
{
int n;
@@ -862,7 +854,7 @@ static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
* we may not be able to use the identity map.
*/
if (try_identity) {
- if (LO_COMMPAGE != -1 && LO_COMMPAGE < mmap_min_addr) {
+ if (commpage_range && commpage_range->lo < mmap_min_addr) {
return false;
}
if (image_range && image_range->lo < mmap_min_addr) {
@@ -877,14 +869,10 @@ static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
ga->bounds[n].hi = reserved_va;
n++;
- /* LO_COMMPAGE and NULL handled by reserving from 0. */
+ /* Low COMMPAGE and NULL handled by reserving from 0. */
} else {
- /* Add any LO_COMMPAGE or NULL page. */
- if (LO_COMMPAGE != -1) {
- ga->bounds[n].lo = 0;
- ga->bounds[n].hi = LO_COMMPAGE + TARGET_PAGE_SIZE - 1;
- n++;
- } else if (!try_identity) {
+ /* Add any low COMMPAGE or NULL page. */
+ if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
ga->bounds[n].lo = 0;
ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
n++;
@@ -896,23 +884,13 @@ static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
}
}
- /*
- * Temporarily disable
- * "comparison is always false due to limited range of data type"
- * due to comparison between unsigned and (possible) 0.
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
-
- /* Add any HI_COMMPAGE not covered by reserved_va. */
- if (reserved_va < HI_COMMPAGE) {
- ga->bounds[n].lo = HI_COMMPAGE & qemu_real_host_page_mask();
- ga->bounds[n].hi = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
+ /* Add any high COMMPAGE not covered by reserved_va. */
+ if (commpage_range && reserved_va < commpage_range->hi) {
+ ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
+ ga->bounds[n].hi = commpage_range->hi;
n++;
}
-#pragma GCC diagnostic pop
-
ga->nbounds = n;
return true;
}
@@ -926,7 +904,7 @@ static void pgb_fail_in_use(const char *image_name)
}
static void pgb_fixed(const char *image_name, const PGBRange *image_range,
- uintptr_t align)
+ const PGBRange *commpage_range, uintptr_t align)
{
PGBAddrs ga;
uintptr_t brk = (uintptr_t)sbrk(0);
@@ -938,7 +916,7 @@ static void pgb_fixed(const char *image_name, const PGBRange *image_range,
exit(EXIT_FAILURE);
}
- if (!pgb_addr_set(&ga, image_range, !guest_base)
+ if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
|| !pgb_try_mmap_set(&ga, guest_base, brk)) {
pgb_fail_in_use(image_name);
}
@@ -1024,14 +1002,14 @@ static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
}
static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
- uintptr_t align)
+ const PGBRange *commpage_range, uintptr_t align)
{
IntervalTreeRoot *root;
uintptr_t brk, ret;
PGBAddrs ga;
/* Try the identity map first. */
- if (pgb_addr_set(&ga, image_range, true)) {
+ if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
brk = (uintptr_t)sbrk(0);
if (pgb_try_mmap_set(&ga, 0, brk)) {
guest_base = 0;
@@ -1043,7 +1021,7 @@ static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
* Rebuild the address set for non-identity map.
* This differs in the mapping of the guest NULL page.
*/
- pgb_addr_set(&ga, image_range, false);
+ pgb_addr_set(&ga, image_range, commpage_range, false);
root = read_self_maps();
@@ -1084,6 +1062,14 @@ static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
void probe_guest_base(const char *image_name, const PGBRange *image_range)
{
+#ifdef COMMPAGE
+ const PGBRange * const commpage_range = &(PGBRange){
+ COMMPAGE, COMMPAGE + TARGET_PAGE_SIZE - 1
+ };
+#else
+ const PGBRange * const commpage_range = NULL;
+#endif
+
/* In order to use host shmat, we must be able to honor SHMLBA. */
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
@@ -1096,9 +1082,9 @@ void probe_guest_base(const char *image_name, const PGBRange *image_range)
}
if (have_guest_base) {
- pgb_fixed(image_name, image_range, align);
+ pgb_fixed(image_name, image_range, commpage_range, align);
} else {
- pgb_dynamic(image_name, image_range, align);
+ pgb_dynamic(image_name, image_range, commpage_range, align);
}
/* Reserve and initialize the commpage. */
diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c
index 2ebb9924f1..ff4301b2ed 100644
--- a/linux-user/hppa/elfload.c
+++ b/linux-user/hppa/elfload.c
@@ -36,7 +36,7 @@ bool init_guest_commpage(void)
if (!reserved_va) {
void *want, *addr;
- want = g2h_untagged(LO_COMMPAGE);
+ want = g2h_untagged(COMMPAGE);
addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
if (addr == MAP_FAILED) {
@@ -55,7 +55,7 @@ bool init_guest_commpage(void)
* and implement syscalls. Here, simply mark the page executable.
* Special case the entry points during translation (see do_page_zero).
*/
- page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK,
+ page_set_flags(COMMPAGE, COMMPAGE | ~TARGET_PAGE_MASK,
PAGE_EXEC | PAGE_VALID, PAGE_VALID);
return true;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 06/13] common-user: Move mmap_min_addr from linux-user
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (4 preceding siblings ...)
2026-07-10 23:01 ` [PULL 05/13] linux-user: Use PGBRange for commpage Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD Richard Henderson
` (7 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Introduce user/mmap-min-addr.h. Initialize the variable
from a constructor instead of main.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/user/mmap-min-addr.h | 12 ++++++++++++
linux-user/user-internals.h | 1 -
common-user/mmap-min-addr.c | 37 ++++++++++++++++++++++++++++++++++++
linux-user/elfload.c | 1 +
linux-user/main.c | 33 +++-----------------------------
linux-user/mmap.c | 1 +
common-user/meson.build | 1 +
7 files changed, 55 insertions(+), 31 deletions(-)
create mode 100644 include/user/mmap-min-addr.h
create mode 100644 common-user/mmap-min-addr.c
diff --git a/include/user/mmap-min-addr.h b/include/user/mmap-min-addr.h
new file mode 100644
index 0000000000..ded0b6909d
--- /dev/null
+++ b/include/user/mmap-min-addr.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef USER_MMAP_MIN_ADDR_H
+#define USER_MMAP_MIN_ADDR_H
+
+#ifndef CONFIG_USER_ONLY
+#error Cannot include this header from system emulation
+#endif
+
+extern uintptr_t mmap_min_addr;
+
+#endif
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index d7f346b0d0..74d8eaabbe 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -29,7 +29,6 @@ void init_task_state(TaskState *ts);
void task_settid(TaskState *);
void stop_all_tasks(void);
extern const char *qemu_uname_release;
-extern unsigned long mmap_min_addr;
typedef struct IOCTLEntry IOCTLEntry;
diff --git a/common-user/mmap-min-addr.c b/common-user/mmap-min-addr.c
new file mode 100644
index 0000000000..b2b3762386
--- /dev/null
+++ b/common-user/mmap-min-addr.c
@@ -0,0 +1,37 @@
+/*
+ * Utility function to get the minimum mmap address.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "user/mmap-min-addr.h"
+
+uintptr_t mmap_min_addr;
+
+static void __attribute__((constructor)) init(void)
+{
+#ifdef __linux__
+ /*
+ * We prefer to not make NULL pointers accessible to QEMU.
+ * If something goes wrong below, fall back to 1 page.
+ */
+ size_t min_addr = qemu_real_host_page_size();
+ /*
+ * Read in mmap_min_addr kernel parameter. This value is used
+ * When loading the ELF image to determine whether guest_base
+ * is needed. It is also used in mmap_find_vma.
+ */
+ FILE *fp = fopen("/proc/sys/vm/mmap_min_addr", "r");
+
+ if (fp) {
+ unsigned long tmp;
+ if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) {
+ min_addr = MAX(min_addr, tmp);
+ }
+ fclose(fp);
+ }
+ mmap_min_addr = min_addr;
+#else
+# error
+#endif
+}
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 557ed20dbf..0671aba537 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -14,6 +14,7 @@
#include "exec/translation-block.h"
#include "exec/tswap.h"
#include "user/guest-base.h"
+#include "user/mmap-min-addr.h"
#include "user-internals.h"
#include "signal-common.h"
#include "loader.h"
diff --git a/linux-user/main.c b/linux-user/main.c
index c08c73fd80..01cf5bf079 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -39,6 +39,7 @@
#include "qemu/module.h"
#include "qemu/plugin.h"
#include "user/guest-base.h"
+#include "user/mmap-min-addr.h"
#include "user/page-protection.h"
#include "exec/gdbstub.h"
#include "gdbstub/user.h"
@@ -78,7 +79,6 @@ static envlist_t *envlist;
static const char *cpu_model;
static const char *cpu_type;
static const char *seed_optarg;
-unsigned long mmap_min_addr;
uintptr_t guest_base;
bool have_guest_base;
@@ -914,35 +914,8 @@ int main(int argc, char **argv, char **envp)
target_environ = envlist_to_environ(envlist, NULL);
envlist_free(envlist);
- /*
- * Read in mmap_min_addr kernel parameter. This value is used
- * When loading the ELF image to determine whether guest_base
- * is needed. It is also used in mmap_find_vma.
- */
- {
- FILE *fp;
-
- if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
- unsigned long tmp;
- if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) {
- mmap_min_addr = MAX(tmp, host_page_size);
- qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n",
- mmap_min_addr);
- }
- fclose(fp);
- }
- }
-
- /*
- * We prefer to not make NULL pointers accessible to QEMU.
- * If we're in a chroot with no /proc, fall back to 1 page.
- */
- if (mmap_min_addr == 0) {
- mmap_min_addr = host_page_size;
- qemu_log_mask(CPU_LOG_PAGE,
- "host mmap_min_addr=0x%lx (fallback)\n",
- mmap_min_addr);
- }
+ qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%" PRIxPTR "\n",
+ mmap_min_addr);
/*
* Prepare copy of argv vector for target.
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 55277f7c3e..cc0c2ee6c2 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -24,6 +24,7 @@
#include "exec/mmap-lock.h"
#include "qemu.h"
#include "user/page-protection.h"
+#include "user/mmap-min-addr.h"
#include "user-internals.h"
#include "user-mmap.h"
#include "target_mman.h"
diff --git a/common-user/meson.build b/common-user/meson.build
index ac9de5b9e3..f9e2e83f9a 100644
--- a/common-user/meson.build
+++ b/common-user/meson.build
@@ -5,6 +5,7 @@ endif
common_user_inc += include_directories('host/' / host_arch)
user_ss.add(files(
+ 'mmap-min-addr.c',
'safe-syscall.S',
'safe-syscall-error.c',
))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (5 preceding siblings ...)
2026-07-10 23:01 ` [PULL 06/13] common-user: Move mmap_min_addr from linux-user Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 08/13] include/user/guest-host: Include missing cpu.h Richard Henderson
` (6 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Use sysctl to fetch the vm layout of the current process.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
common-user/mmap-min-addr.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/common-user/mmap-min-addr.c b/common-user/mmap-min-addr.c
index b2b3762386..830c6f66a0 100644
--- a/common-user/mmap-min-addr.c
+++ b/common-user/mmap-min-addr.c
@@ -5,6 +5,10 @@
#include "qemu/osdep.h"
#include "user/mmap-min-addr.h"
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
uintptr_t mmap_min_addr;
@@ -31,6 +35,15 @@ static void __attribute__((constructor)) init(void)
fclose(fp);
}
mmap_min_addr = min_addr;
+#elif defined(__FreeBSD__)
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_VM_LAYOUT, getpid() };
+ struct kinfo_vm_layout info;
+ size_t info_len = sizeof(info);
+
+ mmap_min_addr =
+ (sysctl(mib, ARRAY_SIZE(mib), &info, &info_len, NULL, 0) < 0
+ ? qemu_real_host_page_size()
+ : info.kvm_min_user_addr);
#else
# error
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 08/13] include/user/guest-host: Include missing cpu.h
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (6 preceding siblings ...)
2026-07-10 23:01 ` [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 09/13] common-user: Move selfmap from util Richard Henderson
` (5 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh, Philippe Mathieu-Daudé
This file dereferences CPUState without including the required header.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/user/guest-host.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/user/guest-host.h b/include/user/guest-host.h
index ef83ad8a18..506efc097e 100644
--- a/include/user/guest-host.h
+++ b/include/user/guest-host.h
@@ -10,6 +10,7 @@
#include "exec/vaddr.h"
#include "user/guest-base.h"
+#include "hw/core/cpu.h"
#include "accel/tcg/cpu-ops.h"
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 09/13] common-user: Move selfmap from util
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (7 preceding siblings ...)
2026-07-10 23:01 ` [PULL 08/13] include/user/guest-host: Include missing cpu.h Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 10/13] common-user: Implement read_self_maps for FreeBSD Richard Henderson
` (4 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh, Philippe Mathieu-Daudé
This interface is only used for user-only. It's more usefully
placed within common-user than util. Temporarily add stub
implementation for bsd-user.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/{qemu => user}/selfmap.h | 0
{util => common-user}/selfmap.c | 6 +++++-
linux-user/elfload.c | 2 +-
linux-user/syscall.c | 2 +-
common-user/meson.build | 1 +
util/meson.build | 4 ----
6 files changed, 8 insertions(+), 7 deletions(-)
rename include/{qemu => user}/selfmap.h (100%)
rename {util => common-user}/selfmap.c (97%)
diff --git a/include/qemu/selfmap.h b/include/user/selfmap.h
similarity index 100%
rename from include/qemu/selfmap.h
rename to include/user/selfmap.h
diff --git a/util/selfmap.c b/common-user/selfmap.c
similarity index 97%
rename from util/selfmap.c
rename to common-user/selfmap.c
index 483cb617e2..a1b88dee66 100644
--- a/util/selfmap.c
+++ b/common-user/selfmap.c
@@ -8,10 +8,11 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
-#include "qemu/selfmap.h"
+#include "user/selfmap.h"
IntervalTreeRoot *read_self_maps(void)
{
+#ifdef __linux__
IntervalTreeRoot *root;
gchar *maps, **lines;
guint i, nlines;
@@ -80,6 +81,9 @@ IntervalTreeRoot *read_self_maps(void)
g_free(maps);
return root;
+#else
+ return NULL;
+#endif
}
/**
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 0671aba537..207f814e15 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -25,7 +25,7 @@
#include "qemu/queue.h"
#include "qemu/guest-random.h"
#include "qemu/units.h"
-#include "qemu/selfmap.h"
+#include "user/selfmap.h"
#include "qemu/lockable.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d257fb9ca9..b3572a6e36 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -141,7 +141,7 @@
#include "user/safe-syscall.h"
#include "user/signal.h"
#include "qemu/guest-random.h"
-#include "qemu/selfmap.h"
+#include "user/selfmap.h"
#include "special-errno.h"
#include "qapi/error.h"
#include "fd-trans.h"
diff --git a/common-user/meson.build b/common-user/meson.build
index f9e2e83f9a..831a7273fb 100644
--- a/common-user/meson.build
+++ b/common-user/meson.build
@@ -8,4 +8,5 @@ user_ss.add(files(
'mmap-min-addr.c',
'safe-syscall.S',
'safe-syscall-error.c',
+ 'selfmap.c',
))
diff --git a/util/meson.build b/util/meson.build
index e29cbd948a..fa174c07a5 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -67,10 +67,6 @@ util_ss.add(files('memalign.c'))
util_ss.add(files('interval-tree.c'))
util_ss.add(files('lockcnt.c'))
-if have_user
- util_ss.add(files('selfmap.c'))
-endif
-
if have_system
util_ss.add(files('crc-ccitt.c'))
util_ss.add(when: gio, if_true: files('dbus.c'))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 10/13] common-user: Implement read_self_maps for FreeBSD
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (8 preceding siblings ...)
2026-07-10 23:01 ` [PULL 09/13] common-user: Move selfmap from util Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 11/13] common-user: Move probe_guest_base from linux-user Richard Henderson
` (3 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Use sysctl to fetch the vm map of the current process.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
common-user/selfmap.c | 45 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/common-user/selfmap.c b/common-user/selfmap.c
index a1b88dee66..139dae15e8 100644
--- a/common-user/selfmap.c
+++ b/common-user/selfmap.c
@@ -9,6 +9,10 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "user/selfmap.h"
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
IntervalTreeRoot *read_self_maps(void)
{
@@ -80,9 +84,48 @@ IntervalTreeRoot *read_self_maps(void)
g_strfreev(lines);
g_free(maps);
+ return root;
+#elif defined(__FreeBSD__)
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
+ size_t len = 0;
+ g_autofree void *buf = NULL;
+ IntervalTreeRoot *root;
+
+ /* Probe for buffer size. */
+ if (sysctl(mib, ARRAY_SIZE(mib), NULL, &len, NULL, 0) < 0) {
+ return NULL;
+ }
+
+ buf = g_malloc(len);
+ if (sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) < 0) {
+ return NULL;
+ }
+
+ root = g_new0(IntervalTreeRoot, 1);
+
+ for (size_t i = 0; i < len; ) {
+ struct kinfo_vmentry *k = buf + i;
+ MapInfo *e = g_new0(MapInfo, 1);
+
+ e->itree.start = k->kve_start;
+ e->itree.last = k->kve_end - 1;
+
+ /*
+ * TODO: The rest of the fields in MapInfo are used by linux-user
+ * for the implementation of open_self_maps(). These fields are
+ * quite specific to the textual format of /proc/self/maps.
+ *
+ * We may need something different to emulate KERN_PROC_VMMAP
+ * in bsd-user, but so far they're unused -- leave them zeroed.
+ */
+
+ interval_tree_insert(&e->itree, root);
+ i += k->kve_structsize;
+ }
+
return root;
#else
- return NULL;
+# error
#endif
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 11/13] common-user: Move probe_guest_base from linux-user
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (9 preceding siblings ...)
2026-07-10 23:01 ` [PULL 10/13] common-user: Implement read_self_maps for FreeBSD Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 12/13] bsd-user: Use probe_guest_base Richard Henderson
` (2 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Warner Losh
Prepare to share probe_guest_base with bsd-user.
Create a linux_probe_guest_base wrapper with the portions
of probe_guest_base that are linux specific: managing the
commpage for various targets.
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
include/user/probe-guest-base.h | 37 ++++
linux-user/user-internals.h | 10 +-
common-user/probe-guest-base.c | 343 ++++++++++++++++++++++++++++++++
linux-user/elfload.c | 337 +------------------------------
linux-user/flatload.c | 2 +-
common-user/meson.build | 1 +
6 files changed, 392 insertions(+), 338 deletions(-)
create mode 100644 include/user/probe-guest-base.h
create mode 100644 common-user/probe-guest-base.c
diff --git a/include/user/probe-guest-base.h b/include/user/probe-guest-base.h
new file mode 100644
index 0000000000..d7492ef46c
--- /dev/null
+++ b/include/user/probe-guest-base.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef USER_PROBE_GUEST_BASE_H
+#define USER_PROBE_GUEST_BASE_H
+
+#ifndef CONFIG_USER_ONLY
+#error Cannot include this header from system emulation
+#endif
+
+#include "exec/vaddr.h"
+
+typedef struct PGBRange {
+ vaddr lo;
+ vaddr hi;
+} PGBRange;
+
+/**
+ * probe_guest_base:
+ * @image_name: the executable being loaded
+ * @image_range: the fixed addresses within the executable
+ *
+ * Creates the initial guest address space in the host memory space.
+ *
+ * If @image_range is NULL, then no address in the executable is fixed,
+ * i.e. it is fully relocatable.
+ *
+ * This function will not return if a valid value for guest_base
+ * cannot be chosen. On return, the executable loader can expect
+ *
+ * target_mmap(i->lo, i->hi - i->lo + 1, ...)
+ *
+ * to succeed.
+ */
+void probe_guest_base(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range);
+
+#endif
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index 74d8eaabbe..e7fbddd0e6 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -22,6 +22,7 @@
#include "qemu/log.h"
#include "exec/tb-flush.h"
#include "exec/translation-block.h"
+#include "user/probe-guest-base.h"
extern char *exec_path;
extern char real_exec_path[PATH_MAX];
@@ -74,13 +75,8 @@ void clone_fork_end(bool child);
void fork_start(void);
void fork_end(pid_t pid);
-typedef struct PGBRange {
- vaddr lo;
- vaddr hi;
-} PGBRange;
-
/**
- * probe_guest_base:
+ * linux_probe_guest_base:
* @image_name: the executable being loaded
* @image_range: the fixed addresses within the executable
*
@@ -96,7 +92,7 @@ typedef struct PGBRange {
*
* to succeed.
*/
-void probe_guest_base(const char *image_name, const PGBRange *image_range);
+void linux_probe_guest_base(const char *image_name, const PGBRange *image_range);
/* syscall.c */
int host_to_target_waitstatus(int status);
diff --git a/common-user/probe-guest-base.c b/common-user/probe-guest-base.c
new file mode 100644
index 0000000000..77722fe63f
--- /dev/null
+++ b/common-user/probe-guest-base.c
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "qemu/target-info.h"
+#include "qemu/log.h"
+#include "user/guest-base.h"
+#include "user/mmap-min-addr.h"
+#include "user/guest-host.h"
+#include "user/probe-guest-base.h"
+#include "user/selfmap.h"
+#include "exec/target_page.h"
+#include <sys/shm.h>
+
+/* Linux and FreeBSD use different flags to express NOREPLACE. */
+#ifdef __FreeBSD__
+#define MAP_FIXED_NOREPLACE (MAP_FIXED | MAP_EXCL)
+#endif
+
+/**
+ * pgb_try_mmap:
+ * @addr: host start address
+ * @addr_last: host last address
+ * @keep: do not unmap the probe region
+ *
+ * Return 1 if [@addr, @addr_last] is not mapped in the host,
+ * return 0 if it is not available to map, and -1 on mmap error.
+ * If @keep, the region is left mapped on success, otherwise unmapped.
+ */
+static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep)
+{
+ size_t size = addr_last - addr + 1;
+ void *p = mmap((void *)addr, size, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE |
+ MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
+ int ret;
+
+ if (p == MAP_FAILED) {
+ return errno == EEXIST ? 0 : -1;
+ }
+ ret = p == (void *)addr;
+ if (!keep || !ret) {
+ munmap(p, size);
+ }
+ return ret;
+}
+
+/**
+ * pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t size, uintptr_t brk)
+ * @addr: host address
+ * @addr_last: host last address
+ * @brk: host brk
+ *
+ * Like pgb_try_mmap, but additionally reserve some memory following brk.
+ */
+static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
+ uintptr_t brk, bool keep)
+{
+ uintptr_t brk_last = brk + 16 * MiB - 1;
+
+ /* Do not map anything close to the host brk. */
+ if (addr <= brk_last && brk <= addr_last) {
+ return 0;
+ }
+ return pgb_try_mmap(addr, addr_last, keep);
+}
+
+/**
+ * pgb_try_mmap_set:
+ * @ga: set of guest addrs
+ * @base: guest_base
+ * @brk: host brk
+ *
+ * Return true if all @ga can be mapped by the host at @base.
+ * On success, retain the mapping at index 0 for reserved_va.
+ */
+
+typedef struct PGBAddrs {
+ PGBRange bounds[3];
+ int nbounds;
+} PGBAddrs;
+
+static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
+{
+ for (int i = ga->nbounds - 1; i >= 0; --i) {
+ if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
+ ga->bounds[i].hi + base,
+ brk, i == 0 && reserved_va) <= 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * pgb_addr_set:
+ * @ga: output set of guest addrs
+ * @image_range: fixed guest image addresses
+ * @identity: create for identity mapping
+ *
+ * Fill in @ga with the image, COMMPAGE and NULL page.
+ */
+static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
+ const PGBRange *commpage_range, bool try_identity)
+{
+ int n;
+
+ /*
+ * With a low commpage, or a guest mapped very low,
+ * we may not be able to use the identity map.
+ */
+ if (try_identity) {
+ if (commpage_range && commpage_range->lo < mmap_min_addr) {
+ return false;
+ }
+ if (image_range && image_range->lo < mmap_min_addr) {
+ return false;
+ }
+ }
+
+ memset(ga, 0, sizeof(*ga));
+ n = 0;
+
+ if (reserved_va) {
+ ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
+ ga->bounds[n].hi = reserved_va;
+ n++;
+ /* Low COMMPAGE and NULL handled by reserving from 0. */
+ } else {
+ /* Add any low COMMPAGE or NULL page. */
+ if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
+ ga->bounds[n].lo = 0;
+ ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
+ n++;
+ }
+
+ /* Add the guest image for ET_EXEC. */
+ if (image_range) {
+ ga->bounds[n++] = *image_range;
+ }
+ }
+
+ /* Add any high COMMPAGE not covered by reserved_va. */
+ if (commpage_range && reserved_va < commpage_range->hi) {
+ ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
+ ga->bounds[n].hi = commpage_range->hi;
+ n++;
+ }
+
+ ga->nbounds = n;
+ return true;
+}
+
+static void pgb_fail_in_use(const char *image_name)
+{
+ error_report("%s: requires virtual address space that is in use "
+ "(omit the -B option or choose a different value)",
+ image_name);
+ exit(EXIT_FAILURE);
+}
+
+static void pgb_fixed(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range, uintptr_t align)
+{
+ PGBAddrs ga;
+ uintptr_t brk = (uintptr_t)sbrk(0);
+
+ if (!QEMU_IS_ALIGNED(guest_base, align)) {
+ fprintf(stderr, "Requested guest base %p does not satisfy "
+ "host minimum alignment (0x%" PRIxPTR ")\n",
+ (void *)guest_base, align);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
+ || !pgb_try_mmap_set(&ga, guest_base, brk)) {
+ pgb_fail_in_use(image_name);
+ }
+}
+
+/**
+ * pgb_find_fallback:
+ *
+ * This is a fallback method for finding holes in the host address space
+ * if we don't have the benefit of being able to access /proc/self/map.
+ * It can potentially take a very long time as we can only dumbly iterate
+ * up the host address space seeing if the allocation would work.
+ */
+static uintptr_t pgb_find_fallback(const PGBAddrs *ga, uintptr_t align,
+ uintptr_t brk)
+{
+ /* TODO: come up with a better estimate of how much to skip. */
+ uintptr_t skip = sizeof(uintptr_t) == 4 ? MiB : GiB;
+
+ for (uintptr_t base = skip; ; base += skip) {
+ base = ROUND_UP(base, align);
+ if (pgb_try_mmap_set(ga, base, brk)) {
+ return base;
+ }
+ if (base >= -skip) {
+ return -1;
+ }
+ }
+}
+
+static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
+ IntervalTreeRoot *root)
+{
+ for (int i = ga->nbounds - 1; i >= 0; --i) {
+ uintptr_t s = base + ga->bounds[i].lo;
+ uintptr_t l = base + ga->bounds[i].hi;
+ IntervalTreeNode *n;
+
+ if (l < s) {
+ /* Wraparound. Skip to advance S to mmap_min_addr. */
+ return mmap_min_addr - s;
+ }
+
+ n = interval_tree_iter_first(root, s, l);
+ if (n != NULL) {
+ /* Conflict. Skip to advance S to LAST + 1. */
+ return n->last - s + 1;
+ }
+ }
+ return 0; /* success */
+}
+
+static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
+ uintptr_t align, uintptr_t brk)
+{
+ uintptr_t last = sizeof(uintptr_t) == 4 ? MiB : GiB;
+ uintptr_t base, skip;
+
+ while (true) {
+ base = ROUND_UP(last, align);
+ if (base < last) {
+ return -1;
+ }
+
+ skip = pgb_try_itree(ga, base, root);
+ if (skip == 0) {
+ break;
+ }
+
+ last = base + skip;
+ if (last < base) {
+ return -1;
+ }
+ }
+
+ /*
+ * We've chosen 'base' based on holes in the interval tree,
+ * but we don't yet know if it is a valid host address.
+ * Because it is the first matching hole, if the host addresses
+ * are invalid we know there are no further matches.
+ */
+ return pgb_try_mmap_set(ga, base, brk) ? base : -1;
+}
+
+static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range, uintptr_t align)
+{
+ IntervalTreeRoot *root;
+ uintptr_t brk, ret;
+ PGBAddrs ga;
+
+ /* Try the identity map first. */
+ if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
+ brk = (uintptr_t)sbrk(0);
+ if (pgb_try_mmap_set(&ga, 0, brk)) {
+ guest_base = 0;
+ return;
+ }
+ }
+
+ /*
+ * Rebuild the address set for non-identity map.
+ * This differs in the mapping of the guest NULL page.
+ */
+ pgb_addr_set(&ga, image_range, commpage_range, false);
+
+ root = read_self_maps();
+
+ /* Read brk after we've read the maps, which will malloc. */
+ brk = (uintptr_t)sbrk(0);
+
+ if (!root) {
+ ret = pgb_find_fallback(&ga, align, brk);
+ } else {
+ /*
+ * Reserve the area close to the host brk.
+ * This will be freed with the rest of the tree.
+ */
+ IntervalTreeNode *b = g_new0(IntervalTreeNode, 1);
+ b->start = brk;
+ b->last = brk + 16 * MiB - 1;
+ interval_tree_insert(b, root);
+
+ ret = pgb_find_itree(&ga, root, align, brk);
+ free_self_maps(root);
+ }
+
+ if (ret == -1) {
+ int w = target_long_bits() / 4;
+
+ error_report("%s: Unable to find a guest_base to satisfy all "
+ "guest address mapping requirements", image_name);
+
+ for (int i = 0; i < ga.nbounds; ++i) {
+ error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
+ w, ga.bounds[i].lo,
+ w, ga.bounds[i].hi);
+ }
+ exit(EXIT_FAILURE);
+ }
+ guest_base = ret;
+}
+
+void probe_guest_base(const char *image_name, const PGBRange *image_range,
+ const PGBRange *commpage_range)
+{
+ /* In order to use host shmat, we must be able to honor SHMLBA. */
+ uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
+
+ /* Sanity check the guest binary. */
+ if (reserved_va && image_range && image_range->hi > reserved_va) {
+ error_report("%s: requires more than reserved virtual "
+ "address space (0x%" VADDR_PRIx " > 0x%lx)",
+ image_name, image_range->hi, reserved_va);
+ exit(EXIT_FAILURE);
+ }
+
+ if (have_guest_base) {
+ pgb_fixed(image_name, image_range, commpage_range, align);
+ } else {
+ pgb_dynamic(image_name, image_range, commpage_range, align);
+ }
+
+ assert(QEMU_IS_ALIGNED(guest_base, align));
+ qemu_log_mask(CPU_LOG_PAGE, "Locating guest address space "
+ "@ 0x%" PRIx64 "\n", (uint64_t)guest_base);
+}
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 207f814e15..e7c56af8ed 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -4,7 +4,6 @@
#include <sys/prctl.h>
#include <sys/resource.h>
-#include <sys/shm.h>
#include "qemu.h"
#include "user/tswap-target.h"
@@ -13,8 +12,6 @@
#include "exec/mmap-lock.h"
#include "exec/translation-block.h"
#include "exec/tswap.h"
-#include "user/guest-base.h"
-#include "user/mmap-min-addr.h"
#include "user-internals.h"
#include "signal-common.h"
#include "loader.h"
@@ -25,7 +22,6 @@
#include "qemu/queue.h"
#include "qemu/guest-random.h"
#include "qemu/units.h"
-#include "user/selfmap.h"
#include "qemu/lockable.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -758,310 +754,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
return sp;
}
-#if !defined(COMMPAGE) && !defined(HAVE_GUEST_COMMPAGE)
-bool init_guest_commpage(void) { return true; }
-#endif
-
-/**
- * pgb_try_mmap:
- * @addr: host start address
- * @addr_last: host last address
- * @keep: do not unmap the probe region
- *
- * Return 1 if [@addr, @addr_last] is not mapped in the host,
- * return 0 if it is not available to map, and -1 on mmap error.
- * If @keep, the region is left mapped on success, otherwise unmapped.
- */
-static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep)
-{
- size_t size = addr_last - addr + 1;
- void *p = mmap((void *)addr, size, PROT_NONE,
- MAP_ANONYMOUS | MAP_PRIVATE |
- MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
- int ret;
-
- if (p == MAP_FAILED) {
- return errno == EEXIST ? 0 : -1;
- }
- ret = p == (void *)addr;
- if (!keep || !ret) {
- munmap(p, size);
- }
- return ret;
-}
-
-/**
- * pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t size, uintptr_t brk)
- * @addr: host address
- * @addr_last: host last address
- * @brk: host brk
- *
- * Like pgb_try_mmap, but additionally reserve some memory following brk.
- */
-static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
- uintptr_t brk, bool keep)
-{
- uintptr_t brk_last = brk + 16 * MiB - 1;
-
- /* Do not map anything close to the host brk. */
- if (addr <= brk_last && brk <= addr_last) {
- return 0;
- }
- return pgb_try_mmap(addr, addr_last, keep);
-}
-
-/**
- * pgb_try_mmap_set:
- * @ga: set of guest addrs
- * @base: guest_base
- * @brk: host brk
- *
- * Return true if all @ga can be mapped by the host at @base.
- * On success, retain the mapping at index 0 for reserved_va.
- */
-
-typedef struct PGBAddrs {
- PGBRange bounds[3];
- int nbounds;
-} PGBAddrs;
-
-static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
-{
- for (int i = ga->nbounds - 1; i >= 0; --i) {
- if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
- ga->bounds[i].hi + base,
- brk, i == 0 && reserved_va) <= 0) {
- return false;
- }
- }
- return true;
-}
-
-/**
- * pgb_addr_set:
- * @ga: output set of guest addrs
- * @image_range: fixed guest image addresses
- * @identity: create for identity mapping
- *
- * Fill in @ga with the image, COMMPAGE and NULL page.
- */
-static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
- const PGBRange *commpage_range, bool try_identity)
-{
- int n;
-
- /*
- * With a low commpage, or a guest mapped very low,
- * we may not be able to use the identity map.
- */
- if (try_identity) {
- if (commpage_range && commpage_range->lo < mmap_min_addr) {
- return false;
- }
- if (image_range && image_range->lo < mmap_min_addr) {
- return false;
- }
- }
-
- memset(ga, 0, sizeof(*ga));
- n = 0;
-
- if (reserved_va) {
- ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
- ga->bounds[n].hi = reserved_va;
- n++;
- /* Low COMMPAGE and NULL handled by reserving from 0. */
- } else {
- /* Add any low COMMPAGE or NULL page. */
- if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
- ga->bounds[n].lo = 0;
- ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
- n++;
- }
-
- /* Add the guest image for ET_EXEC. */
- if (image_range) {
- ga->bounds[n++] = *image_range;
- }
- }
-
- /* Add any high COMMPAGE not covered by reserved_va. */
- if (commpage_range && reserved_va < commpage_range->hi) {
- ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
- ga->bounds[n].hi = commpage_range->hi;
- n++;
- }
-
- ga->nbounds = n;
- return true;
-}
-
-static void pgb_fail_in_use(const char *image_name)
-{
- error_report("%s: requires virtual address space that is in use "
- "(omit the -B option or choose a different value)",
- image_name);
- exit(EXIT_FAILURE);
-}
-
-static void pgb_fixed(const char *image_name, const PGBRange *image_range,
- const PGBRange *commpage_range, uintptr_t align)
-{
- PGBAddrs ga;
- uintptr_t brk = (uintptr_t)sbrk(0);
-
- if (!QEMU_IS_ALIGNED(guest_base, align)) {
- fprintf(stderr, "Requested guest base %p does not satisfy "
- "host minimum alignment (0x%" PRIxPTR ")\n",
- (void *)guest_base, align);
- exit(EXIT_FAILURE);
- }
-
- if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
- || !pgb_try_mmap_set(&ga, guest_base, brk)) {
- pgb_fail_in_use(image_name);
- }
-}
-
-/**
- * pgb_find_fallback:
- *
- * This is a fallback method for finding holes in the host address space
- * if we don't have the benefit of being able to access /proc/self/map.
- * It can potentially take a very long time as we can only dumbly iterate
- * up the host address space seeing if the allocation would work.
- */
-static uintptr_t pgb_find_fallback(const PGBAddrs *ga, uintptr_t align,
- uintptr_t brk)
-{
- /* TODO: come up with a better estimate of how much to skip. */
- uintptr_t skip = sizeof(uintptr_t) == 4 ? MiB : GiB;
-
- for (uintptr_t base = skip; ; base += skip) {
- base = ROUND_UP(base, align);
- if (pgb_try_mmap_set(ga, base, brk)) {
- return base;
- }
- if (base >= -skip) {
- return -1;
- }
- }
-}
-
-static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
- IntervalTreeRoot *root)
-{
- for (int i = ga->nbounds - 1; i >= 0; --i) {
- uintptr_t s = base + ga->bounds[i].lo;
- uintptr_t l = base + ga->bounds[i].hi;
- IntervalTreeNode *n;
-
- if (l < s) {
- /* Wraparound. Skip to advance S to mmap_min_addr. */
- return mmap_min_addr - s;
- }
-
- n = interval_tree_iter_first(root, s, l);
- if (n != NULL) {
- /* Conflict. Skip to advance S to LAST + 1. */
- return n->last - s + 1;
- }
- }
- return 0; /* success */
-}
-
-static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
- uintptr_t align, uintptr_t brk)
-{
- uintptr_t last = sizeof(uintptr_t) == 4 ? MiB : GiB;
- uintptr_t base, skip;
-
- while (true) {
- base = ROUND_UP(last, align);
- if (base < last) {
- return -1;
- }
-
- skip = pgb_try_itree(ga, base, root);
- if (skip == 0) {
- break;
- }
-
- last = base + skip;
- if (last < base) {
- return -1;
- }
- }
-
- /*
- * We've chosen 'base' based on holes in the interval tree,
- * but we don't yet know if it is a valid host address.
- * Because it is the first matching hole, if the host addresses
- * are invalid we know there are no further matches.
- */
- return pgb_try_mmap_set(ga, base, brk) ? base : -1;
-}
-
-static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
- const PGBRange *commpage_range, uintptr_t align)
-{
- IntervalTreeRoot *root;
- uintptr_t brk, ret;
- PGBAddrs ga;
-
- /* Try the identity map first. */
- if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
- brk = (uintptr_t)sbrk(0);
- if (pgb_try_mmap_set(&ga, 0, brk)) {
- guest_base = 0;
- return;
- }
- }
-
- /*
- * Rebuild the address set for non-identity map.
- * This differs in the mapping of the guest NULL page.
- */
- pgb_addr_set(&ga, image_range, commpage_range, false);
-
- root = read_self_maps();
-
- /* Read brk after we've read the maps, which will malloc. */
- brk = (uintptr_t)sbrk(0);
-
- if (!root) {
- ret = pgb_find_fallback(&ga, align, brk);
- } else {
- /*
- * Reserve the area close to the host brk.
- * This will be freed with the rest of the tree.
- */
- IntervalTreeNode *b = g_new0(IntervalTreeNode, 1);
- b->start = brk;
- b->last = brk + 16 * MiB - 1;
- interval_tree_insert(b, root);
-
- ret = pgb_find_itree(&ga, root, align, brk);
- free_self_maps(root);
- }
-
- if (ret == -1) {
- int w = TARGET_LONG_BITS / 4;
-
- error_report("%s: Unable to find a guest_base to satisfy all "
- "guest address mapping requirements", image_name);
-
- for (int i = 0; i < ga.nbounds; ++i) {
- error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
- w, ga.bounds[i].lo,
- w, ga.bounds[i].hi);
- }
- exit(EXIT_FAILURE);
- }
- guest_base = ret;
-}
-
-void probe_guest_base(const char *image_name, const PGBRange *image_range)
+void linux_probe_guest_base(const char *image_name,
+ const PGBRange *image_range)
{
#ifdef COMMPAGE
const PGBRange * const commpage_range = &(PGBRange){
@@ -1071,32 +765,15 @@ void probe_guest_base(const char *image_name, const PGBRange *image_range)
const PGBRange * const commpage_range = NULL;
#endif
- /* In order to use host shmat, we must be able to honor SHMLBA. */
- uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
-
- /* Sanity check the guest binary. */
- if (reserved_va && image_range && image_range->hi > reserved_va) {
- error_report("%s: requires more than reserved virtual "
- "address space (0x%" VADDR_PRIx " > 0x%lx)",
- image_name, image_range->hi, reserved_va);
- exit(EXIT_FAILURE);
- }
-
- if (have_guest_base) {
- pgb_fixed(image_name, image_range, commpage_range, align);
- } else {
- pgb_dynamic(image_name, image_range, commpage_range, align);
- }
+ probe_guest_base(image_name, image_range, commpage_range);
/* Reserve and initialize the commpage. */
+#if defined(COMMPAGE) || defined(HAVE_GUEST_COMMPAGE)
if (!init_guest_commpage()) {
/* We have already probed for the commpage being free. */
g_assert_not_reached();
}
-
- assert(QEMU_IS_ALIGNED(guest_base, align));
- qemu_log_mask(CPU_LOG_PAGE, "Locating guest address space "
- "@ 0x%" PRIx64 "\n", (uint64_t)guest_base);
+#endif
}
enum {
@@ -1345,10 +1022,10 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
* Make sure that the low address does not conflict with
* MMAP_MIN_ADDR or the QEMU application itself.
*/
- probe_guest_base(image_name, &range);
+ linux_probe_guest_base(image_name, &range);
} else {
/* The binary is dynamic; we still need to select guest_base. */
- probe_guest_base(image_name, NULL);
+ linux_probe_guest_base(image_name, NULL);
/*
* Avoid collision with the loader by providing a different
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index 45d69040c6..a551f083a4 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -261,7 +261,7 @@ static int load_flat_file(struct linux_binprm * bprm,
/*
* Allocate the address space.
*/
- probe_guest_base(bprm->filename, NULL);
+ linux_probe_guest_base(bprm->filename, NULL);
/*
* there are a couple of cases here, the separate code/data
diff --git a/common-user/meson.build b/common-user/meson.build
index 831a7273fb..78dea613d0 100644
--- a/common-user/meson.build
+++ b/common-user/meson.build
@@ -6,6 +6,7 @@ common_user_inc += include_directories('host/' / host_arch)
user_ss.add(files(
'mmap-min-addr.c',
+ 'probe-guest-base.c',
'safe-syscall.S',
'safe-syscall-error.c',
'selfmap.c',
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 12/13] bsd-user: Use probe_guest_base
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (10 preceding siblings ...)
2026-07-10 23:01 ` [PULL 11/13] common-user: Move probe_guest_base from linux-user Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-10 23:01 ` [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c Richard Henderson
2026-07-11 8:39 ` [PULL 00/13] user patch queue Stefan Hajnoczi
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel
Merge the PT_LOAD loop with the PT_INTERP loop, as the XXX
comment suggests. Pass the probed range to probe_guest_base.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
bsd-user/elfload.c | 37 +++++++++++++++++++------------------
bsd-user/main.c | 39 ++++-----------------------------------
2 files changed, 23 insertions(+), 53 deletions(-)
diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c
index 3bca0cc9ed..a87a892eb8 100644
--- a/bsd-user/elfload.c
+++ b/bsd-user/elfload.c
@@ -22,6 +22,7 @@
#include "qemu.h"
#include "disas/disas.h"
#include "qemu/path.h"
+#include "user/probe-guest-base.h"
static abi_ulong target_auxents; /* Where the AUX entries are in target */
static size_t target_auxents_sz; /* Size of AUX entries including AT_NULL */
@@ -610,8 +611,9 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
abi_ulong elf_brk;
int error, retval;
char *elf_interpreter;
- abi_ulong baddr, elf_entry, et_dyn_addr, interp_load_addr = 0;
+ abi_ulong elf_entry, et_dyn_addr, interp_load_addr = 0;
abi_ulong reloc_func_desc = 0;
+ PGBRange range = { -1, 0 };
load_addr = 0;
elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
@@ -654,10 +656,10 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
elf_brk = 0;
-
elf_interpreter = NULL;
- for (i = 0; i < elf_ex.e_phnum; i++) {
- if (elf_ppnt->p_type == PT_INTERP) {
+ for (i = 0; i < elf_ex.e_phnum; i++, elf_ppnt++) {
+ switch (elf_ppnt->p_type) {
+ case PT_INTERP:
if (elf_interpreter != NULL) {
free(elf_phdata);
free(elf_interpreter);
@@ -709,8 +711,14 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
close(bprm->fd);
return retval;
}
+ break;
+
+ case PT_LOAD:
+ range.lo = MIN(range.lo, elf_ppnt->p_vaddr);
+ range.hi = MAX(range.hi,
+ elf_ppnt->p_vaddr + elf_ppnt->p_memsz - 1);
+ break;
}
- elf_ppnt++;
}
/* Some simple consistency checks for the interpreter */
@@ -740,19 +748,12 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
info->end_code = 0;
elf_entry = (abi_ulong) elf_ex.e_entry;
- /* XXX Join this with PT_INTERP search? */
- baddr = 0;
- for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
- if (elf_ppnt->p_type != PT_LOAD) {
- continue;
- }
- baddr = elf_ppnt->p_vaddr;
- break;
- }
-
et_dyn_addr = 0;
- if (elf_ex.e_type == ET_DYN && baddr == 0) {
- et_dyn_addr = ELF_ET_DYN_LOAD_ADDR;
+ if (elf_ex.e_type == ET_DYN) {
+ probe_guest_base(bprm->filename, NULL, NULL);
+ et_dyn_addr = ELF_ET_DYN_LOAD_ADDR - range.lo;
+ } else {
+ probe_guest_base(bprm->filename, &range, NULL);
}
/*
@@ -766,7 +767,7 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
info->elf_flags = elf_ex.e_flags;
error = load_elf_sections(&elf_ex, elf_phdata, bprm->fd, et_dyn_addr,
- &load_addr);
+ &load_addr);
for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
if (elf_ppnt->p_type != PT_LOAD) {
continue;
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 4f1544342e..5277b4e099 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -541,41 +541,10 @@ int main(int argc, char **argv)
}
}
- /*
- * If reserving host virtual address space, do so now.
- * Combined with '-B', ensure that the chosen range is free.
- */
- if (reserved_va) {
- void *p;
-
- if (have_guest_base) {
- p = mmap((void *)guest_base, reserved_va + 1, PROT_NONE,
- MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_EXCL, -1, 0);
- } else {
- p = mmap(NULL, reserved_va + 1, PROT_NONE,
- MAP_ANON | MAP_PRIVATE, -1, 0);
- }
- if (p == MAP_FAILED) {
- const char *err = strerror(errno);
- char *sz = size_to_str(reserved_va + 1);
-
- if (have_guest_base) {
- error_report("Cannot allocate %s bytes at -B %p for guest "
- "address space: %s", sz, (void *)guest_base, err);
- } else {
- error_report("Cannot allocate %s bytes for guest "
- "address space: %s", sz, err);
- }
- exit(1);
- }
- guest_base = (uintptr_t)p;
- have_guest_base = true;
-
- /* Ensure that mmap_next_start is within range. */
- if (reserved_va <= mmap_next_start) {
- mmap_next_start = (reserved_va / 4 * 3)
- & TARGET_PAGE_MASK & qemu_host_page_mask;
- }
+ /* Ensure that mmap_next_start is within range. */
+ if (reserved_va && reserved_va <= mmap_next_start) {
+ mmap_next_start = ((reserved_va / 4 * 3)
+ & TARGET_PAGE_MASK & qemu_host_page_mask);
}
if (loader_exec(filename, argv + optind, target_environ, regs, info,
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (11 preceding siblings ...)
2026-07-10 23:01 ` [PULL 12/13] bsd-user: Use probe_guest_base Richard Henderson
@ 2026-07-10 23:01 ` Richard Henderson
2026-07-11 8:39 ` [PULL 00/13] user patch queue Stefan Hajnoczi
13 siblings, 0 replies; 15+ messages in thread
From: Richard Henderson @ 2026-07-10 23:01 UTC (permalink / raw)
To: qemu-devel; +Cc: Helge Deller, Warner Losh
Unify the definitions of guest_base and have_guest_base.
Reviewed-by: Helge Deller <deller@gmx.de>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
bsd-user/main.c | 3 +--
common-user/probe-guest-base.c | 4 ++++
linux-user/main.c | 2 --
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 5277b4e099..694481ae3e 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -63,8 +63,7 @@ intptr_t qemu_host_page_mask;
static bool opt_one_insn_per_tb;
static unsigned long opt_tb_size;
-uintptr_t guest_base;
-bool have_guest_base;
+
/*
* When running 32-on-64 we should make sure we can fit all of the possible
* guest address space into a contiguous chunk of virtual host memory.
diff --git a/common-user/probe-guest-base.c b/common-user/probe-guest-base.c
index 77722fe63f..6f1247cf1a 100644
--- a/common-user/probe-guest-base.c
+++ b/common-user/probe-guest-base.c
@@ -7,6 +7,7 @@
#include "qemu/log.h"
#include "user/guest-base.h"
#include "user/mmap-min-addr.h"
+#include "user/guest-base.h"
#include "user/guest-host.h"
#include "user/probe-guest-base.h"
#include "user/selfmap.h"
@@ -18,6 +19,9 @@
#define MAP_FIXED_NOREPLACE (MAP_FIXED | MAP_EXCL)
#endif
+uintptr_t guest_base;
+bool have_guest_base;
+
/**
* pgb_try_mmap:
* @addr: host start address
diff --git a/linux-user/main.c b/linux-user/main.c
index 01cf5bf079..60a695b7ca 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -79,8 +79,6 @@ static envlist_t *envlist;
static const char *cpu_model;
static const char *cpu_type;
static const char *seed_optarg;
-uintptr_t guest_base;
-bool have_guest_base;
/*
* Used to implement backwards-compatibility for the `-strace`, and
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PULL 00/13] user patch queue
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
` (12 preceding siblings ...)
2026-07-10 23:01 ` [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c Richard Henderson
@ 2026-07-11 8:39 ` Stefan Hajnoczi
13 siblings, 0 replies; 15+ messages in thread
From: Stefan Hajnoczi @ 2026-07-11 8:39 UTC (permalink / raw)
To: Richard Henderson; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-07-11 8:39 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10 23:01 [PULL 00/13] user patch queue Richard Henderson
2026-07-10 23:01 ` [PULL 01/13] linux-user: Introduce PGBRange Richard Henderson
2026-07-10 23:01 ` [PULL 02/13] linux-user: Use PGBRange in load_elf_image Richard Henderson
2026-07-10 23:01 ` [PULL 03/13] linux-user: Drop hiaddr out-of-range check in probe_guest_base Richard Henderson
2026-07-10 23:01 ` [PULL 04/13] linux-user: Pass image_range to probe_guest_base Richard Henderson
2026-07-10 23:01 ` [PULL 05/13] linux-user: Use PGBRange for commpage Richard Henderson
2026-07-10 23:01 ` [PULL 06/13] common-user: Move mmap_min_addr from linux-user Richard Henderson
2026-07-10 23:01 ` [PULL 07/13] common-user: Initialize mmap_min_addr for FreeBSD Richard Henderson
2026-07-10 23:01 ` [PULL 08/13] include/user/guest-host: Include missing cpu.h Richard Henderson
2026-07-10 23:01 ` [PULL 09/13] common-user: Move selfmap from util Richard Henderson
2026-07-10 23:01 ` [PULL 10/13] common-user: Implement read_self_maps for FreeBSD Richard Henderson
2026-07-10 23:01 ` [PULL 11/13] common-user: Move probe_guest_base from linux-user Richard Henderson
2026-07-10 23:01 ` [PULL 12/13] bsd-user: Use probe_guest_base Richard Henderson
2026-07-10 23:01 ` [PULL 13/13] common-user: Move guest_base, have_guest_base to probe-guest-base.c Richard Henderson
2026-07-11 8:39 ` [PULL 00/13] user patch queue Stefan Hajnoczi
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.