|
|
1.1 root 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
1.1.1.2 root 17: * along with this program; if not, see <http://www.gnu.org/licenses/>.
1.1 root 18: */
19: #include <stdlib.h>
20: #include <stdio.h>
21: #include <stdarg.h>
22: #include <string.h>
23: #include <unistd.h>
24: #include <errno.h>
25: #include <sys/mman.h>
26:
27: #include "qemu.h"
28: #include "qemu-common.h"
29: #include "bsd-mman.h"
30:
31: //#define DEBUG_MMAP
32:
1.1.1.3 ! root 33: #if defined(CONFIG_USE_NPTL)
1.1 root 34: pthread_mutex_t mmap_mutex;
35: static int __thread mmap_lock_count;
36:
37: void mmap_lock(void)
38: {
39: if (mmap_lock_count++ == 0) {
40: pthread_mutex_lock(&mmap_mutex);
41: }
42: }
43:
44: void mmap_unlock(void)
45: {
46: if (--mmap_lock_count == 0) {
47: pthread_mutex_unlock(&mmap_mutex);
48: }
49: }
50:
51: /* Grab lock to make sure things are in a consistent state after fork(). */
52: void mmap_fork_start(void)
53: {
54: if (mmap_lock_count)
55: abort();
56: pthread_mutex_lock(&mmap_mutex);
57: }
58:
59: void mmap_fork_end(int child)
60: {
61: if (child)
62: pthread_mutex_init(&mmap_mutex, NULL);
63: else
64: pthread_mutex_unlock(&mmap_mutex);
65: }
66: #else
67: /* We aren't threadsafe to start with, so no need to worry about locking. */
68: void mmap_lock(void)
69: {
70: }
71:
72: void mmap_unlock(void)
73: {
74: }
75: #endif
76:
77: void *qemu_vmalloc(size_t size)
78: {
79: void *p;
80: unsigned long addr;
81: mmap_lock();
82: /* Use map and mark the pages as used. */
83: p = mmap(NULL, size, PROT_READ | PROT_WRITE,
84: MAP_PRIVATE | MAP_ANON, -1, 0);
85:
86: addr = (unsigned long)p;
87: if (addr == (target_ulong) addr) {
88: /* Allocated region overlaps guest address space.
89: This may recurse. */
90: page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
91: PAGE_RESERVED);
92: }
93:
94: mmap_unlock();
95: return p;
96: }
97:
98: void *qemu_malloc(size_t size)
99: {
100: char * p;
101: size += 16;
102: p = qemu_vmalloc(size);
103: *(size_t *)p = size;
104: return p + 16;
105: }
106:
107: /* We use map, which is always zero initialized. */
108: void * qemu_mallocz(size_t size)
109: {
110: return qemu_malloc(size);
111: }
112:
113: void qemu_free(void *ptr)
114: {
115: /* FIXME: We should unmark the reserved pages here. However this gets
116: complicated when one target page spans multiple host pages, so we
117: don't bother. */
118: size_t *p;
119: p = (size_t *)((char *)ptr - 16);
120: munmap(p, *p);
121: }
122:
123: void *qemu_realloc(void *ptr, size_t size)
124: {
125: size_t old_size, copy;
126: void *new_ptr;
127:
128: if (!ptr)
129: return qemu_malloc(size);
130: old_size = *(size_t *)((char *)ptr - 16);
131: copy = old_size < size ? old_size : size;
132: new_ptr = qemu_malloc(size);
133: memcpy(new_ptr, ptr, copy);
134: qemu_free(ptr);
135: return new_ptr;
136: }
137:
138: /* NOTE: all the constants are the HOST ones, but addresses are target. */
139: int target_mprotect(abi_ulong start, abi_ulong len, int prot)
140: {
141: abi_ulong end, host_start, host_end, addr;
142: int prot1, ret;
143:
144: #ifdef DEBUG_MMAP
145: printf("mprotect: start=0x" TARGET_FMT_lx
146: " len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
147: prot & PROT_READ ? 'r' : '-',
148: prot & PROT_WRITE ? 'w' : '-',
149: prot & PROT_EXEC ? 'x' : '-');
150: #endif
151:
152: if ((start & ~TARGET_PAGE_MASK) != 0)
153: return -EINVAL;
154: len = TARGET_PAGE_ALIGN(len);
155: end = start + len;
156: if (end < start)
157: return -EINVAL;
158: prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
159: if (len == 0)
160: return 0;
161:
162: mmap_lock();
163: host_start = start & qemu_host_page_mask;
164: host_end = HOST_PAGE_ALIGN(end);
165: if (start > host_start) {
166: /* handle host page containing start */
167: prot1 = prot;
168: for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
169: prot1 |= page_get_flags(addr);
170: }
171: if (host_end == host_start + qemu_host_page_size) {
172: for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
173: prot1 |= page_get_flags(addr);
174: }
175: end = host_end;
176: }
177: ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
178: if (ret != 0)
179: goto error;
180: host_start += qemu_host_page_size;
181: }
182: if (end < host_end) {
183: prot1 = prot;
184: for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
185: prot1 |= page_get_flags(addr);
186: }
187: ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
188: prot1 & PAGE_BITS);
189: if (ret != 0)
190: goto error;
191: host_end -= qemu_host_page_size;
192: }
193:
194: /* handle the pages in the middle */
195: if (host_start < host_end) {
196: ret = mprotect(g2h(host_start), host_end - host_start, prot);
197: if (ret != 0)
198: goto error;
199: }
200: page_set_flags(start, start + len, prot | PAGE_VALID);
201: mmap_unlock();
202: return 0;
203: error:
204: mmap_unlock();
205: return ret;
206: }
207:
208: /* map an incomplete host page */
209: static int mmap_frag(abi_ulong real_start,
210: abi_ulong start, abi_ulong end,
211: int prot, int flags, int fd, abi_ulong offset)
212: {
213: abi_ulong real_end, addr;
214: void *host_start;
215: int prot1, prot_new;
216:
217: real_end = real_start + qemu_host_page_size;
218: host_start = g2h(real_start);
219:
220: /* get the protection of the target pages outside the mapping */
221: prot1 = 0;
222: for(addr = real_start; addr < real_end; addr++) {
223: if (addr < start || addr >= end)
224: prot1 |= page_get_flags(addr);
225: }
226:
227: if (prot1 == 0) {
228: /* no page was there, so we allocate one */
229: void *p = mmap(host_start, qemu_host_page_size, prot,
230: flags | MAP_ANON, -1, 0);
231: if (p == MAP_FAILED)
232: return -1;
233: prot1 = prot;
234: }
235: prot1 &= PAGE_BITS;
236:
237: prot_new = prot | prot1;
238: if (!(flags & MAP_ANON)) {
239: /* msync() won't work here, so we return an error if write is
240: possible while it is a shared mapping */
241: if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
242: (prot & PROT_WRITE))
243: return -EINVAL;
244:
245: /* adjust protection to be able to read */
246: if (!(prot1 & PROT_WRITE))
247: mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
248:
249: /* read the corresponding file data */
250: pread(fd, g2h(start), end - start, offset);
251:
252: /* put final protection */
253: if (prot_new != (prot1 | PROT_WRITE))
254: mprotect(host_start, qemu_host_page_size, prot_new);
255: } else {
256: /* just update the protection */
257: if (prot_new != prot1) {
258: mprotect(host_start, qemu_host_page_size, prot_new);
259: }
260: }
261: return 0;
262: }
263:
264: #if defined(__CYGWIN__)
265: /* Cygwin doesn't have a whole lot of address space. */
266: static abi_ulong mmap_next_start = 0x18000000;
267: #else
268: static abi_ulong mmap_next_start = 0x40000000;
269: #endif
270:
271: unsigned long last_brk;
272:
273: /* find a free memory area of size 'size'. The search starts at
274: 'start'. If 'start' == 0, then a default start address is used.
275: Return -1 if error.
276: */
277: /* page_init() marks pages used by the host as reserved to be sure not
278: to use them. */
279: static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
280: {
281: abi_ulong addr, addr1, addr_start;
282: int prot;
283: unsigned long new_brk;
284:
285: new_brk = (unsigned long)sbrk(0);
286: if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
287: /* This is a hack to catch the host allocating memory with brk().
288: If it uses mmap then we loose.
289: FIXME: We really want to avoid the host allocating memory in
290: the first place, and maybe leave some slack to avoid switching
291: to mmap. */
292: page_set_flags(last_brk & TARGET_PAGE_MASK,
293: TARGET_PAGE_ALIGN(new_brk),
294: PAGE_RESERVED);
295: }
296: last_brk = new_brk;
297:
298: size = HOST_PAGE_ALIGN(size);
299: start = start & qemu_host_page_mask;
300: addr = start;
301: if (addr == 0)
302: addr = mmap_next_start;
303: addr_start = addr;
304: for(;;) {
305: prot = 0;
306: for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
307: prot |= page_get_flags(addr1);
308: }
309: if (prot == 0)
310: break;
311: addr += qemu_host_page_size;
312: /* we found nothing */
313: if (addr == addr_start)
314: return (abi_ulong)-1;
315: }
316: if (start == 0)
317: mmap_next_start = addr + size;
318: return addr;
319: }
320:
321: /* NOTE: all the constants are the HOST ones */
322: abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
323: int flags, int fd, abi_ulong offset)
324: {
325: abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
326: unsigned long host_start;
327:
328: mmap_lock();
329: #ifdef DEBUG_MMAP
330: {
331: printf("mmap: start=0x" TARGET_FMT_lx
332: " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
333: start, len,
334: prot & PROT_READ ? 'r' : '-',
335: prot & PROT_WRITE ? 'w' : '-',
336: prot & PROT_EXEC ? 'x' : '-');
337: if (flags & MAP_FIXED)
338: printf("MAP_FIXED ");
339: if (flags & MAP_ANON)
340: printf("MAP_ANON ");
341: switch(flags & TARGET_BSD_MAP_FLAGMASK) {
342: case MAP_PRIVATE:
343: printf("MAP_PRIVATE ");
344: break;
345: case MAP_SHARED:
346: printf("MAP_SHARED ");
347: break;
348: default:
349: printf("[MAP_FLAGMASK=0x%x] ", flags & TARGET_BSD_MAP_FLAGMASK);
350: break;
351: }
352: printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
353: }
354: #endif
355:
356: if (offset & ~TARGET_PAGE_MASK) {
357: errno = EINVAL;
358: goto fail;
359: }
360:
361: len = TARGET_PAGE_ALIGN(len);
362: if (len == 0)
363: goto the_end;
364: real_start = start & qemu_host_page_mask;
365:
366: if (!(flags & MAP_FIXED)) {
367: abi_ulong mmap_start;
368: void *p;
369: host_offset = offset & qemu_host_page_mask;
370: host_len = len + offset - host_offset;
371: host_len = HOST_PAGE_ALIGN(host_len);
372: mmap_start = mmap_find_vma(real_start, host_len);
373: if (mmap_start == (abi_ulong)-1) {
374: errno = ENOMEM;
375: goto fail;
376: }
377: /* Note: we prefer to control the mapping address. It is
378: especially important if qemu_host_page_size >
379: qemu_real_host_page_size */
380: p = mmap(g2h(mmap_start),
381: host_len, prot, flags | MAP_FIXED, fd, host_offset);
382: if (p == MAP_FAILED)
383: goto fail;
384: /* update start so that it points to the file position at 'offset' */
385: host_start = (unsigned long)p;
386: if (!(flags & MAP_ANON))
387: host_start += offset - host_offset;
388: start = h2g(host_start);
389: } else {
390: int flg;
391: target_ulong addr;
392:
393: if (start & ~TARGET_PAGE_MASK) {
394: errno = EINVAL;
395: goto fail;
396: }
397: end = start + len;
398: real_end = HOST_PAGE_ALIGN(end);
399:
400: for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
401: flg = page_get_flags(addr);
402: if (flg & PAGE_RESERVED) {
403: errno = ENXIO;
404: goto fail;
405: }
406: }
407:
408: /* worst case: we cannot map the file because the offset is not
409: aligned, so we read it */
410: if (!(flags & MAP_ANON) &&
411: (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
412: /* msync() won't work here, so we return an error if write is
413: possible while it is a shared mapping */
414: if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
415: (prot & PROT_WRITE)) {
416: errno = EINVAL;
417: goto fail;
418: }
419: retaddr = target_mmap(start, len, prot | PROT_WRITE,
420: MAP_FIXED | MAP_PRIVATE | MAP_ANON,
421: -1, 0);
422: if (retaddr == -1)
423: goto fail;
424: pread(fd, g2h(start), len, offset);
425: if (!(prot & PROT_WRITE)) {
426: ret = target_mprotect(start, len, prot);
427: if (ret != 0) {
428: start = ret;
429: goto the_end;
430: }
431: }
432: goto the_end;
433: }
434:
435: /* handle the start of the mapping */
436: if (start > real_start) {
437: if (real_end == real_start + qemu_host_page_size) {
438: /* one single host page */
439: ret = mmap_frag(real_start, start, end,
440: prot, flags, fd, offset);
441: if (ret == -1)
442: goto fail;
443: goto the_end1;
444: }
445: ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
446: prot, flags, fd, offset);
447: if (ret == -1)
448: goto fail;
449: real_start += qemu_host_page_size;
450: }
451: /* handle the end of the mapping */
452: if (end < real_end) {
453: ret = mmap_frag(real_end - qemu_host_page_size,
454: real_end - qemu_host_page_size, real_end,
455: prot, flags, fd,
456: offset + real_end - qemu_host_page_size - start);
457: if (ret == -1)
458: goto fail;
459: real_end -= qemu_host_page_size;
460: }
461:
462: /* map the middle (easier) */
463: if (real_start < real_end) {
464: void *p;
465: unsigned long offset1;
466: if (flags & MAP_ANON)
467: offset1 = 0;
468: else
469: offset1 = offset + real_start - start;
470: p = mmap(g2h(real_start), real_end - real_start,
471: prot, flags, fd, offset1);
472: if (p == MAP_FAILED)
473: goto fail;
474: }
475: }
476: the_end1:
477: page_set_flags(start, start + len, prot | PAGE_VALID);
478: the_end:
479: #ifdef DEBUG_MMAP
480: printf("ret=0x" TARGET_FMT_lx "\n", start);
481: page_dump(stdout);
482: printf("\n");
483: #endif
484: mmap_unlock();
485: return start;
486: fail:
487: mmap_unlock();
488: return -1;
489: }
490:
491: int target_munmap(abi_ulong start, abi_ulong len)
492: {
493: abi_ulong end, real_start, real_end, addr;
494: int prot, ret;
495:
496: #ifdef DEBUG_MMAP
497: printf("munmap: start=0x%lx len=0x%lx\n", start, len);
498: #endif
499: if (start & ~TARGET_PAGE_MASK)
500: return -EINVAL;
501: len = TARGET_PAGE_ALIGN(len);
502: if (len == 0)
503: return -EINVAL;
504: mmap_lock();
505: end = start + len;
506: real_start = start & qemu_host_page_mask;
507: real_end = HOST_PAGE_ALIGN(end);
508:
509: if (start > real_start) {
510: /* handle host page containing start */
511: prot = 0;
512: for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
513: prot |= page_get_flags(addr);
514: }
515: if (real_end == real_start + qemu_host_page_size) {
516: for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
517: prot |= page_get_flags(addr);
518: }
519: end = real_end;
520: }
521: if (prot != 0)
522: real_start += qemu_host_page_size;
523: }
524: if (end < real_end) {
525: prot = 0;
526: for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
527: prot |= page_get_flags(addr);
528: }
529: if (prot != 0)
530: real_end -= qemu_host_page_size;
531: }
532:
533: ret = 0;
534: /* unmap what we can */
535: if (real_start < real_end) {
536: ret = munmap(g2h(real_start), real_end - real_start);
537: }
538:
539: if (ret == 0)
540: page_set_flags(start, start + len, 0);
541: mmap_unlock();
542: return ret;
543: }
544:
545: int target_msync(abi_ulong start, abi_ulong len, int flags)
546: {
547: abi_ulong end;
548:
549: if (start & ~TARGET_PAGE_MASK)
550: return -EINVAL;
551: len = TARGET_PAGE_ALIGN(len);
552: end = start + len;
553: if (end < start)
554: return -EINVAL;
555: if (end == start)
556: return 0;
557:
558: start &= qemu_host_page_mask;
559: return msync(g2h(start), end - start, flags);
560: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.