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