|
|
1.1 root 1: /*
2: * mmap support for qemu
1.1.1.4 ! root 3: *
1.1 root 4: * 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
17: * along with this program; if not, write to the Free Software
18: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19: */
20: #include <stdlib.h>
21: #include <stdio.h>
22: #include <stdarg.h>
23: #include <string.h>
24: #include <unistd.h>
25: #include <errno.h>
26: #include <sys/mman.h>
27:
28: #include "qemu.h"
29:
30: //#define DEBUG_MMAP
31:
1.1.1.2 root 32: /* NOTE: all the constants are the HOST ones, but addresses are target. */
1.1.1.4 ! root 33: int target_mprotect(abi_ulong start, abi_ulong len, int prot)
1.1 root 34: {
1.1.1.4 ! root 35: abi_ulong end, host_start, host_end, addr;
1.1 root 36: int prot1, ret;
37:
38: #ifdef DEBUG_MMAP
1.1.1.4 ! root 39: printf("mprotect: start=0x" TARGET_FMT_lx
! 40: "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
1.1 root 41: prot & PROT_READ ? 'r' : '-',
42: prot & PROT_WRITE ? 'w' : '-',
43: prot & PROT_EXEC ? 'x' : '-');
44: #endif
45:
46: if ((start & ~TARGET_PAGE_MASK) != 0)
47: return -EINVAL;
48: len = TARGET_PAGE_ALIGN(len);
49: end = start + len;
50: if (end < start)
51: return -EINVAL;
52: if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
53: return -EINVAL;
54: if (len == 0)
55: return 0;
1.1.1.4 ! root 56:
1.1 root 57: host_start = start & qemu_host_page_mask;
58: host_end = HOST_PAGE_ALIGN(end);
59: if (start > host_start) {
60: /* handle host page containing start */
61: prot1 = prot;
62: for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
63: prot1 |= page_get_flags(addr);
64: }
65: if (host_end == host_start + qemu_host_page_size) {
66: for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
67: prot1 |= page_get_flags(addr);
68: }
69: end = host_end;
70: }
1.1.1.2 root 71: ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
1.1 root 72: if (ret != 0)
73: return ret;
74: host_start += qemu_host_page_size;
75: }
76: if (end < host_end) {
77: prot1 = prot;
78: for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
79: prot1 |= page_get_flags(addr);
80: }
1.1.1.4 ! root 81: ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
1.1 root 82: prot1 & PAGE_BITS);
83: if (ret != 0)
84: return ret;
85: host_end -= qemu_host_page_size;
86: }
1.1.1.4 ! root 87:
1.1 root 88: /* handle the pages in the middle */
89: if (host_start < host_end) {
1.1.1.2 root 90: ret = mprotect(g2h(host_start), host_end - host_start, prot);
1.1 root 91: if (ret != 0)
92: return ret;
93: }
94: page_set_flags(start, start + len, prot | PAGE_VALID);
95: return 0;
96: }
97:
98: /* map an incomplete host page */
1.1.1.4 ! root 99: static int mmap_frag(abi_ulong real_start,
! 100: abi_ulong start, abi_ulong end,
! 101: int prot, int flags, int fd, abi_ulong offset)
1.1 root 102: {
1.1.1.4 ! root 103: abi_ulong real_end, addr;
1.1.1.2 root 104: void *host_start;
1.1 root 105: int prot1, prot_new;
106:
1.1.1.2 root 107: real_end = real_start + qemu_host_page_size;
108: host_start = g2h(real_start);
1.1 root 109:
110: /* get the protection of the target pages outside the mapping */
111: prot1 = 0;
1.1.1.2 root 112: for(addr = real_start; addr < real_end; addr++) {
1.1 root 113: if (addr < start || addr >= end)
114: prot1 |= page_get_flags(addr);
115: }
1.1.1.4 ! root 116:
1.1 root 117: if (prot1 == 0) {
118: /* no page was there, so we allocate one */
1.1.1.4 ! root 119: void *p = mmap(host_start, qemu_host_page_size, prot,
! 120: flags | MAP_ANONYMOUS, -1, 0);
! 121: if (p == MAP_FAILED)
! 122: return -1;
1.1.1.2 root 123: prot1 = prot;
1.1 root 124: }
125: prot1 &= PAGE_BITS;
126:
127: prot_new = prot | prot1;
128: if (!(flags & MAP_ANONYMOUS)) {
129: /* msync() won't work here, so we return an error if write is
130: possible while it is a shared mapping */
131: if ((flags & MAP_TYPE) == MAP_SHARED &&
132: (prot & PROT_WRITE))
133: return -EINVAL;
134:
135: /* adjust protection to be able to read */
136: if (!(prot1 & PROT_WRITE))
1.1.1.2 root 137: mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
1.1.1.4 ! root 138:
1.1 root 139: /* read the corresponding file data */
1.1.1.2 root 140: pread(fd, g2h(start), end - start, offset);
1.1.1.4 ! root 141:
1.1 root 142: /* put final protection */
143: if (prot_new != (prot1 | PROT_WRITE))
1.1.1.2 root 144: mprotect(host_start, qemu_host_page_size, prot_new);
1.1 root 145: } else {
146: /* just update the protection */
147: if (prot_new != prot1) {
1.1.1.2 root 148: mprotect(host_start, qemu_host_page_size, prot_new);
1.1 root 149: }
150: }
151: return 0;
152: }
153:
1.1.1.4 ! root 154: #if defined(__CYGWIN__)
! 155: /* Cygwin doesn't have a whole lot of address space. */
! 156: static abi_ulong mmap_next_start = 0x18000000;
! 157: #else
! 158: static abi_ulong mmap_next_start = 0x40000000;
! 159: #endif
! 160:
! 161: /* find a free memory area of size 'size'. The search starts at
! 162: 'start'. If 'start' == 0, then a default start address is used.
! 163: Return -1 if error.
! 164: */
! 165: /* page_init() marks pages used by the host as reserved to be sure not
! 166: to use them. */
! 167: static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
! 168: {
! 169: abi_ulong addr, addr1, addr_start;
! 170: int prot;
! 171:
! 172: size = HOST_PAGE_ALIGN(size);
! 173: start = start & qemu_host_page_mask;
! 174: addr = start;
! 175: if (addr == 0)
! 176: addr = mmap_next_start;
! 177: addr_start = addr;
! 178: for(;;) {
! 179: prot = 0;
! 180: for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
! 181: prot |= page_get_flags(addr1);
! 182: }
! 183: if (prot == 0)
! 184: break;
! 185: addr += qemu_host_page_size;
! 186: /* we found nothing */
! 187: if (addr == addr_start)
! 188: return (abi_ulong)-1;
! 189: }
! 190: if (start == 0)
! 191: mmap_next_start = addr + size;
! 192: return addr;
! 193: }
! 194:
1.1 root 195: /* NOTE: all the constants are the HOST ones */
1.1.1.4 ! root 196: abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
! 197: int flags, int fd, abi_ulong offset)
1.1 root 198: {
1.1.1.4 ! root 199: abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
! 200: unsigned long host_start;
1.1 root 201:
202: #ifdef DEBUG_MMAP
203: {
1.1.1.4 ! root 204: printf("mmap: start=0x" TARGET_FMT_lx
! 205: " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
! 206: start, len,
1.1 root 207: prot & PROT_READ ? 'r' : '-',
208: prot & PROT_WRITE ? 'w' : '-',
209: prot & PROT_EXEC ? 'x' : '-');
210: if (flags & MAP_FIXED)
211: printf("MAP_FIXED ");
212: if (flags & MAP_ANONYMOUS)
213: printf("MAP_ANON ");
214: switch(flags & MAP_TYPE) {
215: case MAP_PRIVATE:
216: printf("MAP_PRIVATE ");
217: break;
218: case MAP_SHARED:
219: printf("MAP_SHARED ");
220: break;
221: default:
222: printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
223: break;
224: }
1.1.1.4 ! root 225: printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
1.1 root 226: }
227: #endif
228:
1.1.1.2 root 229: if (offset & ~TARGET_PAGE_MASK) {
230: errno = EINVAL;
231: return -1;
232: }
1.1 root 233:
234: len = TARGET_PAGE_ALIGN(len);
235: if (len == 0)
236: return start;
1.1.1.2 root 237: real_start = start & qemu_host_page_mask;
1.1 root 238:
239: if (!(flags & MAP_FIXED)) {
1.1.1.4 ! root 240: abi_ulong mmap_start;
! 241: void *p;
! 242: host_offset = offset & qemu_host_page_mask;
! 243: host_len = len + offset - host_offset;
! 244: host_len = HOST_PAGE_ALIGN(host_len);
! 245: mmap_start = mmap_find_vma(real_start, host_len);
! 246: if (mmap_start == (abi_ulong)-1) {
! 247: errno = ENOMEM;
! 248: return -1;
1.1 root 249: }
1.1.1.4 ! root 250: /* Note: we prefer to control the mapping address. It is
! 251: especially important if qemu_host_page_size >
! 252: qemu_real_host_page_size */
! 253: p = mmap(g2h(mmap_start),
! 254: host_len, prot, flags | MAP_FIXED, fd, host_offset);
! 255: if (p == MAP_FAILED)
! 256: return -1;
! 257: /* update start so that it points to the file position at 'offset' */
! 258: host_start = (unsigned long)p;
! 259: if (!(flags & MAP_ANONYMOUS))
! 260: host_start += offset - host_offset;
! 261: start = h2g(host_start);
! 262: } else {
! 263: if (start & ~TARGET_PAGE_MASK) {
1.1.1.2 root 264: errno = EINVAL;
265: return -1;
266: }
1.1.1.4 ! root 267: end = start + len;
! 268: real_end = HOST_PAGE_ALIGN(end);
! 269:
! 270: /* worst case: we cannot map the file because the offset is not
! 271: aligned, so we read it */
! 272: if (!(flags & MAP_ANONYMOUS) &&
! 273: (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
! 274: /* msync() won't work here, so we return an error if write is
! 275: possible while it is a shared mapping */
! 276: if ((flags & MAP_TYPE) == MAP_SHARED &&
! 277: (prot & PROT_WRITE)) {
! 278: errno = EINVAL;
! 279: return -1;
! 280: }
! 281: retaddr = target_mmap(start, len, prot | PROT_WRITE,
! 282: MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
! 283: -1, 0);
! 284: if (retaddr == -1)
! 285: return -1;
! 286: pread(fd, g2h(start), len, offset);
! 287: if (!(prot & PROT_WRITE)) {
! 288: ret = target_mprotect(start, len, prot);
! 289: if (ret != 0)
! 290: return ret;
! 291: }
! 292: goto the_end;
1.1 root 293: }
1.1.1.4 ! root 294:
! 295: /* handle the start of the mapping */
! 296: if (start > real_start) {
! 297: if (real_end == real_start + qemu_host_page_size) {
! 298: /* one single host page */
! 299: ret = mmap_frag(real_start, start, end,
! 300: prot, flags, fd, offset);
! 301: if (ret == -1)
! 302: return ret;
! 303: goto the_end1;
! 304: }
! 305: ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
1.1 root 306: prot, flags, fd, offset);
307: if (ret == -1)
308: return ret;
1.1.1.4 ! root 309: real_start += qemu_host_page_size;
! 310: }
! 311: /* handle the end of the mapping */
! 312: if (end < real_end) {
! 313: ret = mmap_frag(real_end - qemu_host_page_size,
! 314: real_end - qemu_host_page_size, real_end,
! 315: prot, flags, fd,
! 316: offset + real_end - qemu_host_page_size - start);
! 317: if (ret == -1)
! 318: return -1;
! 319: real_end -= qemu_host_page_size;
! 320: }
! 321:
! 322: /* map the middle (easier) */
! 323: if (real_start < real_end) {
! 324: void *p;
! 325: unsigned long offset1;
! 326: if (flags & MAP_ANONYMOUS)
! 327: offset1 = 0;
! 328: else
! 329: offset1 = offset + real_start - start;
! 330: p = mmap(g2h(real_start), real_end - real_start,
! 331: prot, flags, fd, offset1);
! 332: if (p == MAP_FAILED)
! 333: return -1;
1.1 root 334: }
335: }
336: the_end1:
337: page_set_flags(start, start + len, prot | PAGE_VALID);
338: the_end:
339: #ifdef DEBUG_MMAP
1.1.1.4 ! root 340: printf("ret=0x%llx\n", start);
1.1 root 341: page_dump(stdout);
342: printf("\n");
343: #endif
344: return start;
345: }
346:
1.1.1.4 ! root 347: int target_munmap(abi_ulong start, abi_ulong len)
1.1 root 348: {
1.1.1.4 ! root 349: abi_ulong end, real_start, real_end, addr;
1.1 root 350: int prot, ret;
351:
352: #ifdef DEBUG_MMAP
353: printf("munmap: start=0x%lx len=0x%lx\n", start, len);
354: #endif
355: if (start & ~TARGET_PAGE_MASK)
356: return -EINVAL;
357: len = TARGET_PAGE_ALIGN(len);
358: if (len == 0)
359: return -EINVAL;
360: end = start + len;
1.1.1.2 root 361: real_start = start & qemu_host_page_mask;
362: real_end = HOST_PAGE_ALIGN(end);
1.1 root 363:
1.1.1.2 root 364: if (start > real_start) {
1.1 root 365: /* handle host page containing start */
366: prot = 0;
1.1.1.2 root 367: for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
1.1 root 368: prot |= page_get_flags(addr);
369: }
1.1.1.2 root 370: if (real_end == real_start + qemu_host_page_size) {
371: for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
1.1 root 372: prot |= page_get_flags(addr);
373: }
1.1.1.2 root 374: end = real_end;
1.1 root 375: }
376: if (prot != 0)
1.1.1.2 root 377: real_start += qemu_host_page_size;
1.1 root 378: }
1.1.1.2 root 379: if (end < real_end) {
1.1 root 380: prot = 0;
1.1.1.2 root 381: for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
1.1 root 382: prot |= page_get_flags(addr);
383: }
384: if (prot != 0)
1.1.1.2 root 385: real_end -= qemu_host_page_size;
1.1 root 386: }
1.1.1.4 ! root 387:
1.1 root 388: /* unmap what we can */
1.1.1.2 root 389: if (real_start < real_end) {
1.1.1.4 ! root 390: ret = munmap(g2h(real_start), real_end - real_start);
1.1 root 391: if (ret != 0)
392: return ret;
393: }
394:
395: page_set_flags(start, start + len, 0);
396: return 0;
397: }
398:
399: /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
400: blocks which have been allocated starting on a host page */
1.1.1.4 ! root 401: abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
! 402: abi_ulong new_size, unsigned long flags,
! 403: abi_ulong new_addr)
1.1 root 404: {
405: int prot;
1.1.1.4 ! root 406: unsigned long host_addr;
1.1 root 407:
408: /* XXX: use 5 args syscall */
1.1.1.4 ! root 409: host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags);
! 410: if (host_addr == -1)
! 411: return -1;
! 412: new_addr = h2g(host_addr);
1.1 root 413: prot = page_get_flags(old_addr);
414: page_set_flags(old_addr, old_addr + old_size, 0);
415: page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
416: return new_addr;
417: }
418:
1.1.1.4 ! root 419: int target_msync(abi_ulong start, abi_ulong len, int flags)
1.1 root 420: {
1.1.1.4 ! root 421: abi_ulong end;
1.1 root 422:
423: if (start & ~TARGET_PAGE_MASK)
424: return -EINVAL;
425: len = TARGET_PAGE_ALIGN(len);
426: end = start + len;
427: if (end < start)
428: return -EINVAL;
429: if (end == start)
430: return 0;
1.1.1.4 ! root 431:
1.1 root 432: start &= qemu_host_page_mask;
1.1.1.2 root 433: return msync(g2h(start), end - start, flags);
1.1 root 434: }
435:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.