Statistics
| Branch: | Revision:

root / linux-user / mmap.c @ 93148aa5

History | View | Annotate | Download (23 kB)

1 54936004 bellard
/*
2 54936004 bellard
 *  mmap support for qemu
3 5fafdf24 ths
 *
4 54936004 bellard
 *  Copyright (c) 2003 Fabrice Bellard
5 54936004 bellard
 *
6 54936004 bellard
 *  This program is free software; you can redistribute it and/or modify
7 54936004 bellard
 *  it under the terms of the GNU General Public License as published by
8 54936004 bellard
 *  the Free Software Foundation; either version 2 of the License, or
9 54936004 bellard
 *  (at your option) any later version.
10 54936004 bellard
 *
11 54936004 bellard
 *  This program is distributed in the hope that it will be useful,
12 54936004 bellard
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 54936004 bellard
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 54936004 bellard
 *  GNU General Public License for more details.
15 54936004 bellard
 *
16 54936004 bellard
 *  You should have received a copy of the GNU General Public License
17 8167ee88 Blue Swirl
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18 54936004 bellard
 */
19 54936004 bellard
#include <stdlib.h>
20 54936004 bellard
#include <stdio.h>
21 54936004 bellard
#include <stdarg.h>
22 54936004 bellard
#include <string.h>
23 54936004 bellard
#include <unistd.h>
24 54936004 bellard
#include <errno.h>
25 54c5a2ae edgar_igl
#include <sys/types.h>
26 54c5a2ae edgar_igl
#include <sys/stat.h>
27 54936004 bellard
#include <sys/mman.h>
28 3af72a4d blueswir1
#include <linux/mman.h>
29 3af72a4d blueswir1
#include <linux/unistd.h>
30 54936004 bellard
31 54936004 bellard
#include "qemu.h"
32 78f5bf1e blueswir1
#include "qemu-common.h"
33 54936004 bellard
34 54936004 bellard
//#define DEBUG_MMAP
35 54936004 bellard
36 2f7bb878 Juan Quintela
#if defined(CONFIG_USE_NPTL)
37 1e6eec8b Blue Swirl
static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
38 dfd3f85c Juan Quintela
static __thread int mmap_lock_count;
39 c8a706fe pbrook
40 c8a706fe pbrook
void mmap_lock(void)
41 c8a706fe pbrook
{
42 c8a706fe pbrook
    if (mmap_lock_count++ == 0) {
43 c8a706fe pbrook
        pthread_mutex_lock(&mmap_mutex);
44 c8a706fe pbrook
    }
45 c8a706fe pbrook
}
46 c8a706fe pbrook
47 c8a706fe pbrook
void mmap_unlock(void)
48 c8a706fe pbrook
{
49 c8a706fe pbrook
    if (--mmap_lock_count == 0) {
50 c8a706fe pbrook
        pthread_mutex_unlock(&mmap_mutex);
51 c8a706fe pbrook
    }
52 c8a706fe pbrook
}
53 d5975363 pbrook
54 d5975363 pbrook
/* Grab lock to make sure things are in a consistent state after fork().  */
55 d5975363 pbrook
void mmap_fork_start(void)
56 d5975363 pbrook
{
57 d5975363 pbrook
    if (mmap_lock_count)
58 d5975363 pbrook
        abort();
59 d5975363 pbrook
    pthread_mutex_lock(&mmap_mutex);
60 d5975363 pbrook
}
61 d5975363 pbrook
62 d5975363 pbrook
void mmap_fork_end(int child)
63 d5975363 pbrook
{
64 d5975363 pbrook
    if (child)
65 d5975363 pbrook
        pthread_mutex_init(&mmap_mutex, NULL);
66 d5975363 pbrook
    else
67 d5975363 pbrook
        pthread_mutex_unlock(&mmap_mutex);
68 d5975363 pbrook
}
69 c8a706fe pbrook
#else
70 c8a706fe pbrook
/* We aren't threadsafe to start with, so no need to worry about locking.  */
71 c8a706fe pbrook
void mmap_lock(void)
72 c8a706fe pbrook
{
73 c8a706fe pbrook
}
74 c8a706fe pbrook
75 c8a706fe pbrook
void mmap_unlock(void)
76 c8a706fe pbrook
{
77 c8a706fe pbrook
}
78 c8a706fe pbrook
#endif
79 c8a706fe pbrook
80 53a5960a pbrook
/* NOTE: all the constants are the HOST ones, but addresses are target. */
81 992f48a0 blueswir1
int target_mprotect(abi_ulong start, abi_ulong len, int prot)
82 54936004 bellard
{
83 992f48a0 blueswir1
    abi_ulong end, host_start, host_end, addr;
84 54936004 bellard
    int prot1, ret;
85 54936004 bellard
86 54936004 bellard
#ifdef DEBUG_MMAP
87 0bf9e31a Blue Swirl
    printf("mprotect: start=0x" TARGET_ABI_FMT_lx
88 0bf9e31a Blue Swirl
           "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
89 54936004 bellard
           prot & PROT_READ ? 'r' : '-',
90 54936004 bellard
           prot & PROT_WRITE ? 'w' : '-',
91 54936004 bellard
           prot & PROT_EXEC ? 'x' : '-');
92 54936004 bellard
#endif
93 54936004 bellard
94 54936004 bellard
    if ((start & ~TARGET_PAGE_MASK) != 0)
95 54936004 bellard
        return -EINVAL;
96 54936004 bellard
    len = TARGET_PAGE_ALIGN(len);
97 54936004 bellard
    end = start + len;
98 54936004 bellard
    if (end < start)
99 54936004 bellard
        return -EINVAL;
100 171cd1cd balrog
    prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
101 54936004 bellard
    if (len == 0)
102 54936004 bellard
        return 0;
103 3b46e624 ths
104 c8a706fe pbrook
    mmap_lock();
105 83fb7adf bellard
    host_start = start & qemu_host_page_mask;
106 54936004 bellard
    host_end = HOST_PAGE_ALIGN(end);
107 54936004 bellard
    if (start > host_start) {
108 54936004 bellard
        /* handle host page containing start */
109 54936004 bellard
        prot1 = prot;
110 54936004 bellard
        for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
111 54936004 bellard
            prot1 |= page_get_flags(addr);
112 54936004 bellard
        }
113 83fb7adf bellard
        if (host_end == host_start + qemu_host_page_size) {
114 d418c81e bellard
            for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
115 d418c81e bellard
                prot1 |= page_get_flags(addr);
116 d418c81e bellard
            }
117 d418c81e bellard
            end = host_end;
118 d418c81e bellard
        }
119 53a5960a pbrook
        ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
120 54936004 bellard
        if (ret != 0)
121 c8a706fe pbrook
            goto error;
122 83fb7adf bellard
        host_start += qemu_host_page_size;
123 54936004 bellard
    }
124 54936004 bellard
    if (end < host_end) {
125 54936004 bellard
        prot1 = prot;
126 54936004 bellard
        for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
127 54936004 bellard
            prot1 |= page_get_flags(addr);
128 54936004 bellard
        }
129 5fafdf24 ths
        ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
130 54936004 bellard
                       prot1 & PAGE_BITS);
131 54936004 bellard
        if (ret != 0)
132 c8a706fe pbrook
            goto error;
133 83fb7adf bellard
        host_end -= qemu_host_page_size;
134 54936004 bellard
    }
135 3b46e624 ths
136 54936004 bellard
    /* handle the pages in the middle */
137 54936004 bellard
    if (host_start < host_end) {
138 53a5960a pbrook
        ret = mprotect(g2h(host_start), host_end - host_start, prot);
139 54936004 bellard
        if (ret != 0)
140 c8a706fe pbrook
            goto error;
141 54936004 bellard
    }
142 54936004 bellard
    page_set_flags(start, start + len, prot | PAGE_VALID);
143 c8a706fe pbrook
    mmap_unlock();
144 54936004 bellard
    return 0;
145 c8a706fe pbrook
error:
146 c8a706fe pbrook
    mmap_unlock();
147 c8a706fe pbrook
    return ret;
148 54936004 bellard
}
149 54936004 bellard
150 54936004 bellard
/* map an incomplete host page */
151 992f48a0 blueswir1
static int mmap_frag(abi_ulong real_start,
152 992f48a0 blueswir1
                     abi_ulong start, abi_ulong end,
153 992f48a0 blueswir1
                     int prot, int flags, int fd, abi_ulong offset)
154 54936004 bellard
{
155 80210bcd ths
    abi_ulong real_end, addr;
156 53a5960a pbrook
    void *host_start;
157 54936004 bellard
    int prot1, prot_new;
158 54936004 bellard
159 53a5960a pbrook
    real_end = real_start + qemu_host_page_size;
160 53a5960a pbrook
    host_start = g2h(real_start);
161 54936004 bellard
162 54936004 bellard
    /* get the protection of the target pages outside the mapping */
163 54936004 bellard
    prot1 = 0;
164 53a5960a pbrook
    for(addr = real_start; addr < real_end; addr++) {
165 54936004 bellard
        if (addr < start || addr >= end)
166 54936004 bellard
            prot1 |= page_get_flags(addr);
167 54936004 bellard
    }
168 3b46e624 ths
169 54936004 bellard
    if (prot1 == 0) {
170 54936004 bellard
        /* no page was there, so we allocate one */
171 80210bcd ths
        void *p = mmap(host_start, qemu_host_page_size, prot,
172 80210bcd ths
                       flags | MAP_ANONYMOUS, -1, 0);
173 80210bcd ths
        if (p == MAP_FAILED)
174 80210bcd ths
            return -1;
175 53a5960a pbrook
        prot1 = prot;
176 54936004 bellard
    }
177 54936004 bellard
    prot1 &= PAGE_BITS;
178 54936004 bellard
179 54936004 bellard
    prot_new = prot | prot1;
180 54936004 bellard
    if (!(flags & MAP_ANONYMOUS)) {
181 54936004 bellard
        /* msync() won't work here, so we return an error if write is
182 54936004 bellard
           possible while it is a shared mapping */
183 54936004 bellard
        if ((flags & MAP_TYPE) == MAP_SHARED &&
184 54936004 bellard
            (prot & PROT_WRITE))
185 ee636500 Juan Quintela
            return -1;
186 54936004 bellard
187 54936004 bellard
        /* adjust protection to be able to read */
188 54936004 bellard
        if (!(prot1 & PROT_WRITE))
189 53a5960a pbrook
            mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
190 3b46e624 ths
191 54936004 bellard
        /* read the corresponding file data */
192 fb7e378c Kirill A. Shutemov
        if (pread(fd, g2h(start), end - start, offset) == -1)
193 fb7e378c Kirill A. Shutemov
            return -1;
194 3b46e624 ths
195 54936004 bellard
        /* put final protection */
196 54936004 bellard
        if (prot_new != (prot1 | PROT_WRITE))
197 53a5960a pbrook
            mprotect(host_start, qemu_host_page_size, prot_new);
198 54936004 bellard
    } else {
199 54936004 bellard
        /* just update the protection */
200 54936004 bellard
        if (prot_new != prot1) {
201 53a5960a pbrook
            mprotect(host_start, qemu_host_page_size, prot_new);
202 54936004 bellard
        }
203 54936004 bellard
    }
204 54936004 bellard
    return 0;
205 54936004 bellard
}
206 54936004 bellard
207 14f24e14 Richard Henderson
#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
208 14f24e14 Richard Henderson
# define TASK_UNMAPPED_BASE  (1ul << 38)
209 14f24e14 Richard Henderson
#elif defined(__CYGWIN__)
210 a03e2d42 bellard
/* Cygwin doesn't have a whole lot of address space.  */
211 14f24e14 Richard Henderson
# define TASK_UNMAPPED_BASE  0x18000000
212 a03e2d42 bellard
#else
213 14f24e14 Richard Henderson
# define TASK_UNMAPPED_BASE  0x40000000
214 a03e2d42 bellard
#endif
215 14f24e14 Richard Henderson
static abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
216 a03e2d42 bellard
217 0776590d pbrook
unsigned long last_brk;
218 0776590d pbrook
219 4e655712 Peter Maydell
#ifdef CONFIG_USE_GUEST_BASE
220 68a1c816 Paul Brook
/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
221 68a1c816 Paul Brook
   of guest address space.  */
222 68a1c816 Paul Brook
static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
223 68a1c816 Paul Brook
{
224 68a1c816 Paul Brook
    abi_ulong addr;
225 68a1c816 Paul Brook
    abi_ulong last_addr;
226 68a1c816 Paul Brook
    int prot;
227 68a1c816 Paul Brook
    int looped = 0;
228 68a1c816 Paul Brook
229 18e9ea8a Aurelien Jarno
    if (size > RESERVED_VA) {
230 68a1c816 Paul Brook
        return (abi_ulong)-1;
231 68a1c816 Paul Brook
    }
232 68a1c816 Paul Brook
233 68a1c816 Paul Brook
    last_addr = start;
234 68a1c816 Paul Brook
    for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) {
235 18e9ea8a Aurelien Jarno
        if (last_addr + size >= RESERVED_VA
236 68a1c816 Paul Brook
            || (abi_ulong)(last_addr + size) < last_addr) {
237 68a1c816 Paul Brook
            if (looped) {
238 68a1c816 Paul Brook
                return (abi_ulong)-1;
239 68a1c816 Paul Brook
            }
240 68a1c816 Paul Brook
            last_addr = qemu_host_page_size;
241 68a1c816 Paul Brook
            addr = 0;
242 68a1c816 Paul Brook
            looped = 1;
243 68a1c816 Paul Brook
            continue;
244 68a1c816 Paul Brook
        }
245 68a1c816 Paul Brook
        prot = page_get_flags(addr);
246 68a1c816 Paul Brook
        if (prot) {
247 68a1c816 Paul Brook
            last_addr = addr + qemu_host_page_size;
248 68a1c816 Paul Brook
        }
249 68a1c816 Paul Brook
    }
250 68a1c816 Paul Brook
    mmap_next_start = addr;
251 68a1c816 Paul Brook
    return last_addr;
252 68a1c816 Paul Brook
}
253 4e655712 Peter Maydell
#endif
254 68a1c816 Paul Brook
255 fe3b4152 Kirill A. Shutemov
/*
256 fe3b4152 Kirill A. Shutemov
 * Find and reserve a free memory area of size 'size'. The search
257 fe3b4152 Kirill A. Shutemov
 * starts at 'start'.
258 fe3b4152 Kirill A. Shutemov
 * It must be called with mmap_lock() held.
259 fe3b4152 Kirill A. Shutemov
 * Return -1 if error.
260 fe3b4152 Kirill A. Shutemov
 */
261 9ad197d9 Riku Voipio
abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
262 a03e2d42 bellard
{
263 14f24e14 Richard Henderson
    void *ptr, *prev;
264 fe3b4152 Kirill A. Shutemov
    abi_ulong addr;
265 14f24e14 Richard Henderson
    int wrapped, repeat;
266 fe3b4152 Kirill A. Shutemov
267 fe3b4152 Kirill A. Shutemov
    /* If 'start' == 0, then a default start address is used. */
268 14f24e14 Richard Henderson
    if (start == 0) {
269 fe3b4152 Kirill A. Shutemov
        start = mmap_next_start;
270 14f24e14 Richard Henderson
    } else {
271 14f24e14 Richard Henderson
        start &= qemu_host_page_mask;
272 14f24e14 Richard Henderson
    }
273 14f24e14 Richard Henderson
274 14f24e14 Richard Henderson
    size = HOST_PAGE_ALIGN(size);
275 fe3b4152 Kirill A. Shutemov
276 4e655712 Peter Maydell
#ifdef CONFIG_USE_GUEST_BASE
277 18e9ea8a Aurelien Jarno
    if (RESERVED_VA) {
278 68a1c816 Paul Brook
        return mmap_find_vma_reserved(start, size);
279 68a1c816 Paul Brook
    }
280 4e655712 Peter Maydell
#endif
281 68a1c816 Paul Brook
282 a03e2d42 bellard
    addr = start;
283 14f24e14 Richard Henderson
    wrapped = repeat = 0;
284 14f24e14 Richard Henderson
    prev = 0;
285 fe3b4152 Kirill A. Shutemov
286 14f24e14 Richard Henderson
    for (;; prev = ptr) {
287 fe3b4152 Kirill A. Shutemov
        /*
288 fe3b4152 Kirill A. Shutemov
         * Reserve needed memory area to avoid a race.
289 fe3b4152 Kirill A. Shutemov
         * It should be discarded using:
290 fe3b4152 Kirill A. Shutemov
         *  - mmap() with MAP_FIXED flag
291 fe3b4152 Kirill A. Shutemov
         *  - mremap() with MREMAP_FIXED flag
292 fe3b4152 Kirill A. Shutemov
         *  - shmat() with SHM_REMAP flag
293 fe3b4152 Kirill A. Shutemov
         */
294 14f24e14 Richard Henderson
        ptr = mmap(g2h(addr), size, PROT_NONE,
295 fe3b4152 Kirill A. Shutemov
                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
296 fe3b4152 Kirill A. Shutemov
297 fe3b4152 Kirill A. Shutemov
        /* ENOMEM, if host address space has no memory */
298 14f24e14 Richard Henderson
        if (ptr == MAP_FAILED) {
299 fe3b4152 Kirill A. Shutemov
            return (abi_ulong)-1;
300 14f24e14 Richard Henderson
        }
301 14f24e14 Richard Henderson
302 14f24e14 Richard Henderson
        /* Count the number of sequential returns of the same address.
303 14f24e14 Richard Henderson
           This is used to modify the search algorithm below.  */
304 14f24e14 Richard Henderson
        repeat = (ptr == prev ? repeat + 1 : 0);
305 14f24e14 Richard Henderson
306 14f24e14 Richard Henderson
        if (h2g_valid(ptr + size - 1)) {
307 14f24e14 Richard Henderson
            addr = h2g(ptr);
308 fe3b4152 Kirill A. Shutemov
309 14f24e14 Richard Henderson
            if ((addr & ~TARGET_PAGE_MASK) == 0) {
310 14f24e14 Richard Henderson
                /* Success.  */
311 14f24e14 Richard Henderson
                if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
312 14f24e14 Richard Henderson
                    mmap_next_start = addr + size;
313 14f24e14 Richard Henderson
                }
314 14f24e14 Richard Henderson
                return addr;
315 14f24e14 Richard Henderson
            }
316 fe3b4152 Kirill A. Shutemov
317 14f24e14 Richard Henderson
            /* The address is not properly aligned for the target.  */
318 14f24e14 Richard Henderson
            switch (repeat) {
319 14f24e14 Richard Henderson
            case 0:
320 14f24e14 Richard Henderson
                /* Assume the result that the kernel gave us is the
321 14f24e14 Richard Henderson
                   first with enough free space, so start again at the
322 14f24e14 Richard Henderson
                   next higher target page.  */
323 14f24e14 Richard Henderson
                addr = TARGET_PAGE_ALIGN(addr);
324 14f24e14 Richard Henderson
                break;
325 14f24e14 Richard Henderson
            case 1:
326 14f24e14 Richard Henderson
                /* Sometimes the kernel decides to perform the allocation
327 14f24e14 Richard Henderson
                   at the top end of memory instead.  */
328 14f24e14 Richard Henderson
                addr &= TARGET_PAGE_MASK;
329 14f24e14 Richard Henderson
                break;
330 14f24e14 Richard Henderson
            case 2:
331 14f24e14 Richard Henderson
                /* Start over at low memory.  */
332 14f24e14 Richard Henderson
                addr = 0;
333 14f24e14 Richard Henderson
                break;
334 14f24e14 Richard Henderson
            default:
335 14f24e14 Richard Henderson
                /* Fail.  This unaligned block must the last.  */
336 14f24e14 Richard Henderson
                addr = -1;
337 14f24e14 Richard Henderson
                break;
338 14f24e14 Richard Henderson
            }
339 14f24e14 Richard Henderson
        } else {
340 14f24e14 Richard Henderson
            /* Since the result the kernel gave didn't fit, start
341 14f24e14 Richard Henderson
               again at low memory.  If any repetition, fail.  */
342 14f24e14 Richard Henderson
            addr = (repeat ? -1 : 0);
343 14f24e14 Richard Henderson
        }
344 14f24e14 Richard Henderson
345 14f24e14 Richard Henderson
        /* Unmap and try again.  */
346 fe3b4152 Kirill A. Shutemov
        munmap(ptr, size);
347 fe3b4152 Kirill A. Shutemov
348 14f24e14 Richard Henderson
        /* ENOMEM if we checked the whole of the target address space.  */
349 d0b3e4f5 Blue Swirl
        if (addr == (abi_ulong)-1) {
350 a03e2d42 bellard
            return (abi_ulong)-1;
351 14f24e14 Richard Henderson
        } else if (addr == 0) {
352 14f24e14 Richard Henderson
            if (wrapped) {
353 14f24e14 Richard Henderson
                return (abi_ulong)-1;
354 14f24e14 Richard Henderson
            }
355 14f24e14 Richard Henderson
            wrapped = 1;
356 14f24e14 Richard Henderson
            /* Don't actually use 0 when wrapping, instead indicate
357 8186e783 Stefan Weil
               that we'd truly like an allocation in low memory.  */
358 14f24e14 Richard Henderson
            addr = (mmap_min_addr > TARGET_PAGE_SIZE
359 14f24e14 Richard Henderson
                     ? TARGET_PAGE_ALIGN(mmap_min_addr)
360 14f24e14 Richard Henderson
                     : TARGET_PAGE_SIZE);
361 14f24e14 Richard Henderson
        } else if (wrapped && addr >= start) {
362 14f24e14 Richard Henderson
            return (abi_ulong)-1;
363 14f24e14 Richard Henderson
        }
364 a03e2d42 bellard
    }
365 a03e2d42 bellard
}
366 a03e2d42 bellard
367 54936004 bellard
/* NOTE: all the constants are the HOST ones */
368 992f48a0 blueswir1
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
369 992f48a0 blueswir1
                     int flags, int fd, abi_ulong offset)
370 54936004 bellard
{
371 992f48a0 blueswir1
    abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
372 a5b85f79 ths
    unsigned long host_start;
373 54936004 bellard
374 c8a706fe pbrook
    mmap_lock();
375 54936004 bellard
#ifdef DEBUG_MMAP
376 54936004 bellard
    {
377 0bf9e31a Blue Swirl
        printf("mmap: start=0x" TARGET_ABI_FMT_lx
378 0bf9e31a Blue Swirl
               " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
379 5fafdf24 ths
               start, len,
380 54936004 bellard
               prot & PROT_READ ? 'r' : '-',
381 54936004 bellard
               prot & PROT_WRITE ? 'w' : '-',
382 54936004 bellard
               prot & PROT_EXEC ? 'x' : '-');
383 54936004 bellard
        if (flags & MAP_FIXED)
384 54936004 bellard
            printf("MAP_FIXED ");
385 54936004 bellard
        if (flags & MAP_ANONYMOUS)
386 54936004 bellard
            printf("MAP_ANON ");
387 54936004 bellard
        switch(flags & MAP_TYPE) {
388 54936004 bellard
        case MAP_PRIVATE:
389 54936004 bellard
            printf("MAP_PRIVATE ");
390 54936004 bellard
            break;
391 54936004 bellard
        case MAP_SHARED:
392 54936004 bellard
            printf("MAP_SHARED ");
393 54936004 bellard
            break;
394 54936004 bellard
        default:
395 54936004 bellard
            printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
396 54936004 bellard
            break;
397 54936004 bellard
        }
398 0bf9e31a Blue Swirl
        printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
399 54936004 bellard
    }
400 54936004 bellard
#endif
401 54936004 bellard
402 e89f07d3 pbrook
    if (offset & ~TARGET_PAGE_MASK) {
403 e89f07d3 pbrook
        errno = EINVAL;
404 c8a706fe pbrook
        goto fail;
405 e89f07d3 pbrook
    }
406 54936004 bellard
407 54936004 bellard
    len = TARGET_PAGE_ALIGN(len);
408 54936004 bellard
    if (len == 0)
409 c8a706fe pbrook
        goto the_end;
410 53a5960a pbrook
    real_start = start & qemu_host_page_mask;
411 54936004 bellard
412 54c5a2ae edgar_igl
    /* When mapping files into a memory area larger than the file, accesses
413 54c5a2ae edgar_igl
       to pages beyond the file size will cause a SIGBUS. 
414 54c5a2ae edgar_igl

415 54c5a2ae edgar_igl
       For example, if mmaping a file of 100 bytes on a host with 4K pages
416 54c5a2ae edgar_igl
       emulating a target with 8K pages, the target expects to be able to
417 54c5a2ae edgar_igl
       access the first 8K. But the host will trap us on any access beyond
418 54c5a2ae edgar_igl
       4K.  
419 54c5a2ae edgar_igl

420 54c5a2ae edgar_igl
       When emulating a target with a larger page-size than the hosts, we
421 54c5a2ae edgar_igl
       may need to truncate file maps at EOF and add extra anonymous pages
422 54c5a2ae edgar_igl
       up to the targets page boundary.  */
423 54c5a2ae edgar_igl
424 54c5a2ae edgar_igl
    if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
425 54c5a2ae edgar_igl
        && !(flags & MAP_ANONYMOUS)) {
426 54c5a2ae edgar_igl
       struct stat sb;
427 54c5a2ae edgar_igl
428 54c5a2ae edgar_igl
       if (fstat (fd, &sb) == -1)
429 54c5a2ae edgar_igl
           goto fail;
430 54c5a2ae edgar_igl
431 54c5a2ae edgar_igl
       /* Are we trying to create a map beyond EOF?.  */
432 54c5a2ae edgar_igl
       if (offset + len > sb.st_size) {
433 54c5a2ae edgar_igl
           /* If so, truncate the file map at eof aligned with 
434 54c5a2ae edgar_igl
              the hosts real pagesize. Additional anonymous maps
435 54c5a2ae edgar_igl
              will be created beyond EOF.  */
436 54c5a2ae edgar_igl
           len = (sb.st_size - offset);
437 54c5a2ae edgar_igl
           len += qemu_real_host_page_size - 1;
438 54c5a2ae edgar_igl
           len &= ~(qemu_real_host_page_size - 1);
439 54c5a2ae edgar_igl
       }
440 54c5a2ae edgar_igl
    }
441 54c5a2ae edgar_igl
442 54936004 bellard
    if (!(flags & MAP_FIXED)) {
443 a03e2d42 bellard
        abi_ulong mmap_start;
444 a03e2d42 bellard
        void *p;
445 a03e2d42 bellard
        host_offset = offset & qemu_host_page_mask;
446 a03e2d42 bellard
        host_len = len + offset - host_offset;
447 a03e2d42 bellard
        host_len = HOST_PAGE_ALIGN(host_len);
448 a03e2d42 bellard
        mmap_start = mmap_find_vma(real_start, host_len);
449 a03e2d42 bellard
        if (mmap_start == (abi_ulong)-1) {
450 a03e2d42 bellard
            errno = ENOMEM;
451 c8a706fe pbrook
            goto fail;
452 54936004 bellard
        }
453 a03e2d42 bellard
        /* Note: we prefer to control the mapping address. It is
454 a03e2d42 bellard
           especially important if qemu_host_page_size >
455 a03e2d42 bellard
           qemu_real_host_page_size */
456 a03e2d42 bellard
        p = mmap(g2h(mmap_start),
457 54c5a2ae edgar_igl
                 host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
458 a03e2d42 bellard
        if (p == MAP_FAILED)
459 c8a706fe pbrook
            goto fail;
460 a03e2d42 bellard
        /* update start so that it points to the file position at 'offset' */
461 a03e2d42 bellard
        host_start = (unsigned long)p;
462 54c5a2ae edgar_igl
        if (!(flags & MAP_ANONYMOUS)) {
463 54c5a2ae edgar_igl
            p = mmap(g2h(mmap_start), len, prot, 
464 54c5a2ae edgar_igl
                     flags | MAP_FIXED, fd, host_offset);
465 a03e2d42 bellard
            host_start += offset - host_offset;
466 54c5a2ae edgar_igl
        }
467 a03e2d42 bellard
        start = h2g(host_start);
468 a03e2d42 bellard
    } else {
469 a03e2d42 bellard
        if (start & ~TARGET_PAGE_MASK) {
470 e89f07d3 pbrook
            errno = EINVAL;
471 c8a706fe pbrook
            goto fail;
472 e89f07d3 pbrook
        }
473 a03e2d42 bellard
        end = start + len;
474 a03e2d42 bellard
        real_end = HOST_PAGE_ALIGN(end);
475 7ab240ad balrog
476 45bc1f52 aurel32
        /*
477 45bc1f52 aurel32
         * Test if requested memory area fits target address space
478 45bc1f52 aurel32
         * It can fail only on 64-bit host with 32-bit target.
479 45bc1f52 aurel32
         * On any other target/host host mmap() handles this error correctly.
480 45bc1f52 aurel32
         */
481 45bc1f52 aurel32
        if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
482 45bc1f52 aurel32
            errno = EINVAL;
483 45bc1f52 aurel32
            goto fail;
484 45bc1f52 aurel32
        }
485 45bc1f52 aurel32
486 a03e2d42 bellard
        /* worst case: we cannot map the file because the offset is not
487 a03e2d42 bellard
           aligned, so we read it */
488 a03e2d42 bellard
        if (!(flags & MAP_ANONYMOUS) &&
489 a03e2d42 bellard
            (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
490 a03e2d42 bellard
            /* msync() won't work here, so we return an error if write is
491 a03e2d42 bellard
               possible while it is a shared mapping */
492 a03e2d42 bellard
            if ((flags & MAP_TYPE) == MAP_SHARED &&
493 a03e2d42 bellard
                (prot & PROT_WRITE)) {
494 a03e2d42 bellard
                errno = EINVAL;
495 c8a706fe pbrook
                goto fail;
496 a03e2d42 bellard
            }
497 a03e2d42 bellard
            retaddr = target_mmap(start, len, prot | PROT_WRITE,
498 a03e2d42 bellard
                                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
499 a03e2d42 bellard
                                  -1, 0);
500 a03e2d42 bellard
            if (retaddr == -1)
501 c8a706fe pbrook
                goto fail;
502 fb7e378c Kirill A. Shutemov
            if (pread(fd, g2h(start), len, offset) == -1)
503 fb7e378c Kirill A. Shutemov
                goto fail;
504 a03e2d42 bellard
            if (!(prot & PROT_WRITE)) {
505 a03e2d42 bellard
                ret = target_mprotect(start, len, prot);
506 c8a706fe pbrook
                if (ret != 0) {
507 c8a706fe pbrook
                    start = ret;
508 c8a706fe pbrook
                    goto the_end;
509 c8a706fe pbrook
                }
510 a03e2d42 bellard
            }
511 a03e2d42 bellard
            goto the_end;
512 54936004 bellard
        }
513 a03e2d42 bellard
        
514 a03e2d42 bellard
        /* handle the start of the mapping */
515 a03e2d42 bellard
        if (start > real_start) {
516 a03e2d42 bellard
            if (real_end == real_start + qemu_host_page_size) {
517 a03e2d42 bellard
                /* one single host page */
518 a03e2d42 bellard
                ret = mmap_frag(real_start, start, end,
519 a03e2d42 bellard
                                prot, flags, fd, offset);
520 a03e2d42 bellard
                if (ret == -1)
521 c8a706fe pbrook
                    goto fail;
522 a03e2d42 bellard
                goto the_end1;
523 a03e2d42 bellard
            }
524 a03e2d42 bellard
            ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
525 54936004 bellard
                            prot, flags, fd, offset);
526 54936004 bellard
            if (ret == -1)
527 c8a706fe pbrook
                goto fail;
528 a03e2d42 bellard
            real_start += qemu_host_page_size;
529 a03e2d42 bellard
        }
530 a03e2d42 bellard
        /* handle the end of the mapping */
531 a03e2d42 bellard
        if (end < real_end) {
532 a03e2d42 bellard
            ret = mmap_frag(real_end - qemu_host_page_size,
533 a03e2d42 bellard
                            real_end - qemu_host_page_size, real_end,
534 a03e2d42 bellard
                            prot, flags, fd,
535 a03e2d42 bellard
                            offset + real_end - qemu_host_page_size - start);
536 a03e2d42 bellard
            if (ret == -1)
537 c8a706fe pbrook
                goto fail;
538 a03e2d42 bellard
            real_end -= qemu_host_page_size;
539 54936004 bellard
        }
540 3b46e624 ths
541 a03e2d42 bellard
        /* map the middle (easier) */
542 a03e2d42 bellard
        if (real_start < real_end) {
543 a03e2d42 bellard
            void *p;
544 a03e2d42 bellard
            unsigned long offset1;
545 a03e2d42 bellard
            if (flags & MAP_ANONYMOUS)
546 a03e2d42 bellard
                offset1 = 0;
547 a03e2d42 bellard
            else
548 a03e2d42 bellard
                offset1 = offset + real_start - start;
549 a03e2d42 bellard
            p = mmap(g2h(real_start), real_end - real_start,
550 a03e2d42 bellard
                     prot, flags, fd, offset1);
551 a03e2d42 bellard
            if (p == MAP_FAILED)
552 c8a706fe pbrook
                goto fail;
553 a03e2d42 bellard
        }
554 54936004 bellard
    }
555 54936004 bellard
 the_end1:
556 54936004 bellard
    page_set_flags(start, start + len, prot | PAGE_VALID);
557 54936004 bellard
 the_end:
558 54936004 bellard
#ifdef DEBUG_MMAP
559 0bf9e31a Blue Swirl
    printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
560 54936004 bellard
    page_dump(stdout);
561 54936004 bellard
    printf("\n");
562 54936004 bellard
#endif
563 c8a706fe pbrook
    mmap_unlock();
564 54936004 bellard
    return start;
565 c8a706fe pbrook
fail:
566 c8a706fe pbrook
    mmap_unlock();
567 c8a706fe pbrook
    return -1;
568 54936004 bellard
}
569 54936004 bellard
570 68a1c816 Paul Brook
static void mmap_reserve(abi_ulong start, abi_ulong size)
571 68a1c816 Paul Brook
{
572 68a1c816 Paul Brook
    abi_ulong real_start;
573 68a1c816 Paul Brook
    abi_ulong real_end;
574 68a1c816 Paul Brook
    abi_ulong addr;
575 68a1c816 Paul Brook
    abi_ulong end;
576 68a1c816 Paul Brook
    int prot;
577 68a1c816 Paul Brook
578 68a1c816 Paul Brook
    real_start = start & qemu_host_page_mask;
579 68a1c816 Paul Brook
    real_end = HOST_PAGE_ALIGN(start + size);
580 68a1c816 Paul Brook
    end = start + size;
581 68a1c816 Paul Brook
    if (start > real_start) {
582 68a1c816 Paul Brook
        /* handle host page containing start */
583 68a1c816 Paul Brook
        prot = 0;
584 68a1c816 Paul Brook
        for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
585 68a1c816 Paul Brook
            prot |= page_get_flags(addr);
586 68a1c816 Paul Brook
        }
587 68a1c816 Paul Brook
        if (real_end == real_start + qemu_host_page_size) {
588 68a1c816 Paul Brook
            for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
589 68a1c816 Paul Brook
                prot |= page_get_flags(addr);
590 68a1c816 Paul Brook
            }
591 68a1c816 Paul Brook
            end = real_end;
592 68a1c816 Paul Brook
        }
593 68a1c816 Paul Brook
        if (prot != 0)
594 68a1c816 Paul Brook
            real_start += qemu_host_page_size;
595 68a1c816 Paul Brook
    }
596 68a1c816 Paul Brook
    if (end < real_end) {
597 68a1c816 Paul Brook
        prot = 0;
598 68a1c816 Paul Brook
        for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
599 68a1c816 Paul Brook
            prot |= page_get_flags(addr);
600 68a1c816 Paul Brook
        }
601 68a1c816 Paul Brook
        if (prot != 0)
602 68a1c816 Paul Brook
            real_end -= qemu_host_page_size;
603 68a1c816 Paul Brook
    }
604 68a1c816 Paul Brook
    if (real_start != real_end) {
605 68a1c816 Paul Brook
        mmap(g2h(real_start), real_end - real_start, PROT_NONE,
606 68a1c816 Paul Brook
                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
607 68a1c816 Paul Brook
                 -1, 0);
608 68a1c816 Paul Brook
    }
609 68a1c816 Paul Brook
}
610 68a1c816 Paul Brook
611 992f48a0 blueswir1
int target_munmap(abi_ulong start, abi_ulong len)
612 54936004 bellard
{
613 992f48a0 blueswir1
    abi_ulong end, real_start, real_end, addr;
614 54936004 bellard
    int prot, ret;
615 54936004 bellard
616 54936004 bellard
#ifdef DEBUG_MMAP
617 0bf9e31a Blue Swirl
    printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
618 0bf9e31a Blue Swirl
           TARGET_ABI_FMT_lx "\n",
619 0bf9e31a Blue Swirl
           start, len);
620 54936004 bellard
#endif
621 54936004 bellard
    if (start & ~TARGET_PAGE_MASK)
622 54936004 bellard
        return -EINVAL;
623 54936004 bellard
    len = TARGET_PAGE_ALIGN(len);
624 54936004 bellard
    if (len == 0)
625 54936004 bellard
        return -EINVAL;
626 c8a706fe pbrook
    mmap_lock();
627 54936004 bellard
    end = start + len;
628 53a5960a pbrook
    real_start = start & qemu_host_page_mask;
629 53a5960a pbrook
    real_end = HOST_PAGE_ALIGN(end);
630 54936004 bellard
631 53a5960a pbrook
    if (start > real_start) {
632 54936004 bellard
        /* handle host page containing start */
633 54936004 bellard
        prot = 0;
634 53a5960a pbrook
        for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
635 54936004 bellard
            prot |= page_get_flags(addr);
636 54936004 bellard
        }
637 53a5960a pbrook
        if (real_end == real_start + qemu_host_page_size) {
638 53a5960a pbrook
            for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
639 d418c81e bellard
                prot |= page_get_flags(addr);
640 d418c81e bellard
            }
641 53a5960a pbrook
            end = real_end;
642 d418c81e bellard
        }
643 54936004 bellard
        if (prot != 0)
644 53a5960a pbrook
            real_start += qemu_host_page_size;
645 54936004 bellard
    }
646 53a5960a pbrook
    if (end < real_end) {
647 54936004 bellard
        prot = 0;
648 53a5960a pbrook
        for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
649 54936004 bellard
            prot |= page_get_flags(addr);
650 54936004 bellard
        }
651 54936004 bellard
        if (prot != 0)
652 53a5960a pbrook
            real_end -= qemu_host_page_size;
653 54936004 bellard
    }
654 3b46e624 ths
655 c8a706fe pbrook
    ret = 0;
656 54936004 bellard
    /* unmap what we can */
657 53a5960a pbrook
    if (real_start < real_end) {
658 18e9ea8a Aurelien Jarno
        if (RESERVED_VA) {
659 68a1c816 Paul Brook
            mmap_reserve(real_start, real_end - real_start);
660 68a1c816 Paul Brook
        } else {
661 68a1c816 Paul Brook
            ret = munmap(g2h(real_start), real_end - real_start);
662 68a1c816 Paul Brook
        }
663 54936004 bellard
    }
664 54936004 bellard
665 c8a706fe pbrook
    if (ret == 0)
666 c8a706fe pbrook
        page_set_flags(start, start + len, 0);
667 c8a706fe pbrook
    mmap_unlock();
668 c8a706fe pbrook
    return ret;
669 54936004 bellard
}
670 54936004 bellard
671 992f48a0 blueswir1
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
672 992f48a0 blueswir1
                       abi_ulong new_size, unsigned long flags,
673 992f48a0 blueswir1
                       abi_ulong new_addr)
674 54936004 bellard
{
675 54936004 bellard
    int prot;
676 f19412a2 aurel32
    void *host_addr;
677 54936004 bellard
678 c8a706fe pbrook
    mmap_lock();
679 f19412a2 aurel32
680 68a1c816 Paul Brook
    if (flags & MREMAP_FIXED) {
681 3af72a4d blueswir1
        host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
682 3af72a4d blueswir1
                                     old_size, new_size,
683 3af72a4d blueswir1
                                     flags,
684 68a1c816 Paul Brook
                                     g2h(new_addr));
685 68a1c816 Paul Brook
686 18e9ea8a Aurelien Jarno
        if (RESERVED_VA && host_addr != MAP_FAILED) {
687 68a1c816 Paul Brook
            /* If new and old addresses overlap then the above mremap will
688 68a1c816 Paul Brook
               already have failed with EINVAL.  */
689 68a1c816 Paul Brook
            mmap_reserve(old_addr, old_size);
690 68a1c816 Paul Brook
        }
691 68a1c816 Paul Brook
    } else if (flags & MREMAP_MAYMOVE) {
692 f19412a2 aurel32
        abi_ulong mmap_start;
693 f19412a2 aurel32
694 f19412a2 aurel32
        mmap_start = mmap_find_vma(0, new_size);
695 f19412a2 aurel32
696 f19412a2 aurel32
        if (mmap_start == -1) {
697 f19412a2 aurel32
            errno = ENOMEM;
698 f19412a2 aurel32
            host_addr = MAP_FAILED;
699 68a1c816 Paul Brook
        } else {
700 3af72a4d blueswir1
            host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
701 3af72a4d blueswir1
                                         old_size, new_size,
702 3af72a4d blueswir1
                                         flags | MREMAP_FIXED,
703 3af72a4d blueswir1
                                         g2h(mmap_start));
704 c65ffe6d amateur
            if ( RESERVED_VA ) {
705 c65ffe6d amateur
                mmap_reserve(old_addr, old_size);
706 c65ffe6d amateur
            }
707 68a1c816 Paul Brook
        }
708 3af72a4d blueswir1
    } else {
709 68a1c816 Paul Brook
        int prot = 0;
710 18e9ea8a Aurelien Jarno
        if (RESERVED_VA && old_size < new_size) {
711 68a1c816 Paul Brook
            abi_ulong addr;
712 68a1c816 Paul Brook
            for (addr = old_addr + old_size;
713 68a1c816 Paul Brook
                 addr < old_addr + new_size;
714 68a1c816 Paul Brook
                 addr++) {
715 68a1c816 Paul Brook
                prot |= page_get_flags(addr);
716 68a1c816 Paul Brook
            }
717 68a1c816 Paul Brook
        }
718 68a1c816 Paul Brook
        if (prot == 0) {
719 68a1c816 Paul Brook
            host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
720 18e9ea8a Aurelien Jarno
            if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) {
721 68a1c816 Paul Brook
                mmap_reserve(old_addr + old_size, new_size - old_size);
722 68a1c816 Paul Brook
            }
723 68a1c816 Paul Brook
        } else {
724 68a1c816 Paul Brook
            errno = ENOMEM;
725 68a1c816 Paul Brook
            host_addr = MAP_FAILED;
726 68a1c816 Paul Brook
        }
727 f19412a2 aurel32
        /* Check if address fits target address space */
728 f19412a2 aurel32
        if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
729 f19412a2 aurel32
            /* Revert mremap() changes */
730 f19412a2 aurel32
            host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
731 f19412a2 aurel32
            errno = ENOMEM;
732 f19412a2 aurel32
            host_addr = MAP_FAILED;
733 f19412a2 aurel32
        }
734 f19412a2 aurel32
    }
735 f19412a2 aurel32
736 f19412a2 aurel32
    if (host_addr == MAP_FAILED) {
737 c8a706fe pbrook
        new_addr = -1;
738 c8a706fe pbrook
    } else {
739 c8a706fe pbrook
        new_addr = h2g(host_addr);
740 c8a706fe pbrook
        prot = page_get_flags(old_addr);
741 c8a706fe pbrook
        page_set_flags(old_addr, old_addr + old_size, 0);
742 c8a706fe pbrook
        page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
743 c8a706fe pbrook
    }
744 c8a706fe pbrook
    mmap_unlock();
745 54936004 bellard
    return new_addr;
746 54936004 bellard
}
747 54936004 bellard
748 992f48a0 blueswir1
int target_msync(abi_ulong start, abi_ulong len, int flags)
749 54936004 bellard
{
750 992f48a0 blueswir1
    abi_ulong end;
751 54936004 bellard
752 54936004 bellard
    if (start & ~TARGET_PAGE_MASK)
753 54936004 bellard
        return -EINVAL;
754 54936004 bellard
    len = TARGET_PAGE_ALIGN(len);
755 54936004 bellard
    end = start + len;
756 d418c81e bellard
    if (end < start)
757 d418c81e bellard
        return -EINVAL;
758 d418c81e bellard
    if (end == start)
759 d418c81e bellard
        return 0;
760 3b46e624 ths
761 83fb7adf bellard
    start &= qemu_host_page_mask;
762 53a5960a pbrook
    return msync(g2h(start), end - start, flags);
763 54936004 bellard
}