|
|
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.