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