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