Commit a5b85f7959378bc038803e51918e0f64461eb9b6
1 parent
0574b6fb
Fix mmap to handle differing host/target page sizes, by Edgar E. Iglesias.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3291 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
67 additions
and
39 deletions
linux-user/mmap.c
| @@ -36,7 +36,8 @@ int target_mprotect(target_ulong start, target_ulong len, int prot) | @@ -36,7 +36,8 @@ int target_mprotect(target_ulong start, target_ulong len, int prot) | ||
| 36 | int prot1, ret; | 36 | int prot1, ret; |
| 37 | 37 | ||
| 38 | #ifdef DEBUG_MMAP | 38 | #ifdef DEBUG_MMAP |
| 39 | - printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len, | 39 | + printf("mprotect: start=0x" TARGET_FMT_lx |
| 40 | + "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len, | ||
| 40 | prot & PROT_READ ? 'r' : '-', | 41 | prot & PROT_READ ? 'r' : '-', |
| 41 | prot & PROT_WRITE ? 'w' : '-', | 42 | prot & PROT_WRITE ? 'w' : '-', |
| 42 | prot & PROT_EXEC ? 'x' : '-'); | 43 | prot & PROT_EXEC ? 'x' : '-'); |
| @@ -151,11 +152,11 @@ static int mmap_frag(target_ulong real_start, | @@ -151,11 +152,11 @@ static int mmap_frag(target_ulong real_start, | ||
| 151 | } | 152 | } |
| 152 | 153 | ||
| 153 | /* NOTE: all the constants are the HOST ones */ | 154 | /* NOTE: all the constants are the HOST ones */ |
| 154 | -long target_mmap(target_ulong start, target_ulong len, int prot, | 155 | +target_long target_mmap(target_ulong start, target_ulong len, int prot, |
| 155 | int flags, int fd, target_ulong offset) | 156 | int flags, int fd, target_ulong offset) |
| 156 | { | 157 | { |
| 157 | target_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; | 158 | target_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; |
| 158 | - long host_start; | 159 | + unsigned long host_start; |
| 159 | #if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ | 160 | #if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ |
| 160 | defined(__ia64) || defined(__mips__) | 161 | defined(__ia64) || defined(__mips__) |
| 161 | static target_ulong last_start = 0x40000000; | 162 | static target_ulong last_start = 0x40000000; |
| @@ -166,7 +167,8 @@ long target_mmap(target_ulong start, target_ulong len, int prot, | @@ -166,7 +167,8 @@ long target_mmap(target_ulong start, target_ulong len, int prot, | ||
| 166 | 167 | ||
| 167 | #ifdef DEBUG_MMAP | 168 | #ifdef DEBUG_MMAP |
| 168 | { | 169 | { |
| 169 | - printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=", | 170 | + printf("mmap: start=0x" TARGET_FMT_lx |
| 171 | + " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=", | ||
| 170 | start, len, | 172 | start, len, |
| 171 | prot & PROT_READ ? 'r' : '-', | 173 | prot & PROT_READ ? 'r' : '-', |
| 172 | prot & PROT_WRITE ? 'w' : '-', | 174 | prot & PROT_WRITE ? 'w' : '-', |
| @@ -186,7 +188,7 @@ long target_mmap(target_ulong start, target_ulong len, int prot, | @@ -186,7 +188,7 @@ long target_mmap(target_ulong start, target_ulong len, int prot, | ||
| 186 | printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); | 188 | printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); |
| 187 | break; | 189 | break; |
| 188 | } | 190 | } |
| 189 | - printf("fd=%d offset=%lx\n", fd, offset); | 191 | + printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset); |
| 190 | } | 192 | } |
| 191 | #endif | 193 | #endif |
| 192 | 194 | ||
| @@ -209,34 +211,59 @@ long target_mmap(target_ulong start, target_ulong len, int prot, | @@ -209,34 +211,59 @@ long target_mmap(target_ulong start, target_ulong len, int prot, | ||
| 209 | last_start += HOST_PAGE_ALIGN(len); | 211 | last_start += HOST_PAGE_ALIGN(len); |
| 210 | } | 212 | } |
| 211 | #endif | 213 | #endif |
| 212 | - if (0 && qemu_host_page_size != qemu_real_host_page_size) { | ||
| 213 | - /* NOTE: this code is only for debugging with '-p' option */ | ||
| 214 | - /* ??? Can also occur when TARGET_PAGE_SIZE > host page size. */ | ||
| 215 | - /* reserve a memory area */ | ||
| 216 | - /* ??? This needs fixing for remapping. */ | ||
| 217 | -abort(); | ||
| 218 | - host_len = HOST_PAGE_ALIGN(len) + qemu_host_page_size - TARGET_PAGE_SIZE; | ||
| 219 | - real_start = (long)mmap(g2h(real_start), host_len, PROT_NONE, | ||
| 220 | - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
| 221 | - if (real_start == -1) | ||
| 222 | - return real_start; | ||
| 223 | - real_end = real_start + host_len; | ||
| 224 | - start = HOST_PAGE_ALIGN(real_start); | ||
| 225 | - end = start + HOST_PAGE_ALIGN(len); | ||
| 226 | - if (start > real_start) | ||
| 227 | - munmap((void *)g2h(real_start), start - real_start); | ||
| 228 | - if (end < real_end) | ||
| 229 | - munmap((void *)g2h(end), real_end - end); | ||
| 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; | 214 | host_offset = offset & qemu_host_page_mask; |
| 235 | host_len = len + offset - host_offset; | 215 | host_len = len + offset - host_offset; |
| 216 | + | ||
| 217 | + if (qemu_host_page_size > qemu_real_host_page_size) { | ||
| 218 | + /* | ||
| 219 | + * The guest expects to see mmapped areas aligned to it's pagesize. | ||
| 220 | + * If the host's real page size is smaller than the guest's, we need | ||
| 221 | + * to fixup the maps. It is done by allocating a larger area, | ||
| 222 | + * displacing the map (if needed) and finally chopping off the spare | ||
| 223 | + * room at the edges. | ||
| 224 | + */ | ||
| 225 | + | ||
| 226 | + /* | ||
| 227 | + * We assume qemu_host_page_size is always the same as | ||
| 228 | + * TARGET_PAGE_SIZE, see exec.c. qemu_real_host_page_size is the | ||
| 229 | + * hosts real page size. | ||
| 230 | + */ | ||
| 231 | + target_ulong host_end; | ||
| 232 | + unsigned long host_aligned_start; | ||
| 233 | + | ||
| 234 | + host_len = HOST_PAGE_ALIGN(host_len + qemu_host_page_size | ||
| 235 | + - qemu_real_host_page_size); | ||
| 236 | + host_start = (unsigned long) mmap(real_start ? | ||
| 237 | + g2h(real_start) : NULL, | ||
| 238 | + host_len, prot, flags, | ||
| 239 | + fd, host_offset); | ||
| 240 | + if (host_start == -1) | ||
| 241 | + return -1; | ||
| 242 | + | ||
| 243 | + host_end = host_start + host_len; | ||
| 244 | + | ||
| 245 | + /* Find start and end, aligned to the targets pagesize with-in the | ||
| 246 | + large mmaped area. */ | ||
| 247 | + host_aligned_start = TARGET_PAGE_ALIGN(host_start); | ||
| 248 | + if (!(flags & MAP_ANONYMOUS)) | ||
| 249 | + host_aligned_start += offset - host_offset; | ||
| 250 | + | ||
| 251 | + start = h2g(host_aligned_start); | ||
| 252 | + end = start + TARGET_PAGE_ALIGN(len); | ||
| 253 | + | ||
| 254 | + /* Chop off the leftovers, if any. */ | ||
| 255 | + if (host_aligned_start > host_start) | ||
| 256 | + munmap((void *)host_start, host_aligned_start - host_start); | ||
| 257 | + if (end < host_end) | ||
| 258 | + munmap((void *)g2h(end), host_end - end); | ||
| 259 | + | ||
| 260 | + goto the_end1; | ||
| 261 | + } else { | ||
| 262 | + /* if not fixed, no need to do anything */ | ||
| 236 | host_start = (long)mmap(real_start ? g2h(real_start) : NULL, | 263 | host_start = (long)mmap(real_start ? g2h(real_start) : NULL, |
| 237 | host_len, prot, flags, fd, host_offset); | 264 | host_len, prot, flags, fd, host_offset); |
| 238 | if (host_start == -1) | 265 | if (host_start == -1) |
| 239 | - return host_start; | 266 | + return -1; |
| 240 | /* update start so that it points to the file position at 'offset' */ | 267 | /* update start so that it points to the file position at 'offset' */ |
| 241 | if (!(flags & MAP_ANONYMOUS)) | 268 | if (!(flags & MAP_ANONYMOUS)) |
| 242 | host_start += offset - host_offset; | 269 | host_start += offset - host_offset; |
| @@ -267,7 +294,7 @@ abort(); | @@ -267,7 +294,7 @@ abort(); | ||
| 267 | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, | 294 | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, |
| 268 | -1, 0); | 295 | -1, 0); |
| 269 | if (retaddr == -1) | 296 | if (retaddr == -1) |
| 270 | - return retaddr; | 297 | + return -1; |
| 271 | pread(fd, g2h(start), len, offset); | 298 | pread(fd, g2h(start), len, offset); |
| 272 | if (!(prot & PROT_WRITE)) { | 299 | if (!(prot & PROT_WRITE)) { |
| 273 | ret = target_mprotect(start, len, prot); | 300 | ret = target_mprotect(start, len, prot); |
| @@ -300,7 +327,7 @@ abort(); | @@ -300,7 +327,7 @@ abort(); | ||
| 300 | prot, flags, fd, | 327 | prot, flags, fd, |
| 301 | offset + real_end - qemu_host_page_size - start); | 328 | offset + real_end - qemu_host_page_size - start); |
| 302 | if (ret == -1) | 329 | if (ret == -1) |
| 303 | - return ret; | 330 | + return -1; |
| 304 | real_end -= qemu_host_page_size; | 331 | real_end -= qemu_host_page_size; |
| 305 | } | 332 | } |
| 306 | 333 | ||
| @@ -314,13 +341,13 @@ abort(); | @@ -314,13 +341,13 @@ abort(); | ||
| 314 | ret = (long)mmap(g2h(real_start), real_end - real_start, | 341 | ret = (long)mmap(g2h(real_start), real_end - real_start, |
| 315 | prot, flags, fd, offset1); | 342 | prot, flags, fd, offset1); |
| 316 | if (ret == -1) | 343 | if (ret == -1) |
| 317 | - return ret; | 344 | + return -1; |
| 318 | } | 345 | } |
| 319 | the_end1: | 346 | the_end1: |
| 320 | page_set_flags(start, start + len, prot | PAGE_VALID); | 347 | page_set_flags(start, start + len, prot | PAGE_VALID); |
| 321 | the_end: | 348 | the_end: |
| 322 | #ifdef DEBUG_MMAP | 349 | #ifdef DEBUG_MMAP |
| 323 | - printf("ret=0x%lx\n", (long)start); | 350 | + printf("ret=0x%llx\n", start); |
| 324 | page_dump(stdout); | 351 | page_dump(stdout); |
| 325 | printf("\n"); | 352 | printf("\n"); |
| 326 | #endif | 353 | #endif |
| @@ -381,17 +408,18 @@ int target_munmap(target_ulong start, target_ulong len) | @@ -381,17 +408,18 @@ int target_munmap(target_ulong start, target_ulong len) | ||
| 381 | 408 | ||
| 382 | /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED | 409 | /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED |
| 383 | blocks which have been allocated starting on a host page */ | 410 | blocks which have been allocated starting on a host page */ |
| 384 | -long target_mremap(target_ulong old_addr, target_ulong old_size, | 411 | +target_long target_mremap(target_ulong old_addr, target_ulong old_size, |
| 385 | target_ulong new_size, unsigned long flags, | 412 | target_ulong new_size, unsigned long flags, |
| 386 | target_ulong new_addr) | 413 | target_ulong new_addr) |
| 387 | { | 414 | { |
| 388 | int prot; | 415 | int prot; |
| 416 | + unsigned long host_addr; | ||
| 389 | 417 | ||
| 390 | /* XXX: use 5 args syscall */ | 418 | /* XXX: use 5 args syscall */ |
| 391 | - new_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags); | ||
| 392 | - if (new_addr == -1) | ||
| 393 | - return new_addr; | ||
| 394 | - new_addr = h2g(new_addr); | 419 | + host_addr = (long)mremap(g2h(old_addr), old_size, new_size, flags); |
| 420 | + if (host_addr == -1) | ||
| 421 | + return -1; | ||
| 422 | + new_addr = h2g(host_addr); | ||
| 395 | prot = page_get_flags(old_addr); | 423 | prot = page_get_flags(old_addr); |
| 396 | page_set_flags(old_addr, old_addr + old_size, 0); | 424 | page_set_flags(old_addr, old_addr + old_size, 0); |
| 397 | page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); | 425 | page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID); |
linux-user/qemu.h
| @@ -164,10 +164,10 @@ int do_vm86(CPUX86State *env, long subfunction, target_ulong v86_addr); | @@ -164,10 +164,10 @@ int do_vm86(CPUX86State *env, long subfunction, target_ulong v86_addr); | ||
| 164 | 164 | ||
| 165 | /* mmap.c */ | 165 | /* mmap.c */ |
| 166 | int target_mprotect(target_ulong start, target_ulong len, int prot); | 166 | int target_mprotect(target_ulong start, target_ulong len, int prot); |
| 167 | -long target_mmap(target_ulong start, target_ulong len, int prot, | 167 | +target_long target_mmap(target_ulong start, target_ulong len, int prot, |
| 168 | int flags, int fd, target_ulong offset); | 168 | int flags, int fd, target_ulong offset); |
| 169 | int target_munmap(target_ulong start, target_ulong len); | 169 | int target_munmap(target_ulong start, target_ulong len); |
| 170 | -long target_mremap(target_ulong old_addr, target_ulong old_size, | 170 | +target_long target_mremap(target_ulong old_addr, target_ulong old_size, |
| 171 | target_ulong new_size, unsigned long flags, | 171 | target_ulong new_size, unsigned long flags, |
| 172 | target_ulong new_addr); | 172 | target_ulong new_addr); |
| 173 | int target_msync(target_ulong start, target_ulong len, int flags); | 173 | int target_msync(target_ulong start, target_ulong len, int flags); |