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