symbian-qemu-0.9.1-12/qemu-symbian-svp/darwin-user/mmap.c
author johnathan.white@2718R8BGH51.accenture.com
Mon, 08 Mar 2010 18:45:03 +0000
changeset 46 b6935a90ca64
parent 1 2fb8b9db1c86
permissions -rw-r--r--
Modify framebuffer and NGA framebuffer to read screen size from board model dtb file. Optimise memory usuage of frame buffer Add example minigui application with hooks to profiler (which writes results to S:\). Modified NGA framebuffer to run its own dfc queue at high priority

/*
 *  mmap support for qemu
 *
 *  Copyright (c) 2003 Fabrice Bellard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>

#include "qemu.h"

//#define DEBUG_MMAP

/* NOTE: all the constants are the HOST ones */
int target_mprotect(unsigned long start, unsigned long len, int prot)
{
    unsigned long end, host_start, host_end, addr;
    int prot1, ret;

#ifdef DEBUG_MMAP
    printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len,
           prot & PROT_READ ? 'r' : '-',
           prot & PROT_WRITE ? 'w' : '-',
           prot & PROT_EXEC ? 'x' : '-');
#endif

    if ((start & ~TARGET_PAGE_MASK) != 0)
        return -EINVAL;
    len = TARGET_PAGE_ALIGN(len);
    end = start + len;
    if (end < start)
        return -EINVAL;
    if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
        return -EINVAL;
    if (len == 0)
        return 0;

    host_start = start & qemu_host_page_mask;
    host_end = HOST_PAGE_ALIGN(end);
    if (start > host_start) {
        /* handle host page containing start */
        prot1 = prot;
        for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
            prot1 |= page_get_flags(addr);
        }
        if (host_end == host_start + qemu_host_page_size) {
            for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
                prot1 |= page_get_flags(addr);
            }
            end = host_end;
        }
        ret = mprotect((void *)host_start, qemu_host_page_size, prot1 & PAGE_BITS);
        if (ret != 0)
            return ret;
        host_start += qemu_host_page_size;
    }
    if (end < host_end) {
        prot1 = prot;
        for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
            prot1 |= page_get_flags(addr);
        }
        ret = mprotect((void *)(host_end - qemu_host_page_size), qemu_host_page_size,
                       prot1 & PAGE_BITS);
        if (ret != 0)
            return ret;
        host_end -= qemu_host_page_size;
    }

    /* handle the pages in the middle */
    if (host_start < host_end) {
        ret = mprotect((void *)host_start, host_end - host_start, prot);
        if (ret != 0)
            return ret;
    }
    page_set_flags(start, start + len, prot | PAGE_VALID);
    return 0;
}

/* map an incomplete host page */
int mmap_frag(unsigned long host_start,
               unsigned long start, unsigned long end,
               int prot, int flags, int fd, unsigned long offset)
{
    unsigned long host_end, ret, addr;
    int prot1, prot_new;

    host_end = host_start + qemu_host_page_size;

    /* get the protection of the target pages outside the mapping */
    prot1 = 0;
    for(addr = host_start; addr < host_end; addr++) {
        if (addr < start || addr >= end)
            prot1 |= page_get_flags(addr);
    }

    if (prot1 == 0) {
        /* no page was there, so we allocate one */
        ret = (long)mmap((void *)host_start, qemu_host_page_size, prot,
                         flags | MAP_ANONYMOUS, -1, 0);
        if (ret == -1)
            return ret;
    }
    prot1 &= PAGE_BITS;

    prot_new = prot | prot1;
    if (!(flags & MAP_ANONYMOUS)) {
        /* msync() won't work here, so we return an error if write is
           possible while it is a shared mapping */
#ifndef __APPLE__
        if ((flags & MAP_TYPE) == MAP_SHARED &&
#else
        if ((flags &  MAP_SHARED) &&
#endif
            (prot & PROT_WRITE))
            return -EINVAL;

        /* adjust protection to be able to read */
        if (!(prot1 & PROT_WRITE))
            mprotect((void *)host_start, qemu_host_page_size, prot1 | PROT_WRITE);

        /* read the corresponding file data */
        pread(fd, (void *)start, end - start, offset);

        /* put final protection */
        if (prot_new != (prot1 | PROT_WRITE))
            mprotect((void *)host_start, qemu_host_page_size, prot_new);
    } else {
        /* just update the protection */
        if (prot_new != prot1) {
            mprotect((void *)host_start, qemu_host_page_size, prot_new);
        }
    }
    return 0;
}

/* NOTE: all the constants are the HOST ones */
long target_mmap(unsigned long start, unsigned long len, int prot,
                 int flags, int fd, unsigned long offset)
{
    unsigned long ret, end, host_start, host_end, retaddr, host_offset, host_len;
#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
    static unsigned long last_start = 0x40000000;
#endif

#ifdef DEBUG_MMAP
    {
        printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=",
               start, len,
               prot & PROT_READ ? 'r' : '-',
               prot & PROT_WRITE ? 'w' : '-',
               prot & PROT_EXEC ? 'x' : '-');
        if (flags & MAP_FIXED)
            printf("MAP_FIXED ");
        if (flags & MAP_ANONYMOUS)
            printf("MAP_ANON ");
#ifndef MAP_TYPE
# define MAP_TYPE 0x3
#endif
        switch(flags & MAP_TYPE) {
        case MAP_PRIVATE:
            printf("MAP_PRIVATE ");
            break;
        case MAP_SHARED:
            printf("MAP_SHARED ");
            break;
        default:
            printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
            break;
        }
        printf("fd=%d offset=%lx\n", fd, offset);
    }
#endif

    if (offset & ~TARGET_PAGE_MASK)
        return -EINVAL;

    len = TARGET_PAGE_ALIGN(len);
    if (len == 0)
        return start;
    host_start = start & qemu_host_page_mask;

    if (!(flags & MAP_FIXED)) {
#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
        /* tell the kernel to search at the same place as i386 */
        if (host_start == 0) {
            host_start = last_start;
            last_start += HOST_PAGE_ALIGN(len);
        }
#endif
        if (qemu_host_page_size != qemu_real_host_page_size) {
            /* NOTE: this code is only for debugging with '-p' option */
            /* reserve a memory area */
            host_len = HOST_PAGE_ALIGN(len) + qemu_host_page_size - TARGET_PAGE_SIZE;
            host_start = (long)mmap((void *)host_start, host_len, PROT_NONE,
                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
            if (host_start == -1)
                return host_start;
            host_end = host_start + host_len;
            start = HOST_PAGE_ALIGN(host_start);
            end = start + HOST_PAGE_ALIGN(len);
            if (start > host_start)
                munmap((void *)host_start, start - host_start);
            if (end < host_end)
                munmap((void *)end, host_end - end);
            /* use it as a fixed mapping */
            flags |= MAP_FIXED;
        } else {
            /* if not fixed, no need to do anything */
            host_offset = offset & qemu_host_page_mask;
            host_len = len + offset - host_offset;
            start = (long)mmap((void *)host_start, host_len,
                               prot, flags, fd, host_offset);
            if (start == -1)
                return start;
            /* update start so that it points to the file position at 'offset' */
            if (!(flags & MAP_ANONYMOUS))
                start += offset - host_offset;
            goto the_end1;
        }
    }

    if (start & ~TARGET_PAGE_MASK)
        return -EINVAL;
    end = start + len;
    host_end = HOST_PAGE_ALIGN(end);

    /* worst case: we cannot map the file because the offset is not
       aligned, so we read it */
    if (!(flags & MAP_ANONYMOUS) &&
        (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
        /* msync() won't work here, so we return an error if write is
           possible while it is a shared mapping */
#ifndef __APPLE__
        if ((flags & MAP_TYPE) == MAP_SHARED &&
#else
        if ((flags & MAP_SHARED) &&
#endif
            (prot & PROT_WRITE))
            return -EINVAL;
        retaddr = target_mmap(start, len, prot | PROT_WRITE,
                              MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
                              -1, 0);
        if (retaddr == -1)
            return retaddr;
        pread(fd, (void *)start, len, offset);
        if (!(prot & PROT_WRITE)) {
            ret = target_mprotect(start, len, prot);
            if (ret != 0)
                return ret;
        }
        goto the_end;
    }

    /* handle the start of the mapping */
    if (start > host_start) {
        if (host_end == host_start + qemu_host_page_size) {
            /* one single host page */
            ret = mmap_frag(host_start, start, end,
                            prot, flags, fd, offset);
            if (ret == -1)
                return ret;
            goto the_end1;
        }
        ret = mmap_frag(host_start, start, host_start + qemu_host_page_size,
                        prot, flags, fd, offset);
        if (ret == -1)
            return ret;
        host_start += qemu_host_page_size;
    }
    /* handle the end of the mapping */
    if (end < host_end) {
        ret = mmap_frag(host_end - qemu_host_page_size,
                        host_end - qemu_host_page_size, host_end,
                        prot, flags, fd,
                        offset + host_end - qemu_host_page_size - start);
        if (ret == -1)
            return ret;
        host_end -= qemu_host_page_size;
    }

    /* map the middle (easier) */
    if (host_start < host_end) {
        unsigned long offset1;
	if (flags & MAP_ANONYMOUS)
	  offset1 = 0;
	else
	  offset1 = offset + host_start - start;
        ret = (long)mmap((void *)host_start, host_end - host_start,
                         prot, flags, fd, offset1);
        if (ret == -1)
            return ret;
    }
 the_end1:
    page_set_flags(start, start + len, prot | PAGE_VALID);
 the_end:
#ifdef DEBUG_MMAP
    printf("target_mmap: ret=0x%lx\n", (long)start);
    page_dump(stdout);
    printf("\n");
#endif
    return start;
}

int target_munmap(unsigned long start, unsigned long len)
{
    unsigned long end, host_start, host_end, addr;
    int prot, ret;

#ifdef DEBUG_MMAP
    printf("munmap: start=0x%lx len=0x%lx\n", start, len);
#endif
    if (start & ~TARGET_PAGE_MASK)
        return -EINVAL;
    len = TARGET_PAGE_ALIGN(len);
    if (len == 0)
        return -EINVAL;
    end = start + len;
    host_start = start & qemu_host_page_mask;
    host_end = HOST_PAGE_ALIGN(end);

    if (start > host_start) {
        /* handle host page containing start */
        prot = 0;
        for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
            prot |= page_get_flags(addr);
        }
        if (host_end == host_start + qemu_host_page_size) {
            for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
                prot |= page_get_flags(addr);
            }
            end = host_end;
        }
        if (prot != 0)
            host_start += qemu_host_page_size;
    }
    if (end < host_end) {
        prot = 0;
        for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
            prot |= page_get_flags(addr);
        }
        if (prot != 0)
            host_end -= qemu_host_page_size;
    }

    /* unmap what we can */
    if (host_start < host_end) {
        ret = munmap((void *)host_start, host_end - host_start);
        if (ret != 0)
            return ret;
    }

    page_set_flags(start, start + len, 0);
    return 0;
}

/* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
   blocks which have been allocated starting on a host page */
long target_mremap(unsigned long old_addr, unsigned long old_size,
                   unsigned long new_size, unsigned long flags,
                   unsigned long new_addr)
{
#ifndef __APPLE__
    /* XXX: use 5 args syscall */
    new_addr = (long)mremap((void *)old_addr, old_size, new_size, flags);
    if (new_addr == -1)
        return new_addr;
    prot = page_get_flags(old_addr);
    page_set_flags(old_addr, old_addr + old_size, 0);
    page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
    return new_addr;
#else
    qerror("target_mremap: unsupported\n");
#endif

}

int target_msync(unsigned long start, unsigned long len, int flags)
{
    unsigned long end;

    if (start & ~TARGET_PAGE_MASK)
        return -EINVAL;
    len = TARGET_PAGE_ALIGN(len);
    end = start + len;
    if (end < start)
        return -EINVAL;
    if (end == start)
        return 0;

    start &= qemu_host_page_mask;
    return msync((void *)start, end - start, flags);
}