]> git.proxmox.com Git - mirror_qemu.git/blame - linux-user/mmap.c
python: move qmp-shell under the AQMP package
[mirror_qemu.git] / linux-user / mmap.c
CommitLineData
54936004
FB
1/*
2 * mmap support for qemu
5fafdf24 3 *
54936004
FB
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
8167ee88 17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
54936004 18 */
d39594e9 19#include "qemu/osdep.h"
11d96056 20#include "trace.h"
10d0d505 21#include "exec/log.h"
54936004 22#include "qemu.h"
3b249d26 23#include "user-internals.h"
5423e6d3 24#include "user-mmap.h"
54936004 25
1e6eec8b 26static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
dfd3f85c 27static __thread int mmap_lock_count;
c8a706fe
PB
28
29void mmap_lock(void)
30{
31 if (mmap_lock_count++ == 0) {
32 pthread_mutex_lock(&mmap_mutex);
33 }
34}
35
36void mmap_unlock(void)
37{
38 if (--mmap_lock_count == 0) {
39 pthread_mutex_unlock(&mmap_mutex);
40 }
41}
d5975363 42
301e40ed
AB
43bool have_mmap_lock(void)
44{
45 return mmap_lock_count > 0 ? true : false;
46}
47
d5975363
PB
48/* Grab lock to make sure things are in a consistent state after fork(). */
49void mmap_fork_start(void)
50{
51 if (mmap_lock_count)
52 abort();
53 pthread_mutex_lock(&mmap_mutex);
54}
55
56void mmap_fork_end(int child)
57{
58 if (child)
59 pthread_mutex_init(&mmap_mutex, NULL);
60 else
61 pthread_mutex_unlock(&mmap_mutex);
62}
c8a706fe 63
9dba3ca5
RH
64/*
65 * Validate target prot bitmask.
66 * Return the prot bitmask for the host in *HOST_PROT.
67 * Return 0 if the target prot bitmask is invalid, otherwise
68 * the internal qemu page_flags (which will include PAGE_VALID).
69 */
70static int validate_prot_to_pageflags(int *host_prot, int prot)
71{
72 int valid = PROT_READ | PROT_WRITE | PROT_EXEC | TARGET_PROT_SEM;
73 int page_flags = (prot & PAGE_BITS) | PAGE_VALID;
74
75 /*
76 * For the host, we need not pass anything except read/write/exec.
77 * While PROT_SEM is allowed by all hosts, it is also ignored, so
78 * don't bother transforming guest bit to host bit. Any other
79 * target-specific prot bits will not be understood by the host
80 * and will need to be encoded into page_flags for qemu emulation.
4eaa960d
RH
81 *
82 * Pages that are executable by the guest will never be executed
83 * by the host, but the host will need to be able to read them.
9dba3ca5 84 */
4eaa960d
RH
85 *host_prot = (prot & (PROT_READ | PROT_WRITE))
86 | (prot & PROT_EXEC ? PROT_READ : 0);
9dba3ca5 87
be5d6f48 88#ifdef TARGET_AARCH64
d109b46d 89 {
be5d6f48 90 ARMCPU *cpu = ARM_CPU(thread_cpu);
d109b46d
RH
91
92 /*
93 * The PROT_BTI bit is only accepted if the cpu supports the feature.
94 * Since this is the unusual case, don't bother checking unless
95 * the bit has been requested. If set and valid, record the bit
96 * within QEMU's page_flags.
97 */
98 if ((prot & TARGET_PROT_BTI) && cpu_isar_feature(aa64_bti, cpu)) {
be5d6f48
RH
99 valid |= TARGET_PROT_BTI;
100 page_flags |= PAGE_BTI;
101 }
d109b46d
RH
102 /* Similarly for the PROT_MTE bit. */
103 if ((prot & TARGET_PROT_MTE) && cpu_isar_feature(aa64_mte, cpu)) {
104 valid |= TARGET_PROT_MTE;
105 page_flags |= PAGE_MTE;
106 }
be5d6f48
RH
107 }
108#endif
109
9dba3ca5
RH
110 return prot & ~valid ? 0 : page_flags;
111}
112
53a5960a 113/* NOTE: all the constants are the HOST ones, but addresses are target. */
9dba3ca5 114int target_mprotect(abi_ulong start, abi_ulong len, int target_prot)
54936004 115{
992f48a0 116 abi_ulong end, host_start, host_end, addr;
9dba3ca5 117 int prot1, ret, page_flags, host_prot;
54936004 118
9dba3ca5 119 trace_target_mprotect(start, len, target_prot);
54936004 120
9dba3ca5 121 if ((start & ~TARGET_PAGE_MASK) != 0) {
78cf3390 122 return -TARGET_EINVAL;
9dba3ca5
RH
123 }
124 page_flags = validate_prot_to_pageflags(&host_prot, target_prot);
125 if (!page_flags) {
126 return -TARGET_EINVAL;
127 }
54936004
FB
128 len = TARGET_PAGE_ALIGN(len);
129 end = start + len;
46b12f46 130 if (!guest_range_valid_untagged(start, len)) {
78cf3390 131 return -TARGET_ENOMEM;
ebf9a363 132 }
9dba3ca5 133 if (len == 0) {
54936004 134 return 0;
9dba3ca5 135 }
3b46e624 136
c8a706fe 137 mmap_lock();
83fb7adf 138 host_start = start & qemu_host_page_mask;
54936004
FB
139 host_end = HOST_PAGE_ALIGN(end);
140 if (start > host_start) {
141 /* handle host page containing start */
9dba3ca5
RH
142 prot1 = host_prot;
143 for (addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
54936004
FB
144 prot1 |= page_get_flags(addr);
145 }
83fb7adf 146 if (host_end == host_start + qemu_host_page_size) {
9dba3ca5 147 for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
d418c81e
FB
148 prot1 |= page_get_flags(addr);
149 }
150 end = host_end;
151 }
3e8f1628 152 ret = mprotect(g2h_untagged(host_start), qemu_host_page_size,
9dba3ca5
RH
153 prot1 & PAGE_BITS);
154 if (ret != 0) {
c8a706fe 155 goto error;
9dba3ca5 156 }
83fb7adf 157 host_start += qemu_host_page_size;
54936004
FB
158 }
159 if (end < host_end) {
9dba3ca5
RH
160 prot1 = host_prot;
161 for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
54936004
FB
162 prot1 |= page_get_flags(addr);
163 }
3e8f1628 164 ret = mprotect(g2h_untagged(host_end - qemu_host_page_size),
9dba3ca5
RH
165 qemu_host_page_size, prot1 & PAGE_BITS);
166 if (ret != 0) {
c8a706fe 167 goto error;
9dba3ca5 168 }
83fb7adf 169 host_end -= qemu_host_page_size;
54936004 170 }
3b46e624 171
54936004
FB
172 /* handle the pages in the middle */
173 if (host_start < host_end) {
3e8f1628
RH
174 ret = mprotect(g2h_untagged(host_start),
175 host_end - host_start, host_prot);
9dba3ca5 176 if (ret != 0) {
c8a706fe 177 goto error;
9dba3ca5 178 }
54936004 179 }
9dba3ca5 180 page_set_flags(start, start + len, page_flags);
c8a706fe 181 mmap_unlock();
54936004 182 return 0;
c8a706fe
PB
183error:
184 mmap_unlock();
185 return ret;
54936004
FB
186}
187
188/* map an incomplete host page */
992f48a0
BS
189static int mmap_frag(abi_ulong real_start,
190 abi_ulong start, abi_ulong end,
191 int prot, int flags, int fd, abi_ulong offset)
54936004 192{
80210bcd 193 abi_ulong real_end, addr;
53a5960a 194 void *host_start;
54936004
FB
195 int prot1, prot_new;
196
53a5960a 197 real_end = real_start + qemu_host_page_size;
3e8f1628 198 host_start = g2h_untagged(real_start);
54936004
FB
199
200 /* get the protection of the target pages outside the mapping */
201 prot1 = 0;
53a5960a 202 for(addr = real_start; addr < real_end; addr++) {
54936004
FB
203 if (addr < start || addr >= end)
204 prot1 |= page_get_flags(addr);
205 }
3b46e624 206
54936004
FB
207 if (prot1 == 0) {
208 /* no page was there, so we allocate one */
80210bcd
TS
209 void *p = mmap(host_start, qemu_host_page_size, prot,
210 flags | MAP_ANONYMOUS, -1, 0);
211 if (p == MAP_FAILED)
212 return -1;
53a5960a 213 prot1 = prot;
54936004
FB
214 }
215 prot1 &= PAGE_BITS;
216
217 prot_new = prot | prot1;
218 if (!(flags & MAP_ANONYMOUS)) {
219 /* msync() won't work here, so we return an error if write is
220 possible while it is a shared mapping */
221 if ((flags & MAP_TYPE) == MAP_SHARED &&
222 (prot & PROT_WRITE))
ee636500 223 return -1;
54936004
FB
224
225 /* adjust protection to be able to read */
226 if (!(prot1 & PROT_WRITE))
53a5960a 227 mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
3b46e624 228
54936004 229 /* read the corresponding file data */
3e8f1628 230 if (pread(fd, g2h_untagged(start), end - start, offset) == -1)
fb7e378c 231 return -1;
3b46e624 232
54936004
FB
233 /* put final protection */
234 if (prot_new != (prot1 | PROT_WRITE))
53a5960a 235 mprotect(host_start, qemu_host_page_size, prot_new);
54936004 236 } else {
54936004 237 if (prot_new != prot1) {
53a5960a 238 mprotect(host_start, qemu_host_page_size, prot_new);
54936004 239 }
e6deac9c 240 if (prot_new & PROT_WRITE) {
3e8f1628 241 memset(g2h_untagged(start), 0, end - start);
e6deac9c 242 }
54936004
FB
243 }
244 return 0;
245}
246
14f24e14 247#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
aab613fb
LY
248#ifdef TARGET_AARCH64
249# define TASK_UNMAPPED_BASE 0x5500000000
250#else
14f24e14 251# define TASK_UNMAPPED_BASE (1ul << 38)
aab613fb 252#endif
a03e2d42 253#else
14f24e14 254# define TASK_UNMAPPED_BASE 0x40000000
a03e2d42 255#endif
59e9d91c 256abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
a03e2d42 257
0776590d
PB
258unsigned long last_brk;
259
68a1c816
PB
260/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
261 of guest address space. */
30ab9ef2
RH
262static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
263 abi_ulong align)
68a1c816 264{
30ab9ef2 265 abi_ulong addr, end_addr, incr = qemu_host_page_size;
68a1c816 266 int prot;
30ab9ef2 267 bool looped = false;
68a1c816 268
b76f21a7 269 if (size > reserved_va) {
68a1c816
PB
270 return (abi_ulong)-1;
271 }
272
30ab9ef2
RH
273 /* Note that start and size have already been aligned by mmap_find_vma. */
274
59e9d91c 275 end_addr = start + size;
30ab9ef2
RH
276 if (start > reserved_va - size) {
277 /* Start at the top of the address space. */
278 end_addr = ((reserved_va - size) & -align) + size;
279 looped = true;
59e9d91c 280 }
59e9d91c 281
30ab9ef2
RH
282 /* Search downward from END_ADDR, checking to see if a page is in use. */
283 addr = end_addr;
59e9d91c 284 while (1) {
30ab9ef2 285 addr -= incr;
59e9d91c 286 if (addr > end_addr) {
68a1c816 287 if (looped) {
30ab9ef2 288 /* Failure. The entire address space has been searched. */
68a1c816
PB
289 return (abi_ulong)-1;
290 }
30ab9ef2
RH
291 /* Re-start at the top of the address space. */
292 addr = end_addr = ((reserved_va - size) & -align) + size;
293 looped = true;
294 } else {
295 prot = page_get_flags(addr);
296 if (prot) {
297 /* Page in use. Restart below this page. */
298 addr = end_addr = ((addr - size) & -align) + size;
299 } else if (addr && addr + size == end_addr) {
300 /* Success! All pages between ADDR and END_ADDR are free. */
301 if (start == mmap_next_start) {
302 mmap_next_start = addr;
303 }
304 return addr;
305 }
68a1c816
PB
306 }
307 }
68a1c816
PB
308}
309
fe3b4152
KS
310/*
311 * Find and reserve a free memory area of size 'size'. The search
312 * starts at 'start'.
313 * It must be called with mmap_lock() held.
314 * Return -1 if error.
315 */
30ab9ef2 316abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
a03e2d42 317{
14f24e14 318 void *ptr, *prev;
fe3b4152 319 abi_ulong addr;
14f24e14 320 int wrapped, repeat;
fe3b4152 321
443b7505
RH
322 align = MAX(align, qemu_host_page_size);
323
fe3b4152 324 /* If 'start' == 0, then a default start address is used. */
14f24e14 325 if (start == 0) {
fe3b4152 326 start = mmap_next_start;
14f24e14
RH
327 } else {
328 start &= qemu_host_page_mask;
329 }
30ab9ef2 330 start = ROUND_UP(start, align);
14f24e14
RH
331
332 size = HOST_PAGE_ALIGN(size);
fe3b4152 333
b76f21a7 334 if (reserved_va) {
30ab9ef2 335 return mmap_find_vma_reserved(start, size, align);
68a1c816
PB
336 }
337
a03e2d42 338 addr = start;
14f24e14
RH
339 wrapped = repeat = 0;
340 prev = 0;
fe3b4152 341
14f24e14 342 for (;; prev = ptr) {
fe3b4152
KS
343 /*
344 * Reserve needed memory area to avoid a race.
345 * It should be discarded using:
346 * - mmap() with MAP_FIXED flag
347 * - mremap() with MREMAP_FIXED flag
348 * - shmat() with SHM_REMAP flag
349 */
3e8f1628 350 ptr = mmap(g2h_untagged(addr), size, PROT_NONE,
fe3b4152
KS
351 MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
352
353 /* ENOMEM, if host address space has no memory */
14f24e14 354 if (ptr == MAP_FAILED) {
fe3b4152 355 return (abi_ulong)-1;
14f24e14
RH
356 }
357
358 /* Count the number of sequential returns of the same address.
359 This is used to modify the search algorithm below. */
360 repeat = (ptr == prev ? repeat + 1 : 0);
361
362 if (h2g_valid(ptr + size - 1)) {
363 addr = h2g(ptr);
fe3b4152 364
30ab9ef2 365 if ((addr & (align - 1)) == 0) {
14f24e14
RH
366 /* Success. */
367 if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
368 mmap_next_start = addr + size;
369 }
370 return addr;
371 }
fe3b4152 372
14f24e14
RH
373 /* The address is not properly aligned for the target. */
374 switch (repeat) {
375 case 0:
376 /* Assume the result that the kernel gave us is the
377 first with enough free space, so start again at the
378 next higher target page. */
30ab9ef2 379 addr = ROUND_UP(addr, align);
14f24e14
RH
380 break;
381 case 1:
382 /* Sometimes the kernel decides to perform the allocation
383 at the top end of memory instead. */
30ab9ef2 384 addr &= -align;
14f24e14
RH
385 break;
386 case 2:
387 /* Start over at low memory. */
388 addr = 0;
389 break;
390 default:
391 /* Fail. This unaligned block must the last. */
392 addr = -1;
393 break;
394 }
395 } else {
396 /* Since the result the kernel gave didn't fit, start
397 again at low memory. If any repetition, fail. */
398 addr = (repeat ? -1 : 0);
399 }
400
401 /* Unmap and try again. */
fe3b4152 402 munmap(ptr, size);
fe3b4152 403
14f24e14 404 /* ENOMEM if we checked the whole of the target address space. */
d0b3e4f5 405 if (addr == (abi_ulong)-1) {
a03e2d42 406 return (abi_ulong)-1;
14f24e14
RH
407 } else if (addr == 0) {
408 if (wrapped) {
409 return (abi_ulong)-1;
410 }
411 wrapped = 1;
412 /* Don't actually use 0 when wrapping, instead indicate
8186e783 413 that we'd truly like an allocation in low memory. */
14f24e14
RH
414 addr = (mmap_min_addr > TARGET_PAGE_SIZE
415 ? TARGET_PAGE_ALIGN(mmap_min_addr)
416 : TARGET_PAGE_SIZE);
417 } else if (wrapped && addr >= start) {
418 return (abi_ulong)-1;
419 }
a03e2d42 420 }
a03e2d42
FB
421}
422
54936004 423/* NOTE: all the constants are the HOST ones */
9dba3ca5 424abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
992f48a0 425 int flags, int fd, abi_ulong offset)
54936004 426{
992f48a0 427 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
9dba3ca5 428 int page_flags, host_prot;
54936004 429
c8a706fe 430 mmap_lock();
9dba3ca5 431 trace_target_mmap(start, len, target_prot, flags, fd, offset);
54936004 432
38138fab 433 if (!len) {
e89f07d3 434 errno = EINVAL;
c8a706fe 435 goto fail;
e89f07d3 436 }
54936004 437
9dba3ca5
RH
438 page_flags = validate_prot_to_pageflags(&host_prot, target_prot);
439 if (!page_flags) {
440 errno = EINVAL;
441 goto fail;
442 }
443
38138fab 444 /* Also check for overflows... */
54936004 445 len = TARGET_PAGE_ALIGN(len);
38138fab
AB
446 if (!len) {
447 errno = ENOMEM;
448 goto fail;
449 }
450
451 if (offset & ~TARGET_PAGE_MASK) {
452 errno = EINVAL;
453 goto fail;
454 }
455
228168cb
RH
456 /*
457 * If we're mapping shared memory, ensure we generate code for parallel
458 * execution and flush old translations. This will work up to the level
459 * supported by the host -- anything that requires EXCP_ATOMIC will not
460 * be atomic with respect to an external process.
461 */
462 if (flags & MAP_SHARED) {
463 CPUState *cpu = thread_cpu;
464 if (!(cpu->tcg_cflags & CF_PARALLEL)) {
465 cpu->tcg_cflags |= CF_PARALLEL;
466 tb_flush(cpu);
467 }
468 }
469
53a5960a 470 real_start = start & qemu_host_page_mask;
a5e7ee46
RH
471 host_offset = offset & qemu_host_page_mask;
472
473 /* If the user is asking for the kernel to find a location, do that
474 before we truncate the length for mapping files below. */
475 if (!(flags & MAP_FIXED)) {
476 host_len = len + offset - host_offset;
477 host_len = HOST_PAGE_ALIGN(host_len);
30ab9ef2 478 start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE);
a5e7ee46
RH
479 if (start == (abi_ulong)-1) {
480 errno = ENOMEM;
481 goto fail;
482 }
483 }
54936004 484
54c5a2ae
EI
485 /* When mapping files into a memory area larger than the file, accesses
486 to pages beyond the file size will cause a SIGBUS.
487
488 For example, if mmaping a file of 100 bytes on a host with 4K pages
489 emulating a target with 8K pages, the target expects to be able to
490 access the first 8K. But the host will trap us on any access beyond
491 4K.
492
493 When emulating a target with a larger page-size than the hosts, we
494 may need to truncate file maps at EOF and add extra anonymous pages
495 up to the targets page boundary. */
496
35f2fd04
MAL
497 if ((qemu_real_host_page_size < qemu_host_page_size) &&
498 !(flags & MAP_ANONYMOUS)) {
499 struct stat sb;
54c5a2ae
EI
500
501 if (fstat (fd, &sb) == -1)
502 goto fail;
503
504 /* Are we trying to create a map beyond EOF?. */
505 if (offset + len > sb.st_size) {
506 /* If so, truncate the file map at eof aligned with
507 the hosts real pagesize. Additional anonymous maps
508 will be created beyond EOF. */
0c2d70c4 509 len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
54c5a2ae
EI
510 }
511 }
512
54936004 513 if (!(flags & MAP_FIXED)) {
a5e7ee46 514 unsigned long host_start;
a03e2d42 515 void *p;
a5e7ee46 516
a03e2d42
FB
517 host_len = len + offset - host_offset;
518 host_len = HOST_PAGE_ALIGN(host_len);
a5e7ee46 519
a03e2d42
FB
520 /* Note: we prefer to control the mapping address. It is
521 especially important if qemu_host_page_size >
522 qemu_real_host_page_size */
3e8f1628 523 p = mmap(g2h_untagged(start), host_len, host_prot,
a5e7ee46 524 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
9dba3ca5 525 if (p == MAP_FAILED) {
c8a706fe 526 goto fail;
9dba3ca5 527 }
a03e2d42
FB
528 /* update start so that it points to the file position at 'offset' */
529 host_start = (unsigned long)p;
54c5a2ae 530 if (!(flags & MAP_ANONYMOUS)) {
3e8f1628 531 p = mmap(g2h_untagged(start), len, host_prot,
54c5a2ae 532 flags | MAP_FIXED, fd, host_offset);
8384274e 533 if (p == MAP_FAILED) {
3e8f1628 534 munmap(g2h_untagged(start), host_len);
8384274e
JB
535 goto fail;
536 }
a03e2d42 537 host_start += offset - host_offset;
54c5a2ae 538 }
a03e2d42
FB
539 start = h2g(host_start);
540 } else {
541 if (start & ~TARGET_PAGE_MASK) {
e89f07d3 542 errno = EINVAL;
c8a706fe 543 goto fail;
e89f07d3 544 }
a03e2d42
FB
545 end = start + len;
546 real_end = HOST_PAGE_ALIGN(end);
7ab240ad 547
7d37435b
PB
548 /*
549 * Test if requested memory area fits target address space
550 * It can fail only on 64-bit host with 32-bit target.
551 * On any other target/host host mmap() handles this error correctly.
552 */
46b12f46 553 if (end < start || !guest_range_valid_untagged(start, len)) {
ebf9a363 554 errno = ENOMEM;
45bc1f52
AJ
555 goto fail;
556 }
557
a03e2d42
FB
558 /* worst case: we cannot map the file because the offset is not
559 aligned, so we read it */
560 if (!(flags & MAP_ANONYMOUS) &&
561 (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
562 /* msync() won't work here, so we return an error if write is
563 possible while it is a shared mapping */
564 if ((flags & MAP_TYPE) == MAP_SHARED &&
9dba3ca5 565 (host_prot & PROT_WRITE)) {
a03e2d42 566 errno = EINVAL;
c8a706fe 567 goto fail;
a03e2d42 568 }
9dba3ca5 569 retaddr = target_mmap(start, len, target_prot | PROT_WRITE,
a03e2d42
FB
570 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
571 -1, 0);
572 if (retaddr == -1)
c8a706fe 573 goto fail;
3e8f1628 574 if (pread(fd, g2h_untagged(start), len, offset) == -1)
fb7e378c 575 goto fail;
9dba3ca5
RH
576 if (!(host_prot & PROT_WRITE)) {
577 ret = target_mprotect(start, len, target_prot);
86abac06 578 assert(ret == 0);
a03e2d42
FB
579 }
580 goto the_end;
54936004 581 }
a03e2d42
FB
582
583 /* handle the start of the mapping */
584 if (start > real_start) {
585 if (real_end == real_start + qemu_host_page_size) {
586 /* one single host page */
587 ret = mmap_frag(real_start, start, end,
9dba3ca5 588 host_prot, flags, fd, offset);
a03e2d42 589 if (ret == -1)
c8a706fe 590 goto fail;
a03e2d42
FB
591 goto the_end1;
592 }
593 ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
9dba3ca5 594 host_prot, flags, fd, offset);
54936004 595 if (ret == -1)
c8a706fe 596 goto fail;
a03e2d42
FB
597 real_start += qemu_host_page_size;
598 }
599 /* handle the end of the mapping */
600 if (end < real_end) {
601 ret = mmap_frag(real_end - qemu_host_page_size,
530c0032 602 real_end - qemu_host_page_size, end,
9dba3ca5 603 host_prot, flags, fd,
a03e2d42
FB
604 offset + real_end - qemu_host_page_size - start);
605 if (ret == -1)
c8a706fe 606 goto fail;
a03e2d42 607 real_end -= qemu_host_page_size;
54936004 608 }
3b46e624 609
a03e2d42
FB
610 /* map the middle (easier) */
611 if (real_start < real_end) {
612 void *p;
613 unsigned long offset1;
614 if (flags & MAP_ANONYMOUS)
615 offset1 = 0;
616 else
617 offset1 = offset + real_start - start;
3e8f1628 618 p = mmap(g2h_untagged(real_start), real_end - real_start,
9dba3ca5 619 host_prot, flags, fd, offset1);
a03e2d42 620 if (p == MAP_FAILED)
c8a706fe 621 goto fail;
a03e2d42 622 }
54936004
FB
623 }
624 the_end1:
26bab757
RH
625 if (flags & MAP_ANONYMOUS) {
626 page_flags |= PAGE_ANON;
627 }
d9c58585 628 page_flags |= PAGE_RESET;
9dba3ca5 629 page_set_flags(start, start + len, page_flags);
54936004 630 the_end:
d0e165ae 631 trace_target_mmap_complete(start);
10d0d505
AB
632 if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
633 log_page_dump(__func__);
634 }
35865339 635 tb_invalidate_phys_range(start, start + len);
c8a706fe 636 mmap_unlock();
54936004 637 return start;
c8a706fe
PB
638fail:
639 mmap_unlock();
640 return -1;
54936004
FB
641}
642
68a1c816
PB
643static void mmap_reserve(abi_ulong start, abi_ulong size)
644{
645 abi_ulong real_start;
646 abi_ulong real_end;
647 abi_ulong addr;
648 abi_ulong end;
649 int prot;
650
651 real_start = start & qemu_host_page_mask;
652 real_end = HOST_PAGE_ALIGN(start + size);
653 end = start + size;
654 if (start > real_start) {
655 /* handle host page containing start */
656 prot = 0;
657 for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
658 prot |= page_get_flags(addr);
659 }
660 if (real_end == real_start + qemu_host_page_size) {
661 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
662 prot |= page_get_flags(addr);
663 }
664 end = real_end;
665 }
666 if (prot != 0)
667 real_start += qemu_host_page_size;
668 }
669 if (end < real_end) {
670 prot = 0;
671 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
672 prot |= page_get_flags(addr);
673 }
674 if (prot != 0)
675 real_end -= qemu_host_page_size;
676 }
677 if (real_start != real_end) {
3e8f1628 678 mmap(g2h_untagged(real_start), real_end - real_start, PROT_NONE,
68a1c816
PB
679 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
680 -1, 0);
681 }
682}
683
992f48a0 684int target_munmap(abi_ulong start, abi_ulong len)
54936004 685{
992f48a0 686 abi_ulong end, real_start, real_end, addr;
54936004
FB
687 int prot, ret;
688
b7b18d26
AB
689 trace_target_munmap(start, len);
690
54936004 691 if (start & ~TARGET_PAGE_MASK)
78cf3390 692 return -TARGET_EINVAL;
54936004 693 len = TARGET_PAGE_ALIGN(len);
46b12f46 694 if (len == 0 || !guest_range_valid_untagged(start, len)) {
78cf3390 695 return -TARGET_EINVAL;
ebf9a363
MF
696 }
697
c8a706fe 698 mmap_lock();
54936004 699 end = start + len;
53a5960a
PB
700 real_start = start & qemu_host_page_mask;
701 real_end = HOST_PAGE_ALIGN(end);
54936004 702
53a5960a 703 if (start > real_start) {
54936004
FB
704 /* handle host page containing start */
705 prot = 0;
53a5960a 706 for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
54936004
FB
707 prot |= page_get_flags(addr);
708 }
53a5960a
PB
709 if (real_end == real_start + qemu_host_page_size) {
710 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
d418c81e
FB
711 prot |= page_get_flags(addr);
712 }
53a5960a 713 end = real_end;
d418c81e 714 }
54936004 715 if (prot != 0)
53a5960a 716 real_start += qemu_host_page_size;
54936004 717 }
53a5960a 718 if (end < real_end) {
54936004 719 prot = 0;
53a5960a 720 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
54936004
FB
721 prot |= page_get_flags(addr);
722 }
723 if (prot != 0)
53a5960a 724 real_end -= qemu_host_page_size;
54936004 725 }
3b46e624 726
c8a706fe 727 ret = 0;
54936004 728 /* unmap what we can */
53a5960a 729 if (real_start < real_end) {
b76f21a7 730 if (reserved_va) {
68a1c816
PB
731 mmap_reserve(real_start, real_end - real_start);
732 } else {
3e8f1628 733 ret = munmap(g2h_untagged(real_start), real_end - real_start);
68a1c816 734 }
54936004
FB
735 }
736
77a8f1a5 737 if (ret == 0) {
c8a706fe 738 page_set_flags(start, start + len, 0);
35865339 739 tb_invalidate_phys_range(start, start + len);
77a8f1a5 740 }
c8a706fe
PB
741 mmap_unlock();
742 return ret;
54936004
FB
743}
744
992f48a0
BS
745abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
746 abi_ulong new_size, unsigned long flags,
747 abi_ulong new_addr)
54936004
FB
748{
749 int prot;
f19412a2 750 void *host_addr;
54936004 751
46b12f46 752 if (!guest_range_valid_untagged(old_addr, old_size) ||
ebf9a363 753 ((flags & MREMAP_FIXED) &&
46b12f46 754 !guest_range_valid_untagged(new_addr, new_size)) ||
ccc5ccc1 755 ((flags & MREMAP_MAYMOVE) == 0 &&
46b12f46 756 !guest_range_valid_untagged(old_addr, new_size))) {
ebf9a363
MF
757 errno = ENOMEM;
758 return -1;
759 }
760
c8a706fe 761 mmap_lock();
f19412a2 762
68a1c816 763 if (flags & MREMAP_FIXED) {
3e8f1628
RH
764 host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
765 flags, g2h_untagged(new_addr));
68a1c816 766
b76f21a7 767 if (reserved_va && host_addr != MAP_FAILED) {
68a1c816
PB
768 /* If new and old addresses overlap then the above mremap will
769 already have failed with EINVAL. */
770 mmap_reserve(old_addr, old_size);
771 }
772 } else if (flags & MREMAP_MAYMOVE) {
f19412a2
AJ
773 abi_ulong mmap_start;
774
30ab9ef2 775 mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
f19412a2
AJ
776
777 if (mmap_start == -1) {
778 errno = ENOMEM;
779 host_addr = MAP_FAILED;
68a1c816 780 } else {
3e8f1628
RH
781 host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
782 flags | MREMAP_FIXED,
783 g2h_untagged(mmap_start));
b76f21a7 784 if (reserved_va) {
c65ffe6d 785 mmap_reserve(old_addr, old_size);
786 }
68a1c816 787 }
3af72a4d 788 } else {
68a1c816 789 int prot = 0;
b76f21a7 790 if (reserved_va && old_size < new_size) {
68a1c816
PB
791 abi_ulong addr;
792 for (addr = old_addr + old_size;
793 addr < old_addr + new_size;
794 addr++) {
795 prot |= page_get_flags(addr);
796 }
797 }
798 if (prot == 0) {
3e8f1628
RH
799 host_addr = mremap(g2h_untagged(old_addr),
800 old_size, new_size, flags);
56d19084
TK
801
802 if (host_addr != MAP_FAILED) {
803 /* Check if address fits target address space */
46b12f46 804 if (!guest_range_valid_untagged(h2g(host_addr), new_size)) {
56d19084 805 /* Revert mremap() changes */
3e8f1628
RH
806 host_addr = mremap(g2h_untagged(old_addr),
807 new_size, old_size, flags);
56d19084
TK
808 errno = ENOMEM;
809 host_addr = MAP_FAILED;
810 } else if (reserved_va && old_size > new_size) {
811 mmap_reserve(old_addr + old_size, old_size - new_size);
812 }
68a1c816
PB
813 }
814 } else {
815 errno = ENOMEM;
816 host_addr = MAP_FAILED;
817 }
f19412a2
AJ
818 }
819
820 if (host_addr == MAP_FAILED) {
c8a706fe
PB
821 new_addr = -1;
822 } else {
823 new_addr = h2g(host_addr);
824 prot = page_get_flags(old_addr);
825 page_set_flags(old_addr, old_addr + old_size, 0);
d9c58585
RH
826 page_set_flags(new_addr, new_addr + new_size,
827 prot | PAGE_VALID | PAGE_RESET);
c8a706fe 828 }
35865339 829 tb_invalidate_phys_range(new_addr, new_addr + new_size);
c8a706fe 830 mmap_unlock();
54936004
FB
831 return new_addr;
832}