Annotation of qemu/linux-user/mmap.c, revision 1.1.1.2

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.