blob: 3519147bce91219e84181b61725cce1126a0492f [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 */
Peter Maydelld39594e2016-01-26 18:17:02 +000019#include "qemu/osdep.h"
bellard54936002003-05-13 00:25:15 +000020#include <sys/mman.h>
blueswir13af72a42008-12-15 17:58:49 +000021#include <linux/mman.h>
22#include <linux/unistd.h>
bellard54936002003-05-13 00:25:15 +000023
24#include "qemu.h"
blueswir178f5bf12008-10-02 19:55:50 +000025#include "qemu-common.h"
Paolo Bonzini1652b972015-04-22 14:15:48 +020026#include "translate-all.h"
bellard54936002003-05-13 00:25:15 +000027
28//#define DEBUG_MMAP
29
Blue Swirl1e6eec82009-09-05 10:14:07 +000030static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
Juan Quinteladfd3f852009-09-23 01:19:03 +020031static __thread int mmap_lock_count;
pbrookc8a706f2008-06-02 16:16:42 +000032
33void mmap_lock(void)
34{
35 if (mmap_lock_count++ == 0) {
36 pthread_mutex_lock(&mmap_mutex);
37 }
38}
39
40void mmap_unlock(void)
41{
42 if (--mmap_lock_count == 0) {
43 pthread_mutex_unlock(&mmap_mutex);
44 }
45}
pbrookd5975362008-06-07 20:50:51 +000046
47/* Grab lock to make sure things are in a consistent state after fork(). */
48void mmap_fork_start(void)
49{
50 if (mmap_lock_count)
51 abort();
52 pthread_mutex_lock(&mmap_mutex);
53}
54
55void mmap_fork_end(int child)
56{
57 if (child)
58 pthread_mutex_init(&mmap_mutex, NULL);
59 else
60 pthread_mutex_unlock(&mmap_mutex);
61}
pbrookc8a706f2008-06-02 16:16:42 +000062
pbrook53a59602006-03-25 19:31:22 +000063/* NOTE: all the constants are the HOST ones, but addresses are target. */
blueswir1992f48a2007-10-14 16:27:31 +000064int target_mprotect(abi_ulong start, abi_ulong len, int prot)
bellard54936002003-05-13 00:25:15 +000065{
blueswir1992f48a2007-10-14 16:27:31 +000066 abi_ulong end, host_start, host_end, addr;
bellard54936002003-05-13 00:25:15 +000067 int prot1, ret;
68
69#ifdef DEBUG_MMAP
Blue Swirl0bf9e312009-07-20 17:19:25 +000070 printf("mprotect: start=0x" TARGET_ABI_FMT_lx
71 "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
bellard54936002003-05-13 00:25:15 +000072 prot & PROT_READ ? 'r' : '-',
73 prot & PROT_WRITE ? 'w' : '-',
74 prot & PROT_EXEC ? 'x' : '-');
75#endif
76
77 if ((start & ~TARGET_PAGE_MASK) != 0)
78 return -EINVAL;
79 len = TARGET_PAGE_ALIGN(len);
80 end = start + len;
81 if (end < start)
82 return -EINVAL;
balrog171cd1c2008-04-24 21:11:41 +000083 prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
bellard54936002003-05-13 00:25:15 +000084 if (len == 0)
85 return 0;
ths3b46e622007-09-17 08:09:54 +000086
pbrookc8a706f2008-06-02 16:16:42 +000087 mmap_lock();
bellard83fb7ad2004-07-05 21:25:26 +000088 host_start = start & qemu_host_page_mask;
bellard54936002003-05-13 00:25:15 +000089 host_end = HOST_PAGE_ALIGN(end);
90 if (start > host_start) {
91 /* handle host page containing start */
92 prot1 = prot;
93 for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
94 prot1 |= page_get_flags(addr);
95 }
bellard83fb7ad2004-07-05 21:25:26 +000096 if (host_end == host_start + qemu_host_page_size) {
bellardd418c812003-05-13 00:57:50 +000097 for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
98 prot1 |= page_get_flags(addr);
99 }
100 end = host_end;
101 }
pbrook53a59602006-03-25 19:31:22 +0000102 ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
bellard54936002003-05-13 00:25:15 +0000103 if (ret != 0)
pbrookc8a706f2008-06-02 16:16:42 +0000104 goto error;
bellard83fb7ad2004-07-05 21:25:26 +0000105 host_start += qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000106 }
107 if (end < host_end) {
bellard54936002003-05-13 00:25:15 +0000108 prot1 = prot;
109 for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
110 prot1 |= page_get_flags(addr);
111 }
ths5fafdf22007-09-16 21:08:06 +0000112 ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
bellard54936002003-05-13 00:25:15 +0000113 prot1 & PAGE_BITS);
114 if (ret != 0)
pbrookc8a706f2008-06-02 16:16:42 +0000115 goto error;
bellard83fb7ad2004-07-05 21:25:26 +0000116 host_end -= qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000117 }
ths3b46e622007-09-17 08:09:54 +0000118
bellard54936002003-05-13 00:25:15 +0000119 /* handle the pages in the middle */
120 if (host_start < host_end) {
pbrook53a59602006-03-25 19:31:22 +0000121 ret = mprotect(g2h(host_start), host_end - host_start, prot);
bellard54936002003-05-13 00:25:15 +0000122 if (ret != 0)
pbrookc8a706f2008-06-02 16:16:42 +0000123 goto error;
bellard54936002003-05-13 00:25:15 +0000124 }
bellard54936002003-05-13 00:25:15 +0000125 page_set_flags(start, start + len, prot | PAGE_VALID);
pbrookc8a706f2008-06-02 16:16:42 +0000126 mmap_unlock();
bellard54936002003-05-13 00:25:15 +0000127 return 0;
pbrookc8a706f2008-06-02 16:16:42 +0000128error:
129 mmap_unlock();
130 return ret;
bellard54936002003-05-13 00:25:15 +0000131}
132
133/* map an incomplete host page */
blueswir1992f48a2007-10-14 16:27:31 +0000134static int mmap_frag(abi_ulong real_start,
135 abi_ulong start, abi_ulong end,
136 int prot, int flags, int fd, abi_ulong offset)
bellard54936002003-05-13 00:25:15 +0000137{
ths80210bc2007-11-02 19:08:57 +0000138 abi_ulong real_end, addr;
pbrook53a59602006-03-25 19:31:22 +0000139 void *host_start;
bellard54936002003-05-13 00:25:15 +0000140 int prot1, prot_new;
141
pbrook53a59602006-03-25 19:31:22 +0000142 real_end = real_start + qemu_host_page_size;
143 host_start = g2h(real_start);
bellard54936002003-05-13 00:25:15 +0000144
145 /* get the protection of the target pages outside the mapping */
146 prot1 = 0;
pbrook53a59602006-03-25 19:31:22 +0000147 for(addr = real_start; addr < real_end; addr++) {
bellard54936002003-05-13 00:25:15 +0000148 if (addr < start || addr >= end)
149 prot1 |= page_get_flags(addr);
150 }
ths3b46e622007-09-17 08:09:54 +0000151
bellard54936002003-05-13 00:25:15 +0000152 if (prot1 == 0) {
153 /* no page was there, so we allocate one */
ths80210bc2007-11-02 19:08:57 +0000154 void *p = mmap(host_start, qemu_host_page_size, prot,
155 flags | MAP_ANONYMOUS, -1, 0);
156 if (p == MAP_FAILED)
157 return -1;
pbrook53a59602006-03-25 19:31:22 +0000158 prot1 = prot;
bellard54936002003-05-13 00:25:15 +0000159 }
160 prot1 &= PAGE_BITS;
161
162 prot_new = prot | prot1;
163 if (!(flags & MAP_ANONYMOUS)) {
164 /* msync() won't work here, so we return an error if write is
165 possible while it is a shared mapping */
166 if ((flags & MAP_TYPE) == MAP_SHARED &&
167 (prot & PROT_WRITE))
Juan Quintelaee636502010-01-20 00:56:24 +0100168 return -1;
bellard54936002003-05-13 00:25:15 +0000169
170 /* adjust protection to be able to read */
171 if (!(prot1 & PROT_WRITE))
pbrook53a59602006-03-25 19:31:22 +0000172 mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
ths3b46e622007-09-17 08:09:54 +0000173
bellard54936002003-05-13 00:25:15 +0000174 /* read the corresponding file data */
Kirill A. Shutemovfb7e3782010-01-20 00:56:20 +0100175 if (pread(fd, g2h(start), end - start, offset) == -1)
176 return -1;
ths3b46e622007-09-17 08:09:54 +0000177
bellard54936002003-05-13 00:25:15 +0000178 /* put final protection */
179 if (prot_new != (prot1 | PROT_WRITE))
pbrook53a59602006-03-25 19:31:22 +0000180 mprotect(host_start, qemu_host_page_size, prot_new);
bellard54936002003-05-13 00:25:15 +0000181 } else {
bellard54936002003-05-13 00:25:15 +0000182 if (prot_new != prot1) {
pbrook53a59602006-03-25 19:31:22 +0000183 mprotect(host_start, qemu_host_page_size, prot_new);
bellard54936002003-05-13 00:25:15 +0000184 }
Chen Gange6deac92015-12-30 09:10:54 +0800185 if (prot_new & PROT_WRITE) {
186 memset(g2h(start), 0, end - start);
187 }
bellard54936002003-05-13 00:25:15 +0000188 }
189 return 0;
190}
191
Richard Henderson14f24e12010-03-10 15:39:07 -0800192#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
193# define TASK_UNMAPPED_BASE (1ul << 38)
194#elif defined(__CYGWIN__)
bellarda03e2d42007-11-14 11:29:07 +0000195/* Cygwin doesn't have a whole lot of address space. */
Richard Henderson14f24e12010-03-10 15:39:07 -0800196# define TASK_UNMAPPED_BASE 0x18000000
bellarda03e2d42007-11-14 11:29:07 +0000197#else
Richard Henderson14f24e12010-03-10 15:39:07 -0800198# define TASK_UNMAPPED_BASE 0x40000000
bellarda03e2d42007-11-14 11:29:07 +0000199#endif
Peter Maydell59e9d912012-03-08 14:40:33 +0000200abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
bellarda03e2d42007-11-14 11:29:07 +0000201
pbrook07765902008-05-31 16:33:53 +0000202unsigned long last_brk;
203
Paul Brook68a1c812010-05-29 02:27:35 +0100204/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
205 of guest address space. */
206static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
207{
208 abi_ulong addr;
Peter Maydell59e9d912012-03-08 14:40:33 +0000209 abi_ulong end_addr;
Paul Brook68a1c812010-05-29 02:27:35 +0100210 int prot;
211 int looped = 0;
212
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200213 if (size > reserved_va) {
Paul Brook68a1c812010-05-29 02:27:35 +0100214 return (abi_ulong)-1;
215 }
216
Peter Maydell59e9d912012-03-08 14:40:33 +0000217 size = HOST_PAGE_ALIGN(size);
218 end_addr = start + size;
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200219 if (end_addr > reserved_va) {
220 end_addr = reserved_va;
Peter Maydell59e9d912012-03-08 14:40:33 +0000221 }
222 addr = end_addr - qemu_host_page_size;
223
224 while (1) {
225 if (addr > end_addr) {
Paul Brook68a1c812010-05-29 02:27:35 +0100226 if (looped) {
227 return (abi_ulong)-1;
228 }
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200229 end_addr = reserved_va;
Peter Maydell59e9d912012-03-08 14:40:33 +0000230 addr = end_addr - qemu_host_page_size;
Paul Brook68a1c812010-05-29 02:27:35 +0100231 looped = 1;
232 continue;
233 }
234 prot = page_get_flags(addr);
235 if (prot) {
Peter Maydell59e9d912012-03-08 14:40:33 +0000236 end_addr = addr;
Paul Brook68a1c812010-05-29 02:27:35 +0100237 }
Peter Maydell59e9d912012-03-08 14:40:33 +0000238 if (addr + size == end_addr) {
239 break;
240 }
241 addr -= qemu_host_page_size;
Paul Brook68a1c812010-05-29 02:27:35 +0100242 }
Peter Maydell59e9d912012-03-08 14:40:33 +0000243
244 if (start == mmap_next_start) {
245 mmap_next_start = addr;
246 }
247
248 return addr;
Paul Brook68a1c812010-05-29 02:27:35 +0100249}
250
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300251/*
252 * Find and reserve a free memory area of size 'size'. The search
253 * starts at 'start'.
254 * It must be called with mmap_lock() held.
255 * Return -1 if error.
256 */
Riku Voipio9ad197d2009-04-21 17:23:23 +0300257abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
bellarda03e2d42007-11-14 11:29:07 +0000258{
Richard Henderson14f24e12010-03-10 15:39:07 -0800259 void *ptr, *prev;
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300260 abi_ulong addr;
Richard Henderson14f24e12010-03-10 15:39:07 -0800261 int wrapped, repeat;
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300262
263 /* If 'start' == 0, then a default start address is used. */
Richard Henderson14f24e12010-03-10 15:39:07 -0800264 if (start == 0) {
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300265 start = mmap_next_start;
Richard Henderson14f24e12010-03-10 15:39:07 -0800266 } else {
267 start &= qemu_host_page_mask;
268 }
269
270 size = HOST_PAGE_ALIGN(size);
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300271
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200272 if (reserved_va) {
Paul Brook68a1c812010-05-29 02:27:35 +0100273 return mmap_find_vma_reserved(start, size);
274 }
275
bellarda03e2d42007-11-14 11:29:07 +0000276 addr = start;
Richard Henderson14f24e12010-03-10 15:39:07 -0800277 wrapped = repeat = 0;
278 prev = 0;
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300279
Richard Henderson14f24e12010-03-10 15:39:07 -0800280 for (;; prev = ptr) {
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300281 /*
282 * Reserve needed memory area to avoid a race.
283 * It should be discarded using:
284 * - mmap() with MAP_FIXED flag
285 * - mremap() with MREMAP_FIXED flag
286 * - shmat() with SHM_REMAP flag
287 */
Richard Henderson14f24e12010-03-10 15:39:07 -0800288 ptr = mmap(g2h(addr), size, PROT_NONE,
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300289 MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
290
291 /* ENOMEM, if host address space has no memory */
Richard Henderson14f24e12010-03-10 15:39:07 -0800292 if (ptr == MAP_FAILED) {
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300293 return (abi_ulong)-1;
Richard Henderson14f24e12010-03-10 15:39:07 -0800294 }
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300295
Richard Henderson14f24e12010-03-10 15:39:07 -0800296 /* Count the number of sequential returns of the same address.
297 This is used to modify the search algorithm below. */
298 repeat = (ptr == prev ? repeat + 1 : 0);
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300299
Richard Henderson14f24e12010-03-10 15:39:07 -0800300 if (h2g_valid(ptr + size - 1)) {
301 addr = h2g(ptr);
302
303 if ((addr & ~TARGET_PAGE_MASK) == 0) {
304 /* Success. */
305 if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
306 mmap_next_start = addr + size;
307 }
308 return addr;
309 }
310
311 /* The address is not properly aligned for the target. */
312 switch (repeat) {
313 case 0:
314 /* Assume the result that the kernel gave us is the
315 first with enough free space, so start again at the
316 next higher target page. */
317 addr = TARGET_PAGE_ALIGN(addr);
318 break;
319 case 1:
320 /* Sometimes the kernel decides to perform the allocation
321 at the top end of memory instead. */
322 addr &= TARGET_PAGE_MASK;
323 break;
324 case 2:
325 /* Start over at low memory. */
326 addr = 0;
327 break;
328 default:
329 /* Fail. This unaligned block must the last. */
330 addr = -1;
331 break;
332 }
333 } else {
334 /* Since the result the kernel gave didn't fit, start
335 again at low memory. If any repetition, fail. */
336 addr = (repeat ? -1 : 0);
337 }
338
339 /* Unmap and try again. */
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300340 munmap(ptr, size);
Kirill A. Shutemovfe3b4152009-08-13 21:03:58 +0300341
Richard Henderson14f24e12010-03-10 15:39:07 -0800342 /* ENOMEM if we checked the whole of the target address space. */
Blue Swirld0b3e4f2010-09-18 05:53:14 +0000343 if (addr == (abi_ulong)-1) {
bellarda03e2d42007-11-14 11:29:07 +0000344 return (abi_ulong)-1;
Richard Henderson14f24e12010-03-10 15:39:07 -0800345 } else if (addr == 0) {
346 if (wrapped) {
347 return (abi_ulong)-1;
348 }
349 wrapped = 1;
350 /* Don't actually use 0 when wrapping, instead indicate
Stefan Weil8186e782011-04-28 17:20:41 +0200351 that we'd truly like an allocation in low memory. */
Richard Henderson14f24e12010-03-10 15:39:07 -0800352 addr = (mmap_min_addr > TARGET_PAGE_SIZE
353 ? TARGET_PAGE_ALIGN(mmap_min_addr)
354 : TARGET_PAGE_SIZE);
355 } else if (wrapped && addr >= start) {
356 return (abi_ulong)-1;
357 }
bellarda03e2d42007-11-14 11:29:07 +0000358 }
bellarda03e2d42007-11-14 11:29:07 +0000359}
360
bellard54936002003-05-13 00:25:15 +0000361/* NOTE: all the constants are the HOST ones */
blueswir1992f48a2007-10-14 16:27:31 +0000362abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
363 int flags, int fd, abi_ulong offset)
bellard54936002003-05-13 00:25:15 +0000364{
blueswir1992f48a2007-10-14 16:27:31 +0000365 abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
bellard54936002003-05-13 00:25:15 +0000366
pbrookc8a706f2008-06-02 16:16:42 +0000367 mmap_lock();
bellard54936002003-05-13 00:25:15 +0000368#ifdef DEBUG_MMAP
369 {
Blue Swirl0bf9e312009-07-20 17:19:25 +0000370 printf("mmap: start=0x" TARGET_ABI_FMT_lx
371 " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
ths5fafdf22007-09-16 21:08:06 +0000372 start, len,
bellard54936002003-05-13 00:25:15 +0000373 prot & PROT_READ ? 'r' : '-',
374 prot & PROT_WRITE ? 'w' : '-',
375 prot & PROT_EXEC ? 'x' : '-');
376 if (flags & MAP_FIXED)
377 printf("MAP_FIXED ");
378 if (flags & MAP_ANONYMOUS)
379 printf("MAP_ANON ");
380 switch(flags & MAP_TYPE) {
381 case MAP_PRIVATE:
382 printf("MAP_PRIVATE ");
383 break;
384 case MAP_SHARED:
385 printf("MAP_SHARED ");
386 break;
387 default:
388 printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
389 break;
390 }
Blue Swirl0bf9e312009-07-20 17:19:25 +0000391 printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
bellard54936002003-05-13 00:25:15 +0000392 }
393#endif
394
pbrooke89f07d2006-02-04 20:46:24 +0000395 if (offset & ~TARGET_PAGE_MASK) {
396 errno = EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000397 goto fail;
pbrooke89f07d2006-02-04 20:46:24 +0000398 }
bellard54936002003-05-13 00:25:15 +0000399
400 len = TARGET_PAGE_ALIGN(len);
401 if (len == 0)
pbrookc8a706f2008-06-02 16:16:42 +0000402 goto the_end;
pbrook53a59602006-03-25 19:31:22 +0000403 real_start = start & qemu_host_page_mask;
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700404 host_offset = offset & qemu_host_page_mask;
405
406 /* If the user is asking for the kernel to find a location, do that
407 before we truncate the length for mapping files below. */
408 if (!(flags & MAP_FIXED)) {
409 host_len = len + offset - host_offset;
410 host_len = HOST_PAGE_ALIGN(host_len);
411 start = mmap_find_vma(real_start, host_len);
412 if (start == (abi_ulong)-1) {
413 errno = ENOMEM;
414 goto fail;
415 }
416 }
bellard54936002003-05-13 00:25:15 +0000417
edgar_igl54c5a2a2009-02-03 23:06:34 +0000418 /* When mapping files into a memory area larger than the file, accesses
419 to pages beyond the file size will cause a SIGBUS.
420
421 For example, if mmaping a file of 100 bytes on a host with 4K pages
422 emulating a target with 8K pages, the target expects to be able to
423 access the first 8K. But the host will trap us on any access beyond
424 4K.
425
426 When emulating a target with a larger page-size than the hosts, we
427 may need to truncate file maps at EOF and add extra anonymous pages
428 up to the targets page boundary. */
429
430 if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
431 && !(flags & MAP_ANONYMOUS)) {
432 struct stat sb;
433
434 if (fstat (fd, &sb) == -1)
435 goto fail;
436
437 /* Are we trying to create a map beyond EOF?. */
438 if (offset + len > sb.st_size) {
439 /* If so, truncate the file map at eof aligned with
440 the hosts real pagesize. Additional anonymous maps
441 will be created beyond EOF. */
Paolo Bonzini0c2d70c2015-12-02 13:00:54 +0100442 len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset);
edgar_igl54c5a2a2009-02-03 23:06:34 +0000443 }
444 }
445
bellard54936002003-05-13 00:25:15 +0000446 if (!(flags & MAP_FIXED)) {
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700447 unsigned long host_start;
bellarda03e2d42007-11-14 11:29:07 +0000448 void *p;
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700449
bellarda03e2d42007-11-14 11:29:07 +0000450 host_len = len + offset - host_offset;
451 host_len = HOST_PAGE_ALIGN(host_len);
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700452
bellarda03e2d42007-11-14 11:29:07 +0000453 /* Note: we prefer to control the mapping address. It is
454 especially important if qemu_host_page_size >
455 qemu_real_host_page_size */
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700456 p = mmap(g2h(start), host_len, prot,
457 flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
bellarda03e2d42007-11-14 11:29:07 +0000458 if (p == MAP_FAILED)
pbrookc8a706f2008-06-02 16:16:42 +0000459 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000460 /* update start so that it points to the file position at 'offset' */
461 host_start = (unsigned long)p;
edgar_igl54c5a2a2009-02-03 23:06:34 +0000462 if (!(flags & MAP_ANONYMOUS)) {
Richard Hendersona5e7ee42012-06-01 16:07:52 -0700463 p = mmap(g2h(start), len, prot,
edgar_igl54c5a2a2009-02-03 23:06:34 +0000464 flags | MAP_FIXED, fd, host_offset);
Jürg Billeter83842742013-06-29 11:41:32 +0200465 if (p == MAP_FAILED) {
466 munmap(g2h(start), host_len);
467 goto fail;
468 }
bellarda03e2d42007-11-14 11:29:07 +0000469 host_start += offset - host_offset;
edgar_igl54c5a2a2009-02-03 23:06:34 +0000470 }
bellarda03e2d42007-11-14 11:29:07 +0000471 start = h2g(host_start);
472 } else {
473 if (start & ~TARGET_PAGE_MASK) {
pbrooke89f07d2006-02-04 20:46:24 +0000474 errno = EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000475 goto fail;
pbrooke89f07d2006-02-04 20:46:24 +0000476 }
bellarda03e2d42007-11-14 11:29:07 +0000477 end = start + len;
478 real_end = HOST_PAGE_ALIGN(end);
balrog7ab240a2008-04-26 12:17:34 +0000479
aurel3245bc1f52008-12-08 18:12:33 +0000480 /*
481 * Test if requested memory area fits target address space
482 * It can fail only on 64-bit host with 32-bit target.
483 * On any other target/host host mmap() handles this error correctly.
484 */
485 if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
486 errno = EINVAL;
487 goto fail;
488 }
489
bellarda03e2d42007-11-14 11:29:07 +0000490 /* worst case: we cannot map the file because the offset is not
491 aligned, so we read it */
492 if (!(flags & MAP_ANONYMOUS) &&
493 (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
494 /* msync() won't work here, so we return an error if write is
495 possible while it is a shared mapping */
496 if ((flags & MAP_TYPE) == MAP_SHARED &&
497 (prot & PROT_WRITE)) {
498 errno = EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000499 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000500 }
501 retaddr = target_mmap(start, len, prot | PROT_WRITE,
502 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
503 -1, 0);
504 if (retaddr == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000505 goto fail;
Kirill A. Shutemovfb7e3782010-01-20 00:56:20 +0100506 if (pread(fd, g2h(start), len, offset) == -1)
507 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000508 if (!(prot & PROT_WRITE)) {
509 ret = target_mprotect(start, len, prot);
Paolo Bonzini86abac02015-09-14 12:31:44 +0200510 assert(ret == 0);
bellarda03e2d42007-11-14 11:29:07 +0000511 }
512 goto the_end;
bellard54936002003-05-13 00:25:15 +0000513 }
bellarda03e2d42007-11-14 11:29:07 +0000514
515 /* handle the start of the mapping */
516 if (start > real_start) {
517 if (real_end == real_start + qemu_host_page_size) {
518 /* one single host page */
519 ret = mmap_frag(real_start, start, end,
520 prot, flags, fd, offset);
521 if (ret == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000522 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000523 goto the_end1;
524 }
525 ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
bellard54936002003-05-13 00:25:15 +0000526 prot, flags, fd, offset);
527 if (ret == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000528 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000529 real_start += qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000530 }
bellarda03e2d42007-11-14 11:29:07 +0000531 /* handle the end of the mapping */
532 if (end < real_end) {
533 ret = mmap_frag(real_end - qemu_host_page_size,
Chen Gang530c0032015-12-24 09:07:33 +0800534 real_end - qemu_host_page_size, end,
bellarda03e2d42007-11-14 11:29:07 +0000535 prot, flags, fd,
536 offset + real_end - qemu_host_page_size - start);
537 if (ret == -1)
pbrookc8a706f2008-06-02 16:16:42 +0000538 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000539 real_end -= qemu_host_page_size;
540 }
ths3b46e622007-09-17 08:09:54 +0000541
bellarda03e2d42007-11-14 11:29:07 +0000542 /* map the middle (easier) */
543 if (real_start < real_end) {
544 void *p;
545 unsigned long offset1;
546 if (flags & MAP_ANONYMOUS)
547 offset1 = 0;
548 else
549 offset1 = offset + real_start - start;
550 p = mmap(g2h(real_start), real_end - real_start,
551 prot, flags, fd, offset1);
552 if (p == MAP_FAILED)
pbrookc8a706f2008-06-02 16:16:42 +0000553 goto fail;
bellarda03e2d42007-11-14 11:29:07 +0000554 }
bellard54936002003-05-13 00:25:15 +0000555 }
556 the_end1:
557 page_set_flags(start, start + len, prot | PAGE_VALID);
558 the_end:
559#ifdef DEBUG_MMAP
Blue Swirl0bf9e312009-07-20 17:19:25 +0000560 printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
bellard54936002003-05-13 00:25:15 +0000561 page_dump(stdout);
562 printf("\n");
563#endif
Paolo Bonzini35865332015-04-22 14:20:35 +0200564 tb_invalidate_phys_range(start, start + len);
pbrookc8a706f2008-06-02 16:16:42 +0000565 mmap_unlock();
bellard54936002003-05-13 00:25:15 +0000566 return start;
pbrookc8a706f2008-06-02 16:16:42 +0000567fail:
568 mmap_unlock();
569 return -1;
bellard54936002003-05-13 00:25:15 +0000570}
571
Paul Brook68a1c812010-05-29 02:27:35 +0100572static void mmap_reserve(abi_ulong start, abi_ulong size)
573{
574 abi_ulong real_start;
575 abi_ulong real_end;
576 abi_ulong addr;
577 abi_ulong end;
578 int prot;
579
580 real_start = start & qemu_host_page_mask;
581 real_end = HOST_PAGE_ALIGN(start + size);
582 end = start + size;
583 if (start > real_start) {
584 /* handle host page containing start */
585 prot = 0;
586 for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
587 prot |= page_get_flags(addr);
588 }
589 if (real_end == real_start + qemu_host_page_size) {
590 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
591 prot |= page_get_flags(addr);
592 }
593 end = real_end;
594 }
595 if (prot != 0)
596 real_start += qemu_host_page_size;
597 }
598 if (end < real_end) {
599 prot = 0;
600 for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
601 prot |= page_get_flags(addr);
602 }
603 if (prot != 0)
604 real_end -= qemu_host_page_size;
605 }
606 if (real_start != real_end) {
607 mmap(g2h(real_start), real_end - real_start, PROT_NONE,
608 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
609 -1, 0);
610 }
611}
612
blueswir1992f48a2007-10-14 16:27:31 +0000613int target_munmap(abi_ulong start, abi_ulong len)
bellard54936002003-05-13 00:25:15 +0000614{
blueswir1992f48a2007-10-14 16:27:31 +0000615 abi_ulong end, real_start, real_end, addr;
bellard54936002003-05-13 00:25:15 +0000616 int prot, ret;
617
618#ifdef DEBUG_MMAP
Blue Swirl0bf9e312009-07-20 17:19:25 +0000619 printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
620 TARGET_ABI_FMT_lx "\n",
621 start, len);
bellard54936002003-05-13 00:25:15 +0000622#endif
623 if (start & ~TARGET_PAGE_MASK)
624 return -EINVAL;
625 len = TARGET_PAGE_ALIGN(len);
626 if (len == 0)
627 return -EINVAL;
pbrookc8a706f2008-06-02 16:16:42 +0000628 mmap_lock();
bellard54936002003-05-13 00:25:15 +0000629 end = start + len;
pbrook53a59602006-03-25 19:31:22 +0000630 real_start = start & qemu_host_page_mask;
631 real_end = HOST_PAGE_ALIGN(end);
bellard54936002003-05-13 00:25:15 +0000632
pbrook53a59602006-03-25 19:31:22 +0000633 if (start > real_start) {
bellard54936002003-05-13 00:25:15 +0000634 /* handle host page containing start */
635 prot = 0;
pbrook53a59602006-03-25 19:31:22 +0000636 for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
bellard54936002003-05-13 00:25:15 +0000637 prot |= page_get_flags(addr);
638 }
pbrook53a59602006-03-25 19:31:22 +0000639 if (real_end == real_start + qemu_host_page_size) {
640 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
bellardd418c812003-05-13 00:57:50 +0000641 prot |= page_get_flags(addr);
642 }
pbrook53a59602006-03-25 19:31:22 +0000643 end = real_end;
bellardd418c812003-05-13 00:57:50 +0000644 }
bellard54936002003-05-13 00:25:15 +0000645 if (prot != 0)
pbrook53a59602006-03-25 19:31:22 +0000646 real_start += qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000647 }
pbrook53a59602006-03-25 19:31:22 +0000648 if (end < real_end) {
bellard54936002003-05-13 00:25:15 +0000649 prot = 0;
pbrook53a59602006-03-25 19:31:22 +0000650 for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
bellard54936002003-05-13 00:25:15 +0000651 prot |= page_get_flags(addr);
652 }
653 if (prot != 0)
pbrook53a59602006-03-25 19:31:22 +0000654 real_end -= qemu_host_page_size;
bellard54936002003-05-13 00:25:15 +0000655 }
ths3b46e622007-09-17 08:09:54 +0000656
pbrookc8a706f2008-06-02 16:16:42 +0000657 ret = 0;
bellard54936002003-05-13 00:25:15 +0000658 /* unmap what we can */
pbrook53a59602006-03-25 19:31:22 +0000659 if (real_start < real_end) {
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200660 if (reserved_va) {
Paul Brook68a1c812010-05-29 02:27:35 +0100661 mmap_reserve(real_start, real_end - real_start);
662 } else {
663 ret = munmap(g2h(real_start), real_end - real_start);
664 }
bellard54936002003-05-13 00:25:15 +0000665 }
666
Alexander Graf77a8f1a2012-05-10 22:40:10 +0000667 if (ret == 0) {
pbrookc8a706f2008-06-02 16:16:42 +0000668 page_set_flags(start, start + len, 0);
Paolo Bonzini35865332015-04-22 14:20:35 +0200669 tb_invalidate_phys_range(start, start + len);
Alexander Graf77a8f1a2012-05-10 22:40:10 +0000670 }
pbrookc8a706f2008-06-02 16:16:42 +0000671 mmap_unlock();
672 return ret;
bellard54936002003-05-13 00:25:15 +0000673}
674
blueswir1992f48a2007-10-14 16:27:31 +0000675abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
676 abi_ulong new_size, unsigned long flags,
677 abi_ulong new_addr)
bellard54936002003-05-13 00:25:15 +0000678{
679 int prot;
aurel32f19412a2008-12-08 18:12:40 +0000680 void *host_addr;
bellard54936002003-05-13 00:25:15 +0000681
pbrookc8a706f2008-06-02 16:16:42 +0000682 mmap_lock();
aurel32f19412a2008-12-08 18:12:40 +0000683
Paul Brook68a1c812010-05-29 02:27:35 +0100684 if (flags & MREMAP_FIXED) {
blueswir13af72a42008-12-15 17:58:49 +0000685 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
686 old_size, new_size,
687 flags,
Paul Brook68a1c812010-05-29 02:27:35 +0100688 g2h(new_addr));
689
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200690 if (reserved_va && host_addr != MAP_FAILED) {
Paul Brook68a1c812010-05-29 02:27:35 +0100691 /* If new and old addresses overlap then the above mremap will
692 already have failed with EINVAL. */
693 mmap_reserve(old_addr, old_size);
694 }
695 } else if (flags & MREMAP_MAYMOVE) {
aurel32f19412a2008-12-08 18:12:40 +0000696 abi_ulong mmap_start;
697
698 mmap_start = mmap_find_vma(0, new_size);
699
700 if (mmap_start == -1) {
701 errno = ENOMEM;
702 host_addr = MAP_FAILED;
Paul Brook68a1c812010-05-29 02:27:35 +0100703 } else {
blueswir13af72a42008-12-15 17:58:49 +0000704 host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
705 old_size, new_size,
706 flags | MREMAP_FIXED,
707 g2h(mmap_start));
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200708 if (reserved_va) {
amateurc65ffe62010-09-14 13:22:34 +0800709 mmap_reserve(old_addr, old_size);
710 }
Paul Brook68a1c812010-05-29 02:27:35 +0100711 }
blueswir13af72a42008-12-15 17:58:49 +0000712 } else {
Paul Brook68a1c812010-05-29 02:27:35 +0100713 int prot = 0;
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200714 if (reserved_va && old_size < new_size) {
Paul Brook68a1c812010-05-29 02:27:35 +0100715 abi_ulong addr;
716 for (addr = old_addr + old_size;
717 addr < old_addr + new_size;
718 addr++) {
719 prot |= page_get_flags(addr);
720 }
721 }
722 if (prot == 0) {
723 host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
Laurent Vivierb76f21a2015-08-24 14:53:54 +0200724 if (host_addr != MAP_FAILED && reserved_va && old_size > new_size) {
Paul Brook68a1c812010-05-29 02:27:35 +0100725 mmap_reserve(old_addr + old_size, new_size - old_size);
726 }
727 } else {
728 errno = ENOMEM;
729 host_addr = MAP_FAILED;
730 }
aurel32f19412a2008-12-08 18:12:40 +0000731 /* Check if address fits target address space */
732 if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
733 /* Revert mremap() changes */
734 host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
735 errno = ENOMEM;
736 host_addr = MAP_FAILED;
737 }
738 }
739
740 if (host_addr == MAP_FAILED) {
pbrookc8a706f2008-06-02 16:16:42 +0000741 new_addr = -1;
742 } else {
743 new_addr = h2g(host_addr);
744 prot = page_get_flags(old_addr);
745 page_set_flags(old_addr, old_addr + old_size, 0);
746 page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
747 }
Paolo Bonzini35865332015-04-22 14:20:35 +0200748 tb_invalidate_phys_range(new_addr, new_addr + new_size);
pbrookc8a706f2008-06-02 16:16:42 +0000749 mmap_unlock();
bellard54936002003-05-13 00:25:15 +0000750 return new_addr;
751}
752
blueswir1992f48a2007-10-14 16:27:31 +0000753int target_msync(abi_ulong start, abi_ulong len, int flags)
bellard54936002003-05-13 00:25:15 +0000754{
blueswir1992f48a2007-10-14 16:27:31 +0000755 abi_ulong end;
bellard54936002003-05-13 00:25:15 +0000756
757 if (start & ~TARGET_PAGE_MASK)
758 return -EINVAL;
759 len = TARGET_PAGE_ALIGN(len);
bellard54936002003-05-13 00:25:15 +0000760 end = start + len;
bellardd418c812003-05-13 00:57:50 +0000761 if (end < start)
762 return -EINVAL;
763 if (end == start)
764 return 0;
ths3b46e622007-09-17 08:09:54 +0000765
bellard83fb7ad2004-07-05 21:25:26 +0000766 start &= qemu_host_page_mask;
pbrook53a59602006-03-25 19:31:22 +0000767 return msync(g2h(start), end - start, flags);
bellard54936002003-05-13 00:25:15 +0000768}