1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
/* 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-base.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
uintptr_t guest_base;
bool have_guest_base;
/**
* 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);
}
|