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