blob: a249f0ceb6ac190475858fc522edfb92222cacb0 [file] [log] [blame]
bellard54936002003-05-13 00:25:15 +00001/*
2 * mmap support for qemu
ths5fafdf22007-09-16 21:08:06 +00003 *
bellard54936002003-05-13 00:25:15 +00004 * 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
Blue Swirl8167ee82009-07-16 20:47:01 +000017 * along with this program; if not, see <http://www.gnu.org/licenses/>.
bellard54936002003-05-13 00:25:15 +000018 */
19#include <stdlib.h>
20#include <stdio.h>
21#include <stdarg.h>
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
edgar_igl54c5a2a2009-02-03 23:06:34 +000025#include <sys/types.h>
26#include <sys/stat.h>
bellard54936002003-05-13 00:25:15 +000027#include <sys/mman.h>
blueswir13af72a42008-12-15 17:58:49 +000028#include <linux/mman.h>
29#include <linux/unistd.h>
bellard54936002003-05-13 00:25:15 +000030
31#include "qemu.h"
blueswir178f5bf12008-10-02 19:55:50 +000032#include "qemu-common.h"
bellard54936002003-05-13 00:25:15 +000033
34//#define DEBUG_MMAP
35
Blue Swirl1e6eec82009-09-05 10:14:07 +000036static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
Juan Quinteladfd3f852009-09-23 01:19:03 +020037static __thread int mmap_lock_count;
pbrookc8a706f2008-06-02 16:16:42 +000038
39void mmap_lock(void)
40{
41 if (mmap_lock_count++ == 0) {
42 pthread_mutex_lock(&mmap_mutex);
43 }
44}
45
46void mmap_unlock(void)
47{
48 if (--mmap_lock_count == 0) {
49 pthread_mutex_unlock(&mmap_mutex);
50 }
51}
pbrookd5975362008-06-07 20:50:51 +000052
53/* Grab lock to make sure things are in a consistent state after fork(). */
54void mmap_fork_start(void)
55{
56 if (mmap_lock_count)
57 abort();
58 pthread_mutex_lock(&mmap_mutex);
59}
60
61void mmap_fork_end(int child)
62{
63 if (child)
64 pthread_mutex_init(&mmap_mutex, NULL);
65 else
66 pthread_mutex_unlock(&mmap_mutex);
67}
pbrookc8a706f2008-06-02 16:16:42 +000068
pbrook53a59602006-03-25 19:31:22 +000069/* NOTE: all the constants are the HOST ones, but addresses are target. */
blueswir1992f48a2007-10-14 16:27:31 +000070int target_mprotect(abi_ulong start, abi_ulong len, int prot)
bellard54936002003-05-13 00:25:15 +000071{
blueswir1992f48a2007-10-14 16:27:31 +000072 abi_ulong end, host_start, host_end, addr;
bellard54936002003-05-13 00:25:15 +000073 int prot1, ret;
74
75#ifdef DEBUG_MMAP
Blue Swirl0bf9e312009-07-20 17:19:25 +000076 printf("mprotect: start=0x" TARGET_ABI_FMT_lx
77 "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
bellard54936002003-05-13 00:25:15 +000078 prot & PROT_READ ? 'r' : '-',
79 prot & PROT_WRITE ? 'w' : '-',
80 prot & PROT_EXEC ? 'x' : '-');
81#endif
82
83 if ((start & ~TARGET_PAGE_MASK) != 0)
84 return -EINVAL;
85 len = TARGET_PAGE_ALIGN(len);
86 end = start + len;
87 if (end < start)
88 return -EINVAL;
balrog171cd1c2008-04-24 21:11:41 +000089 prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
bellard54936002003-05-13 00:25:15 +000090 if (len == 0)
91 return 0;
ths3b46e622007-09-17 08:09:54 +000092
pbrookc8a706f2008-06-02 16:16:42 +000093 mmap_lock();
bellard83fb7ad2004-07-05 21:25:26 +000094 host_start = start & qemu_host_page_mask;
bellard54936002003-05-13 00:25:15 +000095 host_end = HOST_PAGE_ALIGN(end);
96 if (start > host_start) {
97 /* handle host page containing start */
98 prot1 = prot;
99 for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
100 prot1 |= page_get_flags(addr);
101 }
bellard83fb7ad2004-07-05 21:25:26 +0000102 if (host_end == host_start + qemu_host_page_size) {
bellardd418c812003-05-13 00:57:50 +0000103 for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
104 prot1 |= page_get_flags(addr);
105 }
106 end = host_end;
107 }
pbrook53a59602006-03-25 19:31:22 +0000108 ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
bellard54936002003-05-13 00:25:15 +0000109 if (ret != 0)
pbrookc8a706f2008-06-02 16:16:42 +0000110 goto error;
bellard83fb7ad2004-07-05 21:25:26 +0000111 host_start += qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000112 }
113 if (end < host_end) {
bellard54936002003-05-13 00:25:15 +0000114 prot1 = prot;
115 for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
116 prot1 |= page_get_flags(addr);
117 }
ths5fafdf22007-09-16 21:08:06 +0000118 ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
bellard54936002003-05-13 00:25:15 +0000119 prot1 & PAGE_BITS);
120 if (ret != 0)
pbrookc8a706f2008-06-02 16:16:42 +0000121 goto error;
bellard83fb7ad2004-07-05 21:25:26 +0000122 host_end -= qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000123 }
ths3b46e622007-09-17 08:09:54 +0000124
bellard54936002003-05-13 00:25:15 +0000125 /* handle the pages in the middle */
126 if (host_start < host_end) {
pbrook53a59602006-03-25 19:31:22 +0000127 ret = mprotect(g2h(host_start), host_end - host_start, prot);
bellard54936002003-05-13 00:25:15 +0000128 if (ret != 0)
pbrookc8a706f2008-06-02 16:16:42 +0000129 goto error;
bellard54936002003-05-13 00:25:15 +0000130 }
bellard54936002003-05-13 00:25:15 +0000131 page_set_flags(start, start + len, prot | PAGE_VALID);
pbrookc8a706f2008-06-02 16:16:42 +0000132 mmap_unlock();
bellard54936002003-05-13 00:25:15 +0000133 return 0;
pbrookc8a706f2008-06-02 16:16:42 +0000134error:
135 mmap_unlock();
136 return ret;
bellard54936002003-05-13 00:25:15 +0000137}
138
139/* map an incomplete host page */
blueswir1992f48a2007-10-14 16:27:31 +0000140static int mmap_frag(abi_ulong real_start,
141 abi_ulong start, abi_ulong end,
142 int prot, int flags, int fd, abi_ulong offset)
bellard54936002003-05-13 00:25:15 +0000143{
ths80210bc2007-11-02 19:08:57 +0000144 abi_ulong real_end, addr;
pbrook53a59602006-03-25 19:31:22 +0000145 void *host_start;
bellard54936002003-05-13 00:25:15 +0000146 int prot1, prot_new;
147
pbrook53a59602006-03-25 19:31:22 +0000148 real_end = real_start + qemu_host_page_size;
149 host_start = g2h(real_start);
bellard54936002003-05-13 00:25:15 +0000150
151 /* get the protection of the target pages outside the mapping */
152 prot1 = 0;
pbrook53a59602006-03-25 19:31:22 +0000153 for(addr = real_start; addr < real_end; addr++) {
bellard54936002003-05-13 00:25:15 +0000154 if (addr < start || addr >= end)
155 prot1 |= page_get_flags(addr);
156 }
ths3b46e622007-09-17 08:09:54 +0000157
bellard54936002003-05-13 00:25:15 +0000158 if (prot1 == 0) {
159 /* no page was there, so we allocate one */
ths80210bc2007-11-02 19:08:57 +0000160 void *p = mmap(host_start, qemu_host_page_size, prot,
161 flags | MAP_ANONYMOUS, -1, 0);
162 if (p == MAP_FAILED)
163 return -1;
pbrook53a59602006-03-25 19:31:22 +0000164 prot1 = prot;
bellard54936002003-05-13 00:25:15 +0000165 }
166 prot1 &= PAGE_BITS;
167
168 prot_new = prot | prot1;
169 if (!(flags & MAP_ANONYMOUS)) {
170 /* msync() won't work here, so we return an error if write is
171 possible while it is a shared mapping */
172 if ((flags & MAP_TYPE) == MAP_SHARED &&
173 (prot & PROT_WRITE))
Juan Quintelaee636502010-01-20 00:56:24 +0100174 return -1;
bellard54936002003-05-13 00:25:15 +0000175
176 /* adjust protection to be able to read */
177 if (!(prot1 & PROT_WRITE))
pbrook53a59602006-03-25 19:31:22 +0000178 mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
ths3b46e622007-09-17 08:09:54 +0000179
bellard54936002003-05-13 00:25:15 +0000180 /* read the corresponding file data */
Kirill A. Shutemovfb7e3782010-01-20 00:56:20 +0100181 if (pread(fd, g2h(start), end - start, offset) == -1)
182 return -1;
ths3b46e622007-09-17 08:09:54 +0000183
bellard54936002003-05-13 00:25:15 +0000184 /* put final protection */
185 if (prot_new != (prot1 | PROT_WRITE))
pbrook53a59602006-03-25 19:31:22 +0000186 mprotect(host_start, qemu_host_page_size, prot_new);
bellard54936002003-05-13 00:25:15 +0000187 } else {
188 /* just update the protection */
189 if (prot_new != prot1) {
pbrook53a59602006-03-25 19:31:22 +0000190 mprotect(host_start, qemu_host_page_size, prot_new);
bellard54936002003-05-13 00:25:15 +0000191 }
192 }
193 return 0;
194}
195
Richard Henderson14f24e12010-03-10 15:39:07 -0800196#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
197# define TASK_UNMAPPED_BASE (1ul << 38)
198#elif defined(__CYGWIN__)
bellarda03e2d42007-11-14 11:29:07 +0000199/* Cygwin doesn't have a whole lot of address space. */
Richard Henderson14f24e12010-03-10 15:39:07 -0800200# define TASK_UNMAPPED_BASE 0x18000000
bellarda03e2d42007-11-14 11:29:07 +0000201#else
Richard Henderson14f24e12010-03-10 15:39:07 -0800202# define TASK_UNMAPPED_BASE 0x40000000
bellarda03e2d42007-11-14 11:29:07 +0000203#endif
Peter Maydell59e9d912012-03-08 14:40:33 +0000204abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
bellarda03e2d42007-11-14 11:29:07 +0000205
pbrook07765902008-05-31 16:33:53 +0000206unsigned long last_brk;
207
Peter Maydell4e655712011-02-10 16:53:04 +0000208#ifdef CONFIG_USE_GUEST_BASE
Paul Brook68a1c812010-05-29 02:27:35 +0100209/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
210 of guest address space. */
211static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
212{
213 abi_ulong addr;
Peter Maydell59e9d912012-03-08 14:40:33 +0000214 abi_ulong end_addr;
Paul Brook68a1c812010-05-29 02:27:35 +0100215 int prot;
216 int looped = 0;
217
Aurelien Jarno18e9ea82010-07-30 21:09:10 +0200218 if (size > RESERVED_VA) {
Paul Brook68a1c812010-05-29 02:27:35 +0100219 return (abi_ulong)-1;
220 }
221
Peter Maydell59e9d912012-03-08 14:40:33 +0000222 size = HOST_PAGE_ALIGN(size);
223 end_addr = start + size;
224 if (end_addr > RESERVED_VA) {
225 end_addr = RESERVED_VA;
226 }
227 addr = end_addr - qemu_host_page_size;
228
229 while (1) {
230 if (addr > end_addr) {
Paul Brook68a1c812010-05-29 02:27:35 +0100231 if (looped) {
232 return (abi_ulong)-1;
233 }
Peter Maydell59e9d912012-03-08 14:40:33 +0000234 end_addr = RESERVED_VA;
235 addr = end_addr - qemu_host_page_size;
Paul Brook68a1c812010-05-29 02:27:35 +0100236 looped = 1;
237 continue;
238 }
239 prot = page_get_flags(addr);
240 if (prot) {
Peter Maydell59e9d912012-03-08 14:40:33 +0000241 end_addr = addr;
Paul Brook68a1c812010-05-29 02:27:35 +0100242 }
Peter Maydell59e9d912012-03-08 14:40:33 +0000243 if (addr + size == end_addr) {
244 break;
245 }
246 addr -= qemu_host_page_size;
Paul Brook68a1c812010-05-29 02:27:35 +0100247 }
Peter Maydell59e9d912012-03-08 14:40:33 +0000248
249 if (start == mmap_next_start) {
250 mmap_next_start = addr;
251 }
252
253 return addr;
Paul Brook68a1c812010-05-29 02:27:35 +0100254}
Peter Maydell4e655712011-02-10 16:53:04 +0000255#endif
Paul Brook68a1c812010-05-29 02:27:35 +0100256
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300257/*
258 * Find and reserve a free memory area of size 'size'. The search
259 * starts at 'start'.
260 * It must be called with mmap_lock() held.
261 * Return -1 if error.
262 */
Riku Voipio9ad197d2009-04-21 17:23:23 +0300263abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
bellarda03e2d42007-11-14 11:29:07 +0000264{
Richard Henderson14f24e12010-03-10 15:39:07 -0800265 void *ptr, *prev;
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300266 abi_ulong addr;
Richard Henderson14f24e12010-03-10 15:39:07 -0800267 int wrapped, repeat;
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300268
269 /* If 'start' == 0, then a default start address is used. */
Richard Henderson14f24e12010-03-10 15:39:07 -0800270 if (start == 0) {
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300271 start = mmap_next_start;
Richard Henderson14f24e12010-03-10 15:39:07 -0800272 } else {
273 start &= qemu_host_page_mask;
274 }
275
276 size = HOST_PAGE_ALIGN(size);
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300277
Peter Maydell4e655712011-02-10 16:53:04 +0000278#ifdef CONFIG_USE_GUEST_BASE
Aurelien Jarno18e9ea82010-07-30 21:09:10 +0200279 if (RESERVED_VA) {
Paul Brook68a1c812010-05-29 02:27:35 +0100280 return mmap_find_vma_reserved(start, size);
281 }
Peter Maydell4e655712011-02-10 16:53:04 +0000282#endif
Paul Brook68a1c812010-05-29 02:27:35 +0100283
bellarda03e2d42007-11-14 11:29:07 +0000284 addr = start;
Richard Henderson14f24e12010-03-10 15:39:07 -0800285 wrapped = repeat = 0;
286 prev = 0;
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300287
Richard Henderson14f24e12010-03-10 15:39:07 -0800288 for (;; prev = ptr) {
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300289 /*
290 * Reserve needed memory area to avoid a race.
291 * It should be discarded using:
292 * - mmap() with MAP_FIXED flag
293 * - mremap() with MREMAP_FIXED flag
294 * - shmat() with SHM_REMAP flag
295 */
Richard Henderson14f24e12010-03-10 15:39:07 -0800296 ptr = mmap(g2h(addr), size, PROT_NONE,
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300297 MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
298
299 /* ENOMEM, if host address space has no memory */
Richard Henderson14f24e12010-03-10 15:39:07 -0800300 if (ptr == MAP_FAILED) {
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300301 return (abi_ulong)-1;
Richard Henderson14f24e12010-03-10 15:39:07 -0800302 }
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300303
Richard Henderson14f24e12010-03-10 15:39:07 -0800304 /* Count the number of sequential returns of the same address.
305 This is used to modify the search algorithm below. */
306 repeat = (ptr == prev ? repeat + 1 : 0);
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300307
Richard Henderson14f24e12010-03-10 15:39:07 -0800308 if (h2g_valid(ptr + size - 1)) {
309 addr = h2g(ptr);
310
311 if ((addr & ~TARGET_PAGE_MASK) == 0) {
312 /* Success. */
313 if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
314 mmap_next_start = addr + size;
315 }
316 return addr;
317 }
318
319 /* The address is not properly aligned for the target. */
320 switch (repeat) {
321 case 0:
322 /* Assume the result that the kernel gave us is the
323 first with enough free space, so start again at the
324 next higher target page. */
325 addr = TARGET_PAGE_ALIGN(addr);
326 break;
327 case 1:
328 /* Sometimes the kernel decides to perform the allocation
329 at the top end of memory instead. */
330 addr &= TARGET_PAGE_MASK;
331 break;
332 case 2:
333 /* Start over at low memory. */
334 addr = 0;
335 break;
336 default:
337 /* Fail. This unaligned block must the last. */
338 addr = -1;
339 break;
340 }
341 } else {
342 /* Since the result the kernel gave didn't fit, start
343 again at low memory. If any repetition, fail. */
344 addr = (repeat ? -1 : 0);
345 }
346
347 /* Unmap and try again. */
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300348 munmap(ptr, size);
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300349
Richard Henderson14f24e12010-03-10 15:39:07 -0800350 /* ENOMEM if we checked the whole of the target address space. */
Blue Swirld0b3e4f2010-09-18 05:53:14 +0000351 if (addr == (abi_ulong)-1) {
bellarda03e2d42007-11-14 11:29:07 +0000352 return (abi_ulong)-1;
Richard Henderson14f24e12010-03-10 15:39:07 -0800353 } else if (addr == 0) {
354 if (wrapped) {
355 return (abi_ulong)-1;
356 }
357 wrapped = 1;
358 /* Don't actually use 0 when wrapping, instead indicate
Stefan Weil8186e782011-04-28 17:20:41 +0200359 that we'd truly like an allocation in low memory. */
Richard Henderson14f24e12010-03-10 15:39:07 -0800360 addr = (mmap_min_addr > TARGET_PAGE_SIZE
361 ? TARGET_PAGE_ALIGN(mmap_min_addr)
362 : TARGET_PAGE_SIZE);
363 } else if (wrapped && addr >= start) {
364 return (abi_ulong)-1;
365 }
bellarda03e2d42007-11-14 11:29:07 +0000366 }
bellarda03e2d42007-11-14 11:29:07 +0000367}
368
bellard54936002003-05-13 00:25:15 +0000369/* NOTE: all the constants are the HOST ones */
blueswir1992f48a2007-10-14 16:27:31 +0000370abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
371 int flags, int fd, abi_ulong offset)
bellard54936002003-05-13 00:25:15 +0000372{
blueswir1992f48a2007-10-14 16:27:31 +0000373 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
bellard54936002003-05-13 00:25:15 +0000374
pbrookc8a706f2008-06-02 16:16:42 +0000375 mmap_lock();
bellard54936002003-05-13 00:25:15 +0000376#ifdef DEBUG_MMAP
377 {
Blue Swirl0bf9e312009-07-20 17:19:25 +0000378 printf("mmap: start=0x" TARGET_ABI_FMT_lx
379 " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
ths5fafdf22007-09-16 21:08:06 +0000380 start, len,
bellard54936002003-05-13 00:25:15 +0000381 prot & PROT_READ ? 'r' : '-',
382 prot & PROT_WRITE ? 'w' : '-',
383 prot & PROT_EXEC ? 'x' : '-');
384 if (flags & MAP_FIXED)
385 printf("MAP_FIXED ");
386 if (flags & MAP_ANONYMOUS)
387 printf("MAP_ANON ");
388 switch(flags & MAP_TYPE) {
389 case MAP_PRIVATE:
390 printf("MAP_PRIVATE ");
391 break;
392 case MAP_SHARED:
393 printf("MAP_SHARED ");
394 break;
395 default:
396 printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
397 break;
398 }
Blue Swirl0bf9e312009-07-20 17:19:25 +0000399 printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
bellard54936002003-05-13 00:25:15 +0000400 }
401#endif
402
pbrooke89f07d2006-02-04 20:46:24 +0000403 if (offset & ~TARGET_PAGE_MASK) {
404 errno = EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000405 goto fail;
pbrooke89f07d2006-02-04 20:46:24 +0000406 }
bellard54936002003-05-13 00:25:15 +0000407
408 len = TARGET_PAGE_ALIGN(len);
409 if (len == 0)
pbrookc8a706f2008-06-02 16:16:42 +0000410 goto the_end;
pbrook53a59602006-03-25 19:31:22 +0000411 real_start = start & qemu_host_page_mask;
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700412 host_offset = offset & qemu_host_page_mask;
413
414 /* If the user is asking for the kernel to find a location, do that
415 before we truncate the length for mapping files below. */
416 if (!(flags & MAP_FIXED)) {
417 host_len = len + offset - host_offset;
418 host_len = HOST_PAGE_ALIGN(host_len);
419 start = mmap_find_vma(real_start, host_len);
420 if (start == (abi_ulong)-1) {
421 errno = ENOMEM;
422 goto fail;
423 }
424 }
bellard54936002003-05-13 00:25:15 +0000425
edgar_igl54c5a2a2009-02-03 23:06:34 +0000426 /* When mapping files into a memory area larger than the file, accesses
427 to pages beyond the file size will cause a SIGBUS.
428
429 For example, if mmaping a file of 100 bytes on a host with 4K pages
430 emulating a target with 8K pages, the target expects to be able to
431 access the first 8K. But the host will trap us on any access beyond
432 4K.
433
434 When emulating a target with a larger page-size than the hosts, we
435 may need to truncate file maps at EOF and add extra anonymous pages
436 up to the targets page boundary. */
437
438 if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
439 && !(flags & MAP_ANONYMOUS)) {
440 struct stat sb;
441
442 if (fstat (fd, &sb) == -1)
443 goto fail;
444
445 /* Are we trying to create a map beyond EOF?. */
446 if (offset + len > sb.st_size) {
447 /* If so, truncate the file map at eof aligned with
448 the hosts real pagesize. Additional anonymous maps
449 will be created beyond EOF. */
450 len = (sb.st_size - offset);
451 len += qemu_real_host_page_size - 1;
452 len &= ~(qemu_real_host_page_size - 1);
453 }
454 }
455
bellard54936002003-05-13 00:25:15 +0000456 if (!(flags & MAP_FIXED)) {
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700457 unsigned long host_start;
bellarda03e2d42007-11-14 11:29:07 +0000458 void *p;
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700459
bellarda03e2d42007-11-14 11:29:07 +0000460 host_len = len + offset - host_offset;
461 host_len = HOST_PAGE_ALIGN(host_len);
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700462
bellarda03e2d42007-11-14 11:29:07 +0000463 /* Note: we prefer to control the mapping address. It is
464 especially important if qemu_host_page_size >
465 qemu_real_host_page_size */
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700466 p = mmap(g2h(start), host_len, prot,
467 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
bellarda03e2d42007-11-14 11:29:07 +0000468 if (p == MAP_FAILED)
pbrookc8a706f2008-06-02 16:16:42 +0000469 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000470 /* update start so that it points to the file position at 'offset' */
471 host_start = (unsigned long)p;
edgar_igl54c5a2a2009-02-03 23:06:34 +0000472 if (!(flags & MAP_ANONYMOUS)) {
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700473 p = mmap(g2h(start), len, prot,
edgar_igl54c5a2a2009-02-03 23:06:34 +0000474 flags | MAP_FIXED, fd, host_offset);
Jürg Billeter83842742013-06-29 11:41:32 +0200475 if (p == MAP_FAILED) {
476 munmap(g2h(start), host_len);
477 goto fail;
478 }
bellarda03e2d42007-11-14 11:29:07 +0000479 host_start += offset - host_offset;
edgar_igl54c5a2a2009-02-03 23:06:34 +0000480 }
bellarda03e2d42007-11-14 11:29:07 +0000481 start = h2g(host_start);
482 } else {
483 if (start & ~TARGET_PAGE_MASK) {
pbrooke89f07d2006-02-04 20:46:24 +0000484 errno = EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000485 goto fail;
pbrooke89f07d2006-02-04 20:46:24 +0000486 }
bellarda03e2d42007-11-14 11:29:07 +0000487 end = start + len;
488 real_end = HOST_PAGE_ALIGN(end);
balrog7ab240a2008-04-26 12:17:34 +0000489
aurel3245bc1f52008-12-08 18:12:33 +0000490 /*
491 * Test if requested memory area fits target address space
492 * It can fail only on 64-bit host with 32-bit target.
493 * On any other target/host host mmap() handles this error correctly.
494 */
495 if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
496 errno = EINVAL;
497 goto fail;
498 }
499
bellarda03e2d42007-11-14 11:29:07 +0000500 /* worst case: we cannot map the file because the offset is not
501 aligned, so we read it */
502 if (!(flags & MAP_ANONYMOUS) &&
503 (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
504 /* msync() won't work here, so we return an error if write is
505 possible while it is a shared mapping */
506 if ((flags & MAP_TYPE) == MAP_SHARED &&
507 (prot & PROT_WRITE)) {
508 errno = EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000509 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000510 }
511 retaddr = target_mmap(start, len, prot | PROT_WRITE,
512 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
513 -1, 0);
514 if (retaddr == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000515 goto fail;
Kirill A. Shutemovfb7e3782010-01-20 00:56:20 +0100516 if (pread(fd, g2h(start), len, offset) == -1)
517 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000518 if (!(prot & PROT_WRITE)) {
519 ret = target_mprotect(start, len, prot);
pbrookc8a706f2008-06-02 16:16:42 +0000520 if (ret != 0) {
521 start = ret;
522 goto the_end;
523 }
bellarda03e2d42007-11-14 11:29:07 +0000524 }
525 goto the_end;
bellard54936002003-05-13 00:25:15 +0000526 }
bellarda03e2d42007-11-14 11:29:07 +0000527
528 /* handle the start of the mapping */
529 if (start > real_start) {
530 if (real_end == real_start + qemu_host_page_size) {
531 /* one single host page */
532 ret = mmap_frag(real_start, start, end,
533 prot, flags, fd, offset);
534 if (ret == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000535 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000536 goto the_end1;
537 }
538 ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
bellard54936002003-05-13 00:25:15 +0000539 prot, flags, fd, offset);
540 if (ret == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000541 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000542 real_start += qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000543 }
bellarda03e2d42007-11-14 11:29:07 +0000544 /* handle the end of the mapping */
545 if (end < real_end) {
546 ret = mmap_frag(real_end - qemu_host_page_size,
547 real_end - qemu_host_page_size, real_end,
548 prot, flags, fd,
549 offset + real_end - qemu_host_page_size - start);
550 if (ret == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000551 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000552 real_end -= qemu_host_page_size;
553 }
ths3b46e622007-09-17 08:09:54 +0000554
bellarda03e2d42007-11-14 11:29:07 +0000555 /* map the middle (easier) */
556 if (real_start < real_end) {
557 void *p;
558 unsigned long offset1;
559 if (flags & MAP_ANONYMOUS)
560 offset1 = 0;
561 else
562 offset1 = offset + real_start - start;
563 p = mmap(g2h(real_start), real_end - real_start,
564 prot, flags, fd, offset1);
565 if (p == MAP_FAILED)
pbrookc8a706f2008-06-02 16:16:42 +0000566 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000567 }
bellard54936002003-05-13 00:25:15 +0000568 }
569 the_end1:
570 page_set_flags(start, start + len, prot | PAGE_VALID);
571 the_end:
572#ifdef DEBUG_MMAP
Blue Swirl0bf9e312009-07-20 17:19:25 +0000573 printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
bellard54936002003-05-13 00:25:15 +0000574 page_dump(stdout);
575 printf("\n");
576#endif
Alexander Graf77a8f1a2012-05-10 22:40:10 +0000577 tb_invalidate_phys_range(start, start + len, 0);
pbrookc8a706f2008-06-02 16:16:42 +0000578 mmap_unlock();
bellard54936002003-05-13 00:25:15 +0000579 return start;
pbrookc8a706f2008-06-02 16:16:42 +0000580fail:
581 mmap_unlock();
582 return -1;
bellard54936002003-05-13 00:25:15 +0000583}
584
Paul Brook68a1c812010-05-29 02:27:35 +0100585static void mmap_reserve(abi_ulong start, abi_ulong size)
586{
587 abi_ulong real_start;
588 abi_ulong real_end;
589 abi_ulong addr;
590 abi_ulong end;
591 int prot;
592
593 real_start = start & qemu_host_page_mask;
594 real_end = HOST_PAGE_ALIGN(start + size);
595 end = start + size;
596 if (start > real_start) {
597 /* handle host page containing start */
598 prot = 0;
599 for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
600 prot |= page_get_flags(addr);
601 }
602 if (real_end == real_start + qemu_host_page_size) {
603 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
604 prot |= page_get_flags(addr);
605 }
606 end = real_end;
607 }
608 if (prot != 0)
609 real_start += qemu_host_page_size;
610 }
611 if (end < real_end) {
612 prot = 0;
613 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
614 prot |= page_get_flags(addr);
615 }
616 if (prot != 0)
617 real_end -= qemu_host_page_size;
618 }
619 if (real_start != real_end) {
620 mmap(g2h(real_start), real_end - real_start, PROT_NONE,
621 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
622 -1, 0);
623 }
624}
625
blueswir1992f48a2007-10-14 16:27:31 +0000626int target_munmap(abi_ulong start, abi_ulong len)
bellard54936002003-05-13 00:25:15 +0000627{
blueswir1992f48a2007-10-14 16:27:31 +0000628 abi_ulong end, real_start, real_end, addr;
bellard54936002003-05-13 00:25:15 +0000629 int prot, ret;
630
631#ifdef DEBUG_MMAP
Blue Swirl0bf9e312009-07-20 17:19:25 +0000632 printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
633 TARGET_ABI_FMT_lx "\n",
634 start, len);
bellard54936002003-05-13 00:25:15 +0000635#endif
636 if (start & ~TARGET_PAGE_MASK)
637 return -EINVAL;
638 len = TARGET_PAGE_ALIGN(len);
639 if (len == 0)
640 return -EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000641 mmap_lock();
bellard54936002003-05-13 00:25:15 +0000642 end = start + len;
pbrook53a59602006-03-25 19:31:22 +0000643 real_start = start & qemu_host_page_mask;
644 real_end = HOST_PAGE_ALIGN(end);
bellard54936002003-05-13 00:25:15 +0000645
pbrook53a59602006-03-25 19:31:22 +0000646 if (start > real_start) {
bellard54936002003-05-13 00:25:15 +0000647 /* handle host page containing start */
648 prot = 0;
pbrook53a59602006-03-25 19:31:22 +0000649 for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
bellard54936002003-05-13 00:25:15 +0000650 prot |= page_get_flags(addr);
651 }
pbrook53a59602006-03-25 19:31:22 +0000652 if (real_end == real_start + qemu_host_page_size) {
653 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
bellardd418c812003-05-13 00:57:50 +0000654 prot |= page_get_flags(addr);
655 }
pbrook53a59602006-03-25 19:31:22 +0000656 end = real_end;
bellardd418c812003-05-13 00:57:50 +0000657 }
bellard54936002003-05-13 00:25:15 +0000658 if (prot != 0)
pbrook53a59602006-03-25 19:31:22 +0000659 real_start += qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000660 }
pbrook53a59602006-03-25 19:31:22 +0000661 if (end < real_end) {
bellard54936002003-05-13 00:25:15 +0000662 prot = 0;
pbrook53a59602006-03-25 19:31:22 +0000663 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
bellard54936002003-05-13 00:25:15 +0000664 prot |= page_get_flags(addr);
665 }
666 if (prot != 0)
pbrook53a59602006-03-25 19:31:22 +0000667 real_end -= qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000668 }
ths3b46e622007-09-17 08:09:54 +0000669
pbrookc8a706f2008-06-02 16:16:42 +0000670 ret = 0;
bellard54936002003-05-13 00:25:15 +0000671 /* unmap what we can */
pbrook53a59602006-03-25 19:31:22 +0000672 if (real_start < real_end) {
Aurelien Jarno18e9ea82010-07-30 21:09:10 +0200673 if (RESERVED_VA) {
Paul Brook68a1c812010-05-29 02:27:35 +0100674 mmap_reserve(real_start, real_end - real_start);
675 } else {
676 ret = munmap(g2h(real_start), real_end - real_start);
677 }
bellard54936002003-05-13 00:25:15 +0000678 }
679
Alexander Graf77a8f1a2012-05-10 22:40:10 +0000680 if (ret == 0) {
pbrookc8a706f2008-06-02 16:16:42 +0000681 page_set_flags(start, start + len, 0);
Alexander Graf77a8f1a2012-05-10 22:40:10 +0000682 tb_invalidate_phys_range(start, start + len, 0);
683 }
pbrookc8a706f2008-06-02 16:16:42 +0000684 mmap_unlock();
685 return ret;
bellard54936002003-05-13 00:25:15 +0000686}
687
blueswir1992f48a2007-10-14 16:27:31 +0000688abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
689 abi_ulong new_size, unsigned long flags,
690 abi_ulong new_addr)
bellard54936002003-05-13 00:25:15 +0000691{
692 int prot;
aurel32f19412a2008-12-08 18:12:40 +0000693 void *host_addr;
bellard54936002003-05-13 00:25:15 +0000694
pbrookc8a706f2008-06-02 16:16:42 +0000695 mmap_lock();
aurel32f19412a2008-12-08 18:12:40 +0000696
Paul Brook68a1c812010-05-29 02:27:35 +0100697 if (flags & MREMAP_FIXED) {
blueswir13af72a42008-12-15 17:58:49 +0000698 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
699 old_size, new_size,
700 flags,
Paul Brook68a1c812010-05-29 02:27:35 +0100701 g2h(new_addr));
702
Aurelien Jarno18e9ea82010-07-30 21:09:10 +0200703 if (RESERVED_VA && host_addr != MAP_FAILED) {
Paul Brook68a1c812010-05-29 02:27:35 +0100704 /* If new and old addresses overlap then the above mremap will
705 already have failed with EINVAL. */
706 mmap_reserve(old_addr, old_size);
707 }
708 } else if (flags & MREMAP_MAYMOVE) {
aurel32f19412a2008-12-08 18:12:40 +0000709 abi_ulong mmap_start;
710
711 mmap_start = mmap_find_vma(0, new_size);
712
713 if (mmap_start == -1) {
714 errno = ENOMEM;
715 host_addr = MAP_FAILED;
Paul Brook68a1c812010-05-29 02:27:35 +0100716 } else {
blueswir13af72a42008-12-15 17:58:49 +0000717 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
718 old_size, new_size,
719 flags | MREMAP_FIXED,
720 g2h(mmap_start));
amateurc65ffe62010-09-14 13:22:34 +0800721 if ( RESERVED_VA ) {
722 mmap_reserve(old_addr, old_size);
723 }
Paul Brook68a1c812010-05-29 02:27:35 +0100724 }
blueswir13af72a42008-12-15 17:58:49 +0000725 } else {
Paul Brook68a1c812010-05-29 02:27:35 +0100726 int prot = 0;
Aurelien Jarno18e9ea82010-07-30 21:09:10 +0200727 if (RESERVED_VA && old_size < new_size) {
Paul Brook68a1c812010-05-29 02:27:35 +0100728 abi_ulong addr;
729 for (addr = old_addr + old_size;
730 addr < old_addr + new_size;
731 addr++) {
732 prot |= page_get_flags(addr);
733 }
734 }
735 if (prot == 0) {
736 host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
Aurelien Jarno18e9ea82010-07-30 21:09:10 +0200737 if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) {
Paul Brook68a1c812010-05-29 02:27:35 +0100738 mmap_reserve(old_addr + old_size, new_size - old_size);
739 }
740 } else {
741 errno = ENOMEM;
742 host_addr = MAP_FAILED;
743 }
aurel32f19412a2008-12-08 18:12:40 +0000744 /* Check if address fits target address space */
745 if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
746 /* Revert mremap() changes */
747 host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
748 errno = ENOMEM;
749 host_addr = MAP_FAILED;
750 }
751 }
752
753 if (host_addr == MAP_FAILED) {
pbrookc8a706f2008-06-02 16:16:42 +0000754 new_addr = -1;
755 } else {
756 new_addr = h2g(host_addr);
757 prot = page_get_flags(old_addr);
758 page_set_flags(old_addr, old_addr + old_size, 0);
759 page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
760 }
Alexander Graf77a8f1a2012-05-10 22:40:10 +0000761 tb_invalidate_phys_range(new_addr, new_addr + new_size, 0);
pbrookc8a706f2008-06-02 16:16:42 +0000762 mmap_unlock();
bellard54936002003-05-13 00:25:15 +0000763 return new_addr;
764}
765
blueswir1992f48a2007-10-14 16:27:31 +0000766int target_msync(abi_ulong start, abi_ulong len, int flags)
bellard54936002003-05-13 00:25:15 +0000767{
blueswir1992f48a2007-10-14 16:27:31 +0000768 abi_ulong end;
bellard54936002003-05-13 00:25:15 +0000769
770 if (start & ~TARGET_PAGE_MASK)
771 return -EINVAL;
772 len = TARGET_PAGE_ALIGN(len);
bellard54936002003-05-13 00:25:15 +0000773 end = start + len;
bellardd418c812003-05-13 00:57:50 +0000774 if (end < start)
775 return -EINVAL;
776 if (end == start)
777 return 0;
ths3b46e622007-09-17 08:09:54 +0000778
bellard83fb7ad2004-07-05 21:25:26 +0000779 start &= qemu_host_page_mask;
pbrook53a59602006-03-25 19:31:22 +0000780 return msync(g2h(start), end - start, flags);
bellard54936002003-05-13 00:25:15 +0000781}