File:  [Qemu by Fabrice Bellard] / qemu / bsd-user / mmap.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:33:03 2018 UTC (3 years, 3 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu1000, qemu0151, qemu0150, qemu0141, qemu0140, qemu0130, HEAD
qemu 0.13.0

    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, see <http://www.gnu.org/licenses/>.
   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: 
   33: #if defined(CONFIG_USE_NPTL)
   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: 
   85:     if (h2g_valid(p)) {
   86:         /* Allocated region overlaps guest address space.
   87:            This may recurse.  */
   88:         abi_ulong addr = h2g(p);
   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: 
   97: void *qemu_malloc(size_t size)
   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.  */
  107: void * qemu_mallocz(size_t size)
  108: {
  109:     return qemu_malloc(size);
  110: }
  111: 
  112: void qemu_free(void *ptr)
  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: 
  122: void *qemu_realloc(void *ptr, size_t size)
  123: {
  124:     size_t old_size, copy;
  125:     void *new_ptr;
  126: 
  127:     if (!ptr)
  128:         return qemu_malloc(size);
  129:     old_size = *(size_t *)((char *)ptr - 16);
  130:     copy = old_size < size ? old_size : size;
  131:     new_ptr = qemu_malloc(size);
  132:     memcpy(new_ptr, ptr, copy);
  133:     qemu_free(ptr);
  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))
  242:             return -1;
  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: }

unix.superglobalmegacorp.com