target/hppa: Convert arithmetic immediate insns
[qemu/ar7.git] / linux-user / mmap.c
blobe0249efe4fedadccaac7c8f8ad82ad67ccf3c983
1 /*
2 * mmap support for qemu
4 * Copyright (c) 2003 Fabrice Bellard
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.
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.
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/>.
19 #include "qemu/osdep.h"
21 #include "qemu.h"
22 #include "qemu-common.h"
24 //#define DEBUG_MMAP
26 static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
27 static __thread int mmap_lock_count;
29 void mmap_lock(void)
31 if (mmap_lock_count++ == 0) {
32 pthread_mutex_lock(&mmap_mutex);
36 void mmap_unlock(void)
38 if (--mmap_lock_count == 0) {
39 pthread_mutex_unlock(&mmap_mutex);
43 bool have_mmap_lock(void)
45 return mmap_lock_count > 0 ? true : false;
48 /* Grab lock to make sure things are in a consistent state after fork(). */
49 void mmap_fork_start(void)
51 if (mmap_lock_count)
52 abort();
53 pthread_mutex_lock(&mmap_mutex);
56 void mmap_fork_end(int child)
58 if (child)
59 pthread_mutex_init(&mmap_mutex, NULL);
60 else
61 pthread_mutex_unlock(&mmap_mutex);
64 /* NOTE: all the constants are the HOST ones, but addresses are target. */
65 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
67 abi_ulong end, host_start, host_end, addr;
68 int prot1, ret;
70 #ifdef DEBUG_MMAP
71 printf("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 #endif
78 if ((start & ~TARGET_PAGE_MASK) != 0)
79 return -TARGET_EINVAL;
80 len = TARGET_PAGE_ALIGN(len);
81 end = start + len;
82 if (!guest_range_valid(start, len)) {
83 return -TARGET_ENOMEM;
85 prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
86 if (len == 0)
87 return 0;
89 mmap_lock();
90 host_start = start & qemu_host_page_mask;
91 host_end = HOST_PAGE_ALIGN(end);
92 if (start > host_start) {
93 /* handle host page containing start */
94 prot1 = prot;
95 for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
96 prot1 |= page_get_flags(addr);
98 if (host_end == host_start + qemu_host_page_size) {
99 for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
100 prot1 |= page_get_flags(addr);
102 end = host_end;
104 ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
105 if (ret != 0)
106 goto error;
107 host_start += qemu_host_page_size;
109 if (end < host_end) {
110 prot1 = prot;
111 for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
112 prot1 |= page_get_flags(addr);
114 ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
115 prot1 & PAGE_BITS);
116 if (ret != 0)
117 goto error;
118 host_end -= qemu_host_page_size;
121 /* handle the pages in the middle */
122 if (host_start < host_end) {
123 ret = mprotect(g2h(host_start), host_end - host_start, prot);
124 if (ret != 0)
125 goto error;
127 page_set_flags(start, start + len, prot | PAGE_VALID);
128 mmap_unlock();
129 return 0;
130 error:
131 mmap_unlock();
132 return ret;
135 /* map an incomplete host page */
136 static int mmap_frag(abi_ulong real_start,
137 abi_ulong start, abi_ulong end,
138 int prot, int flags, int fd, abi_ulong offset)
140 abi_ulong real_end, addr;
141 void *host_start;
142 int prot1, prot_new;
144 real_end = real_start + qemu_host_page_size;
145 host_start = g2h(real_start);
147 /* get the protection of the target pages outside the mapping */
148 prot1 = 0;
149 for(addr = real_start; addr < real_end; addr++) {
150 if (addr < start || addr >= end)
151 prot1 |= page_get_flags(addr);
154 if (prot1 == 0) {
155 /* no page was there, so we allocate one */
156 void *p = mmap(host_start, qemu_host_page_size, prot,
157 flags | MAP_ANONYMOUS, -1, 0);
158 if (p == MAP_FAILED)
159 return -1;
160 prot1 = prot;
162 prot1 &= PAGE_BITS;
164 prot_new = prot | prot1;
165 if (!(flags & MAP_ANONYMOUS)) {
166 /* msync() won't work here, so we return an error if write is
167 possible while it is a shared mapping */
168 if ((flags & MAP_TYPE) == MAP_SHARED &&
169 (prot & PROT_WRITE))
170 return -1;
172 /* adjust protection to be able to read */
173 if (!(prot1 & PROT_WRITE))
174 mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
176 /* read the corresponding file data */
177 if (pread(fd, g2h(start), end - start, offset) == -1)
178 return -1;
180 /* put final protection */
181 if (prot_new != (prot1 | PROT_WRITE))
182 mprotect(host_start, qemu_host_page_size, prot_new);
183 } else {
184 if (prot_new != prot1) {
185 mprotect(host_start, qemu_host_page_size, prot_new);
187 if (prot_new & PROT_WRITE) {
188 memset(g2h(start), 0, end - start);
191 return 0;
194 #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
195 # define TASK_UNMAPPED_BASE (1ul << 38)
196 #else
197 # define TASK_UNMAPPED_BASE 0x40000000
198 #endif
199 abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
201 unsigned long last_brk;
203 /* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
204 of guest address space. */
205 static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
207 abi_ulong addr;
208 abi_ulong end_addr;
209 int prot;
210 int looped = 0;
212 if (size > reserved_va) {
213 return (abi_ulong)-1;
216 size = HOST_PAGE_ALIGN(size);
217 end_addr = start + size;
218 if (end_addr > reserved_va) {
219 end_addr = reserved_va;
221 addr = end_addr - qemu_host_page_size;
223 while (1) {
224 if (addr > end_addr) {
225 if (looped) {
226 return (abi_ulong)-1;
228 end_addr = reserved_va;
229 addr = end_addr - qemu_host_page_size;
230 looped = 1;
231 continue;
233 prot = page_get_flags(addr);
234 if (prot) {
235 end_addr = addr;
237 if (addr && addr + size == end_addr) {
238 break;
240 addr -= qemu_host_page_size;
243 if (start == mmap_next_start) {
244 mmap_next_start = addr;
247 return addr;
251 * Find and reserve a free memory area of size 'size'. The search
252 * starts at 'start'.
253 * It must be called with mmap_lock() held.
254 * Return -1 if error.
256 abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
258 void *ptr, *prev;
259 abi_ulong addr;
260 int wrapped, repeat;
262 /* If 'start' == 0, then a default start address is used. */
263 if (start == 0) {
264 start = mmap_next_start;
265 } else {
266 start &= qemu_host_page_mask;
269 size = HOST_PAGE_ALIGN(size);
271 if (reserved_va) {
272 return mmap_find_vma_reserved(start, size);
275 addr = start;
276 wrapped = repeat = 0;
277 prev = 0;
279 for (;; prev = ptr) {
281 * Reserve needed memory area to avoid a race.
282 * It should be discarded using:
283 * - mmap() with MAP_FIXED flag
284 * - mremap() with MREMAP_FIXED flag
285 * - shmat() with SHM_REMAP flag
287 ptr = mmap(g2h(addr), size, PROT_NONE,
288 MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
290 /* ENOMEM, if host address space has no memory */
291 if (ptr == MAP_FAILED) {
292 return (abi_ulong)-1;
295 /* Count the number of sequential returns of the same address.
296 This is used to modify the search algorithm below. */
297 repeat = (ptr == prev ? repeat + 1 : 0);
299 if (h2g_valid(ptr + size - 1)) {
300 addr = h2g(ptr);
302 if ((addr & ~TARGET_PAGE_MASK) == 0) {
303 /* Success. */
304 if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
305 mmap_next_start = addr + size;
307 return addr;
310 /* The address is not properly aligned for the target. */
311 switch (repeat) {
312 case 0:
313 /* Assume the result that the kernel gave us is the
314 first with enough free space, so start again at the
315 next higher target page. */
316 addr = TARGET_PAGE_ALIGN(addr);
317 break;
318 case 1:
319 /* Sometimes the kernel decides to perform the allocation
320 at the top end of memory instead. */
321 addr &= TARGET_PAGE_MASK;
322 break;
323 case 2:
324 /* Start over at low memory. */
325 addr = 0;
326 break;
327 default:
328 /* Fail. This unaligned block must the last. */
329 addr = -1;
330 break;
332 } else {
333 /* Since the result the kernel gave didn't fit, start
334 again at low memory. If any repetition, fail. */
335 addr = (repeat ? -1 : 0);
338 /* Unmap and try again. */
339 munmap(ptr, size);
341 /* ENOMEM if we checked the whole of the target address space. */
342 if (addr == (abi_ulong)-1) {
343 return (abi_ulong)-1;
344 } else if (addr == 0) {
345 if (wrapped) {
346 return (abi_ulong)-1;
348 wrapped = 1;
349 /* Don't actually use 0 when wrapping, instead indicate
350 that we'd truly like an allocation in low memory. */
351 addr = (mmap_min_addr > TARGET_PAGE_SIZE
352 ? TARGET_PAGE_ALIGN(mmap_min_addr)
353 : TARGET_PAGE_SIZE);
354 } else if (wrapped && addr >= start) {
355 return (abi_ulong)-1;
360 /* NOTE: all the constants are the HOST ones */
361 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
362 int flags, int fd, abi_ulong offset)
364 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
366 mmap_lock();
367 #ifdef DEBUG_MMAP
369 printf("mmap: start=0x" TARGET_ABI_FMT_lx
370 " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
371 start, len,
372 prot & PROT_READ ? 'r' : '-',
373 prot & PROT_WRITE ? 'w' : '-',
374 prot & PROT_EXEC ? 'x' : '-');
375 if (flags & MAP_FIXED)
376 printf("MAP_FIXED ");
377 if (flags & MAP_ANONYMOUS)
378 printf("MAP_ANON ");
379 switch(flags & MAP_TYPE) {
380 case MAP_PRIVATE:
381 printf("MAP_PRIVATE ");
382 break;
383 case MAP_SHARED:
384 printf("MAP_SHARED ");
385 break;
386 default:
387 printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
388 break;
390 printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
392 #endif
394 if (!len) {
395 errno = EINVAL;
396 goto fail;
399 /* Also check for overflows... */
400 len = TARGET_PAGE_ALIGN(len);
401 if (!len) {
402 errno = ENOMEM;
403 goto fail;
406 if (offset & ~TARGET_PAGE_MASK) {
407 errno = EINVAL;
408 goto fail;
411 real_start = start & qemu_host_page_mask;
412 host_offset = offset & qemu_host_page_mask;
414 /* If the user is asking for the kernel to find a location, do that
415 before we truncate the length for mapping files below. */
416 if (!(flags & MAP_FIXED)) {
417 host_len = len + offset - host_offset;
418 host_len = HOST_PAGE_ALIGN(host_len);
419 start = mmap_find_vma(real_start, host_len);
420 if (start == (abi_ulong)-1) {
421 errno = ENOMEM;
422 goto fail;
426 /* When mapping files into a memory area larger than the file, accesses
427 to pages beyond the file size will cause a SIGBUS.
429 For example, if mmaping a file of 100 bytes on a host with 4K pages
430 emulating a target with 8K pages, the target expects to be able to
431 access the first 8K. But the host will trap us on any access beyond
432 4K.
434 When emulating a target with a larger page-size than the hosts, we
435 may need to truncate file maps at EOF and add extra anonymous pages
436 up to the targets page boundary. */
438 if ((qemu_real_host_page_size < qemu_host_page_size) &&
439 !(flags & MAP_ANONYMOUS)) {
440 struct stat sb;
442 if (fstat (fd, &sb) == -1)
443 goto fail;
445 /* Are we trying to create a map beyond EOF?. */
446 if (offset + len > sb.st_size) {
447 /* If so, truncate the file map at eof aligned with
448 the hosts real pagesize. Additional anonymous maps
449 will be created beyond EOF. */
450 len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
454 if (!(flags & MAP_FIXED)) {
455 unsigned long host_start;
456 void *p;
458 host_len = len + offset - host_offset;
459 host_len = HOST_PAGE_ALIGN(host_len);
461 /* Note: we prefer to control the mapping address. It is
462 especially important if qemu_host_page_size >
463 qemu_real_host_page_size */
464 p = mmap(g2h(start), host_len, prot,
465 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
466 if (p == MAP_FAILED)
467 goto fail;
468 /* update start so that it points to the file position at 'offset' */
469 host_start = (unsigned long)p;
470 if (!(flags & MAP_ANONYMOUS)) {
471 p = mmap(g2h(start), len, prot,
472 flags | MAP_FIXED, fd, host_offset);
473 if (p == MAP_FAILED) {
474 munmap(g2h(start), host_len);
475 goto fail;
477 host_start += offset - host_offset;
479 start = h2g(host_start);
480 } else {
481 if (start & ~TARGET_PAGE_MASK) {
482 errno = EINVAL;
483 goto fail;
485 end = start + len;
486 real_end = HOST_PAGE_ALIGN(end);
489 * Test if requested memory area fits target address space
490 * It can fail only on 64-bit host with 32-bit target.
491 * On any other target/host host mmap() handles this error correctly.
493 if (!guest_range_valid(start, len)) {
494 errno = ENOMEM;
495 goto fail;
498 /* worst case: we cannot map the file because the offset is not
499 aligned, so we read it */
500 if (!(flags & MAP_ANONYMOUS) &&
501 (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
502 /* msync() won't work here, so we return an error if write is
503 possible while it is a shared mapping */
504 if ((flags & MAP_TYPE) == MAP_SHARED &&
505 (prot & PROT_WRITE)) {
506 errno = EINVAL;
507 goto fail;
509 retaddr = target_mmap(start, len, prot | PROT_WRITE,
510 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
511 -1, 0);
512 if (retaddr == -1)
513 goto fail;
514 if (pread(fd, g2h(start), len, offset) == -1)
515 goto fail;
516 if (!(prot & PROT_WRITE)) {
517 ret = target_mprotect(start, len, prot);
518 assert(ret == 0);
520 goto the_end;
523 /* handle the start of the mapping */
524 if (start > real_start) {
525 if (real_end == real_start + qemu_host_page_size) {
526 /* one single host page */
527 ret = mmap_frag(real_start, start, end,
528 prot, flags, fd, offset);
529 if (ret == -1)
530 goto fail;
531 goto the_end1;
533 ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
534 prot, flags, fd, offset);
535 if (ret == -1)
536 goto fail;
537 real_start += qemu_host_page_size;
539 /* handle the end of the mapping */
540 if (end < real_end) {
541 ret = mmap_frag(real_end - qemu_host_page_size,
542 real_end - qemu_host_page_size, end,
543 prot, flags, fd,
544 offset + real_end - qemu_host_page_size - start);
545 if (ret == -1)
546 goto fail;
547 real_end -= qemu_host_page_size;
550 /* map the middle (easier) */
551 if (real_start < real_end) {
552 void *p;
553 unsigned long offset1;
554 if (flags & MAP_ANONYMOUS)
555 offset1 = 0;
556 else
557 offset1 = offset + real_start - start;
558 p = mmap(g2h(real_start), real_end - real_start,
559 prot, flags, fd, offset1);
560 if (p == MAP_FAILED)
561 goto fail;
564 the_end1:
565 page_set_flags(start, start + len, prot | PAGE_VALID);
566 the_end:
567 #ifdef DEBUG_MMAP
568 printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
569 page_dump(stdout);
570 printf("\n");
571 #endif
572 tb_invalidate_phys_range(start, start + len);
573 mmap_unlock();
574 return start;
575 fail:
576 mmap_unlock();
577 return -1;
580 static void mmap_reserve(abi_ulong start, abi_ulong size)
582 abi_ulong real_start;
583 abi_ulong real_end;
584 abi_ulong addr;
585 abi_ulong end;
586 int prot;
588 real_start = start & qemu_host_page_mask;
589 real_end = HOST_PAGE_ALIGN(start + size);
590 end = start + size;
591 if (start > real_start) {
592 /* handle host page containing start */
593 prot = 0;
594 for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
595 prot |= page_get_flags(addr);
597 if (real_end == real_start + qemu_host_page_size) {
598 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
599 prot |= page_get_flags(addr);
601 end = real_end;
603 if (prot != 0)
604 real_start += qemu_host_page_size;
606 if (end < real_end) {
607 prot = 0;
608 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
609 prot |= page_get_flags(addr);
611 if (prot != 0)
612 real_end -= qemu_host_page_size;
614 if (real_start != real_end) {
615 mmap(g2h(real_start), real_end - real_start, PROT_NONE,
616 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
617 -1, 0);
621 int target_munmap(abi_ulong start, abi_ulong len)
623 abi_ulong end, real_start, real_end, addr;
624 int prot, ret;
626 #ifdef DEBUG_MMAP
627 printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
628 TARGET_ABI_FMT_lx "\n",
629 start, len);
630 #endif
631 if (start & ~TARGET_PAGE_MASK)
632 return -TARGET_EINVAL;
633 len = TARGET_PAGE_ALIGN(len);
634 if (len == 0 || !guest_range_valid(start, len)) {
635 return -TARGET_EINVAL;
638 mmap_lock();
639 end = start + len;
640 real_start = start & qemu_host_page_mask;
641 real_end = HOST_PAGE_ALIGN(end);
643 if (start > real_start) {
644 /* handle host page containing start */
645 prot = 0;
646 for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
647 prot |= page_get_flags(addr);
649 if (real_end == real_start + qemu_host_page_size) {
650 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
651 prot |= page_get_flags(addr);
653 end = real_end;
655 if (prot != 0)
656 real_start += qemu_host_page_size;
658 if (end < real_end) {
659 prot = 0;
660 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
661 prot |= page_get_flags(addr);
663 if (prot != 0)
664 real_end -= qemu_host_page_size;
667 ret = 0;
668 /* unmap what we can */
669 if (real_start < real_end) {
670 if (reserved_va) {
671 mmap_reserve(real_start, real_end - real_start);
672 } else {
673 ret = munmap(g2h(real_start), real_end - real_start);
677 if (ret == 0) {
678 page_set_flags(start, start + len, 0);
679 tb_invalidate_phys_range(start, start + len);
681 mmap_unlock();
682 return ret;
685 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
686 abi_ulong new_size, unsigned long flags,
687 abi_ulong new_addr)
689 int prot;
690 void *host_addr;
692 if (!guest_range_valid(old_addr, old_size) ||
693 ((flags & MREMAP_FIXED) &&
694 !guest_range_valid(new_addr, new_size))) {
695 errno = ENOMEM;
696 return -1;
699 mmap_lock();
701 if (flags & MREMAP_FIXED) {
702 host_addr = mremap(g2h(old_addr), old_size, new_size,
703 flags, g2h(new_addr));
705 if (reserved_va && host_addr != MAP_FAILED) {
706 /* If new and old addresses overlap then the above mremap will
707 already have failed with EINVAL. */
708 mmap_reserve(old_addr, old_size);
710 } else if (flags & MREMAP_MAYMOVE) {
711 abi_ulong mmap_start;
713 mmap_start = mmap_find_vma(0, new_size);
715 if (mmap_start == -1) {
716 errno = ENOMEM;
717 host_addr = MAP_FAILED;
718 } else {
719 host_addr = mremap(g2h(old_addr), old_size, new_size,
720 flags | MREMAP_FIXED, g2h(mmap_start));
721 if (reserved_va) {
722 mmap_reserve(old_addr, old_size);
725 } else {
726 int prot = 0;
727 if (reserved_va && old_size < new_size) {
728 abi_ulong addr;
729 for (addr = old_addr + old_size;
730 addr < old_addr + new_size;
731 addr++) {
732 prot |= page_get_flags(addr);
735 if (prot == 0) {
736 host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
737 if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
738 mmap_reserve(old_addr + old_size, new_size - old_size);
740 } else {
741 errno = ENOMEM;
742 host_addr = MAP_FAILED;
744 /* Check if address fits target address space */
745 if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
746 /* Revert mremap() changes */
747 host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
748 errno = ENOMEM;
749 host_addr = MAP_FAILED;
753 if (host_addr == MAP_FAILED) {
754 new_addr = -1;
755 } else {
756 new_addr = h2g(host_addr);
757 prot = page_get_flags(old_addr);
758 page_set_flags(old_addr, old_addr + old_size, 0);
759 page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
761 tb_invalidate_phys_range(new_addr, new_addr + new_size);
762 mmap_unlock();
763 return new_addr;