(linenum→info "unix/slp.c:2238")

qemu/0.9.1/linux-user/mmap.c

    1: /*
    2:  *  mmap support for qemu
    3:  *
    4:  *  Copyright (c) 2003 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., 675 Mass Ave, Cambridge, MA 02139, USA.
   19:  */
   20: #include <stdlib.h>
   21: #include <stdio.h>
   22: #include <stdarg.h>
   23: #include <string.h>
   24: #include <unistd.h>
   25: #include <errno.h>
   26: #include <sys/mman.h>
   27: 
   28: #include "qemu.h"
   29: 
   30: //#define DEBUG_MMAP
   31: 
   32: /* NOTE: all the constants are the HOST ones, but addresses are target. */
   33: int target_mprotect(abi_ulong start, abi_ulong len, int prot)
   34: {
   35:     abi_ulong end, host_start, host_end, addr;
   36:     int prot1, ret;
   37: 
   38: #ifdef DEBUG_MMAP
   39:     printf("mprotect: start=0x" TARGET_FMT_lx
   40:            "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
   41:            prot & PROT_READ ? 'r' : '-',
   42:            prot & PROT_WRITE ? 'w' : '-',
   43:            prot & PROT_EXEC ? 'x' : '-');
   44: #endif
   45: 
   46:     if ((start & ~TARGET_PAGE_MASK) != 0)
   47:         return -EINVAL;
   48:     len = TARGET_PAGE_ALIGN(len);
   49:     end = start + len;
   50:     if (end < start)
   51:         return -EINVAL;
   52:     if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
   53:         return -EINVAL;
   54:     if (len == 0)
   55:         return 0;
   56: 
   57:     host_start = start & qemu_host_page_mask;
   58:     host_end = HOST_PAGE_ALIGN(end);
   59:     if (start > host_start) {
   60:         /* handle host page containing start */
   61:         prot1 = prot;
   62:         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
   63:             prot1 |= page_get_flags(addr);
   64:         }
   65:         if (host_end == host_start + qemu_host_page_size) {
   66:             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
   67:                 prot1 |= page_get_flags(addr);
   68:             }
   69:             end = host_end;
   70:         }
   71:         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
   72:         if (ret != 0)
   73:             return ret;
   74:         host_start += qemu_host_page_size;
   75:     }
   76:     if (end < host_end) {
   77:         prot1 = prot;
   78:         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
   79:             prot1 |= page_get_flags(addr);
   80:         }
   81:         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
   82:                        prot1 & PAGE_BITS);
   83:         if (ret != 0)
   84:             return ret;
   85:         host_end -= qemu_host_page_size;
   86:     }
   87: 
   88:     /* handle the pages in the middle */
   89:     if (host_start < host_end) {
   90:         ret = mprotect(g2h(host_start), host_end - host_start, prot);
   91:         if (ret != 0)
   92:             return ret;
   93:     }
   94:     page_set_flags(start, start + len, prot | PAGE_VALID);
   95:     return 0;
   96: }
   97: 
   98: /* map an incomplete host page */
   99: static int mmap_frag(abi_ulong real_start,
  100:                      abi_ulong start, abi_ulong end,
  101:                      int prot, int flags, int fd, abi_ulong offset)
  102: {
  103:     abi_ulong real_end, addr;
  104:     void *host_start;
  105:     int prot1, prot_new;
  106: 
  107:     real_end = real_start + qemu_host_page_size;
  108:     host_start = g2h(real_start);
  109: 
  110:     /* get the protection of the target pages outside the mapping */
  111:     prot1 = 0;
  112:     for(addr = real_start; addr < real_end; addr++) {
  113:         if (addr < start || addr >= end)
  114:             prot1 |= page_get_flags(addr);
  115:     }
  116: 
  117:     if (prot1 == 0) {
  118:         /* no page was there, so we allocate one */
  119:         void *p = mmap(host_start, qemu_host_page_size, prot,
  120:                        flags | MAP_ANONYMOUS, -1, 0);
  121:         if (p == MAP_FAILED)
  122:             return -1;
  123:         prot1 = prot;
  124:     }
  125:     prot1 &= PAGE_BITS;
  126: 
  127:     prot_new = prot | prot1;
  128:     if (!(flags & MAP_ANONYMOUS)) {
  129:         /* msync() won't work here, so we return an error if write is
  130:            possible while it is a shared mapping */
  131:         if ((flags & MAP_TYPE) == MAP_SHARED &&
  132:             (prot & PROT_WRITE))
  133:             return -EINVAL;
  134: 
  135:         /* adjust protection to be able to read */
  136:         if (!(prot1 & PROT_WRITE))
  137:             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
  138: 
  139:         /* read the corresponding file data */
  140:         pread(fd, g2h(start), end - start, offset);
  141: 
  142:         /* put final protection */
  143:         if (prot_new != (prot1 | PROT_WRITE))
  144:             mprotect(host_start, qemu_host_page_size, prot_new);
  145:     } else {
  146:         /* just update the protection */
  147:         if (prot_new != prot1) {
  148:             mprotect(host_start, qemu_host_page_size, prot_new);
  149:         }
  150:     }
  151:     return 0;
  152: }
  153: 
  154: #if defined(__CYGWIN__)
  155: /* Cygwin doesn't have a whole lot of address space.  */
  156: static abi_ulong mmap_next_start = 0x18000000;
  157: #else
  158: static abi_ulong mmap_next_start = 0x40000000;
  159: #endif
  160: 
  161: /* find a free memory area of size 'size'. The search starts at
  162:    'start'. If 'start' == 0, then a default start address is used.
  163:    Return -1 if error.
  164: */
  165: /* page_init() marks pages used by the host as reserved to be sure not
  166:    to use them. */
  167: static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
  168: {
  169:     abi_ulong addr, addr1, addr_start;
  170:     int prot;
  171: 
  172:     size = HOST_PAGE_ALIGN(size);
  173:     start = start & qemu_host_page_mask;
  174:     addr = start;
  175:     if (addr == 0)
  176:         addr = mmap_next_start;
  177:     addr_start = addr;
  178:     for(;;) {
  179:         prot = 0;
  180:         for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
  181:             prot |= page_get_flags(addr1);
  182:         }
  183:         if (prot == 0)
  184:             break;
  185:         addr += qemu_host_page_size;
  186:         /* we found nothing */
  187:         if (addr == addr_start)
  188:             return (abi_ulong)-1;
  189:     }
  190:     if (start == 0)
  191:         mmap_next_start = addr + size;
  192:     return addr;
  193: }
  194: 
  195: /* NOTE: all the constants are the HOST ones */
  196: abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
  197:                      int flags, int fd, abi_ulong offset)
  198: {
  199:     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
  200:     unsigned long host_start;
  201: 
  202: #ifdef DEBUG_MMAP
  203:     {
  204:         printf("mmap: start=0x" TARGET_FMT_lx
  205:                " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
  206:                start, len,
  207:                prot & PROT_READ ? 'r' : '-',
  208:                prot & PROT_WRITE ? 'w' : '-',
  209:                prot & PROT_EXEC ? 'x' : '-');
  210:         if (flags & MAP_FIXED)
  211:             printf("MAP_FIXED ");
  212:         if (flags & MAP_ANONYMOUS)
  213:             printf("MAP_ANON ");
  214:         switch(flags & MAP_TYPE) {
  215:         case MAP_PRIVATE:
  216:             printf("MAP_PRIVATE ");
  217:             break;
  218:         case MAP_SHARED:
  219:             printf("MAP_SHARED ");
  220:             break;
  221:         default:
  222:             printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
  223:             break;
  224:         }
  225:         printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
  226:     }
  227: #endif
  228: 
  229:     if (offset & ~TARGET_PAGE_MASK) {
  230:         errno = EINVAL;
  231:         return -1;
  232:     }
  233: 
  234:     len = TARGET_PAGE_ALIGN(len);
  235:     if (len == 0)
  236:         return start;
  237:     real_start = start & qemu_host_page_mask;
  238: 
  239:     if (!(flags & MAP_FIXED)) {
  240:         abi_ulong mmap_start;
  241:         void *p;
  242:         host_offset = offset & qemu_host_page_mask;
  243:         host_len = len + offset - host_offset;
  244:         host_len = HOST_PAGE_ALIGN(host_len);
  245:         mmap_start = mmap_find_vma(real_start, host_len);
  246:         if (mmap_start == (abi_ulong)-1) {
  247:             errno = ENOMEM;
  248:             return -1;
  249:         }
  250:         /* Note: we prefer to control the mapping address. It is
  251:            especially important if qemu_host_page_size >
  252:            qemu_real_host_page_size */
  253:         p = mmap(g2h(mmap_start),
  254:                  host_len, prot, flags | MAP_FIXED, fd, host_offset);
  255:         if (p == MAP_FAILED)
  256:             return -1;
  257:         /* update start so that it points to the file position at 'offset' */
  258:         host_start = (unsigned long)p;
  259:         if (!(flags & MAP_ANONYMOUS))
  260:             host_start += offset - host_offset;
  261:         start = h2g(host_start);
  262:     } else {
  263:         if (start & ~TARGET_PAGE_MASK) {
  264:             errno = EINVAL;
  265:             return -1;
  266:         }
  267:         end = start + len;
  268:         real_end = HOST_PAGE_ALIGN(end);
  269:         
  270:         /* worst case: we cannot map the file because the offset is not
  271:            aligned, so we read it */
  272:         if (!(flags & MAP_ANONYMOUS) &&
  273:             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
  274:             /* msync() won't work here, so we return an error if write is
  275:                possible while it is a shared mapping */
  276:             if ((flags & MAP_TYPE) == MAP_SHARED &&
  277:                 (prot & PROT_WRITE)) {
  278:                 errno = EINVAL;
  279:                 return -1;
  280:             }
  281:             retaddr = target_mmap(start, len, prot | PROT_WRITE,
  282:                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
  283:                                   -1, 0);
  284:             if (retaddr == -1)
  285:                 return -1;
  286:             pread(fd, g2h(start), len, offset);
  287:             if (!(prot & PROT_WRITE)) {
  288:                 ret = target_mprotect(start, len, prot);
  289:                 if (ret != 0)
  290:                     return ret;
  291:             }
  292:             goto the_end;
  293:         }
  294:         
  295:         /* handle the start of the mapping */
  296:         if (start > real_start) {
  297:             if (real_end == real_start + qemu_host_page_size) {
  298:                 /* one single host page */
  299:                 ret = mmap_frag(real_start, start, end,
  300:                                 prot, flags, fd, offset);
  301:                 if (ret == -1)
  302:                     return ret;
  303:                 goto the_end1;
  304:             }
  305:             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
  306:                             prot, flags, fd, offset);
  307:             if (ret == -1)
  308:                 return ret;
  309:             real_start += qemu_host_page_size;
  310:         }
  311:         /* handle the end of the mapping */
  312:         if (end < real_end) {
  313:             ret = mmap_frag(real_end - qemu_host_page_size,
  314:                             real_end - qemu_host_page_size, real_end,
  315:                             prot, flags, fd,
  316:                             offset + real_end - qemu_host_page_size - start);
  317:             if (ret == -1)
  318:                 return -1;
  319:             real_end -= qemu_host_page_size;
  320:         }
  321: 
  322:         /* map the middle (easier) */
  323:         if (real_start < real_end) {
  324:             void *p;
  325:             unsigned long offset1;
  326:             if (flags & MAP_ANONYMOUS)
  327:                 offset1 = 0;
  328:             else
  329:                 offset1 = offset + real_start - start;
  330:             p = mmap(g2h(real_start), real_end - real_start,
  331:                      prot, flags, fd, offset1);
  332:             if (p == MAP_FAILED)
  333:                 return -1;
  334:         }
  335:     }
  336:  the_end1:
  337:     page_set_flags(start, start + len, prot | PAGE_VALID);
  338:  the_end:
  339: #ifdef DEBUG_MMAP
  340:     printf("ret=0x%llx\n", start);
  341:     page_dump(stdout);
  342:     printf("\n");
  343: #endif
  344:     return start;
  345: }
  346: 
  347: int target_munmap(abi_ulong start, abi_ulong len)
  348: {
  349:     abi_ulong end, real_start, real_end, addr;
  350:     int prot, ret;
  351: 
  352: #ifdef DEBUG_MMAP
  353:     printf("munmap: start=0x%lx len=0x%lx\n", start, len);
  354: #endif
  355:     if (start & ~TARGET_PAGE_MASK)
  356:         return -EINVAL;
  357:     len = TARGET_PAGE_ALIGN(len);
  358:     if (len == 0)
  359:         return -EINVAL;
  360:     end = start + len;
  361:     real_start = start & qemu_host_page_mask;
  362:     real_end = HOST_PAGE_ALIGN(end);
  363: 
  364:     if (start > real_start) {
  365:         /* handle host page containing start */
  366:         prot = 0;
  367:         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
  368:             prot |= page_get_flags(addr);
  369:         }
  370:         if (real_end == real_start + qemu_host_page_size) {
  371:             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
  372:                 prot |= page_get_flags(addr);
  373:             }
  374:             end = real_end;
  375:         }
  376:         if (prot != 0)
  377:             real_start += qemu_host_page_size;
  378:     }
  379:     if (end < real_end) {
  380:         prot = 0;
  381:         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
  382:             prot |= page_get_flags(addr);
  383:         }
  384:         if (prot != 0)
  385:             real_end -= qemu_host_page_size;
  386:     }
  387: 
  388:     /* unmap what we can */
  389:     if (real_start < real_end) {
  390:         ret = munmap(g2h(real_start), real_end - real_start);
  391:         if (ret != 0)
  392:             return ret;
  393:     }
  394: 
  395:     page_set_flags(start, start + len, 0);
  396:     return 0;
  397: }
  398: 
  399: /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
  400:    blocks which have been allocated starting on a host page */
  401: abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
  402:                        abi_ulong new_size, unsigned long flags,
  403:                        abi_ulong new_addr)
  404: {
  405:     int prot;
  406:     unsigned long host_addr;
  407: 
  408:     /* XXX: use 5 args syscall */
  409:     host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
  410:     if (host_addr == -1)
  411:         return -1;
  412:     new_addr = h2g(host_addr);
  413:     prot = page_get_flags(old_addr);
  414:     page_set_flags(old_addr, old_addr + old_size, 0);
  415:     page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
  416:     return new_addr;
  417: }
  418: 
  419: int target_msync(abi_ulong start, abi_ulong len, int flags)
  420: {
  421:     abi_ulong end;
  422: 
  423:     if (start & ~TARGET_PAGE_MASK)
  424:         return -EINVAL;
  425:     len = TARGET_PAGE_ALIGN(len);
  426:     end = start + len;
  427:     if (end < start)
  428:         return -EINVAL;
  429:     if (end == start)
  430:         return 0;
  431: 
  432:     start &= qemu_host_page_mask;
  433:     return msync(g2h(start), end - start, flags);
  434: }
  435: 
Syntax (Markdown)