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