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