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