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