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