linux-user: Rewrite fixed probe_guest_base
Create a set of subroutines to collect a set of guest addresses, all of which must be mappable on the host. Use this within the renamed pgb_fixed subroutine to validate the user's choice of guest_base specified by the -B command-line option. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
0c441aeb39
commit
06f38c6688
@ -2504,6 +2504,157 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#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 {
|
||||||
|
uintptr_t bounds[3][2]; /* start/last pairs */
|
||||||
|
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,
|
||||||
|
brk, i == 0 && reserved_va) <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pgb_addr_set:
|
||||||
|
* @ga: output set of guest addrs
|
||||||
|
* @guest_loaddr: guest image low address
|
||||||
|
* @guest_loaddr: guest image high address
|
||||||
|
* @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)
|
||||||
|
{
|
||||||
|
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 (LO_COMMPAGE != -1 && LO_COMMPAGE < mmap_min_addr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (guest_loaddr != 0 && guest_loaddr < mmap_min_addr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ga, 0, sizeof(*ga));
|
||||||
|
n = 0;
|
||||||
|
|
||||||
|
if (reserved_va) {
|
||||||
|
ga->bounds[n][0] = try_identity ? mmap_min_addr : 0;
|
||||||
|
ga->bounds[n][1] = 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;
|
||||||
|
n++;
|
||||||
|
} else if (!try_identity) {
|
||||||
|
ga->bounds[n][0] = 0;
|
||||||
|
ga->bounds[n][1] = 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;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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][0] = HI_COMMPAGE & qemu_host_page_mask;
|
||||||
|
ga->bounds[n][1] = HI_COMMPAGE + TARGET_PAGE_SIZE - 1;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
ga->nbounds = n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void pgb_fail_in_use(const char *image_name)
|
static void pgb_fail_in_use(const char *image_name)
|
||||||
{
|
{
|
||||||
error_report("%s: requires virtual address space that is in use "
|
error_report("%s: requires virtual address space that is in use "
|
||||||
@ -2512,33 +2663,21 @@ static void pgb_fail_in_use(const char *image_name)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr,
|
static void pgb_fixed(const char *image_name, uintptr_t guest_loaddr,
|
||||||
abi_ulong guest_hiaddr, long align)
|
uintptr_t guest_hiaddr, uintptr_t align)
|
||||||
{
|
{
|
||||||
const int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
|
PGBAddrs ga;
|
||||||
void *addr, *test;
|
uintptr_t brk = (uintptr_t)sbrk(0);
|
||||||
|
|
||||||
if (!QEMU_IS_ALIGNED(guest_base, align)) {
|
if (!QEMU_IS_ALIGNED(guest_base, align)) {
|
||||||
fprintf(stderr, "Requested guest base %p does not satisfy "
|
fprintf(stderr, "Requested guest base %p does not satisfy "
|
||||||
"host minimum alignment (0x%lx)\n",
|
"host minimum alignment (0x%" PRIxPTR ")\n",
|
||||||
(void *)guest_base, align);
|
(void *)guest_base, align);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (!pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, !guest_base)
|
||||||
* Expand the allocation to the entire reserved_va.
|
|| !pgb_try_mmap_set(&ga, guest_base, brk)) {
|
||||||
* Exclude the mmap_min_addr hole.
|
|
||||||
*/
|
|
||||||
if (reserved_va) {
|
|
||||||
guest_loaddr = (guest_base >= mmap_min_addr ? 0
|
|
||||||
: mmap_min_addr - guest_base);
|
|
||||||
guest_hiaddr = reserved_va;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reserve the address space for the binary, or reserved_va. */
|
|
||||||
test = g2h_untagged(guest_loaddr);
|
|
||||||
addr = mmap(test, guest_hiaddr - guest_loaddr + 1, PROT_NONE, flags, -1, 0);
|
|
||||||
if (test != addr) {
|
|
||||||
pgb_fail_in_use(image_name);
|
pgb_fail_in_use(image_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2784,7 +2923,7 @@ void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (have_guest_base) {
|
if (have_guest_base) {
|
||||||
pgb_have_guest_base(image_name, guest_loaddr, guest_hiaddr, align);
|
pgb_fixed(image_name, guest_loaddr, guest_hiaddr, align);
|
||||||
} else if (reserved_va) {
|
} else if (reserved_va) {
|
||||||
pgb_reserved_va(image_name, guest_loaddr, guest_hiaddr, align);
|
pgb_reserved_va(image_name, guest_loaddr, guest_hiaddr, align);
|
||||||
} else if (guest_loaddr) {
|
} else if (guest_loaddr) {
|
||||||
@ -2795,13 +2934,8 @@ void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
|
|||||||
|
|
||||||
/* Reserve and initialize the commpage. */
|
/* Reserve and initialize the commpage. */
|
||||||
if (!init_guest_commpage()) {
|
if (!init_guest_commpage()) {
|
||||||
/*
|
/* We have already probed for the commpage being free. */
|
||||||
* With have_guest_base, the user has selected the address and
|
g_assert_not_reached();
|
||||||
* we are trying to work with that. Otherwise, we have selected
|
|
||||||
* free space and init_guest_commpage must succeeded.
|
|
||||||
*/
|
|
||||||
assert(have_guest_base);
|
|
||||||
pgb_fail_in_use(image_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(QEMU_IS_ALIGNED(guest_base, align));
|
assert(QEMU_IS_ALIGNED(guest_base, align));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user