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 | 36 | int prot1, ret; |
| 37 | 37 | |
| 38 | 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 | 41 | prot & PROT_READ ? 'r' : '-', |
| 41 | 42 | prot & PROT_WRITE ? 'w' : '-', |
| 42 | 43 | prot & PROT_EXEC ? 'x' : '-'); |
| ... | ... | @@ -151,11 +152,11 @@ static int mmap_frag(target_ulong real_start, |
| 151 | 152 | } |
| 152 | 153 | |
| 153 | 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 | 156 | int flags, int fd, target_ulong offset) |
| 156 | 157 | { |
| 157 | 158 | target_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; |
| 158 | - long host_start; | |
| 159 | + unsigned long host_start; | |
| 159 | 160 | #if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ |
| 160 | 161 | defined(__ia64) || defined(__mips__) |
| 161 | 162 | static target_ulong last_start = 0x40000000; |
| ... | ... | @@ -166,7 +167,8 @@ long target_mmap(target_ulong start, target_ulong len, int prot, |
| 166 | 167 | |
| 167 | 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 | 172 | start, len, |
| 171 | 173 | prot & PROT_READ ? 'r' : '-', |
| 172 | 174 | prot & PROT_WRITE ? 'w' : '-', |
| ... | ... | @@ -186,7 +188,7 @@ long target_mmap(target_ulong start, target_ulong len, int prot, |
| 186 | 188 | printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); |
| 187 | 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 | 193 | #endif |
| 192 | 194 | |
| ... | ... | @@ -209,34 +211,59 @@ long target_mmap(target_ulong start, target_ulong len, int prot, |
| 209 | 211 | last_start += HOST_PAGE_ALIGN(len); |
| 210 | 212 | } |
| 211 | 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 | 214 | host_offset = offset & qemu_host_page_mask; |
| 235 | 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 | 263 | host_start = (long)mmap(real_start ? g2h(real_start) : NULL, |
| 237 | 264 | host_len, prot, flags, fd, host_offset); |
| 238 | 265 | if (host_start == -1) |
| 239 | - return host_start; | |
| 266 | + return -1; | |
| 240 | 267 | /* update start so that it points to the file position at 'offset' */ |
| 241 | 268 | if (!(flags & MAP_ANONYMOUS)) |
| 242 | 269 | host_start += offset - host_offset; |
| ... | ... | @@ -267,7 +294,7 @@ abort(); |
| 267 | 294 | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, |
| 268 | 295 | -1, 0); |
| 269 | 296 | if (retaddr == -1) |
| 270 | - return retaddr; | |
| 297 | + return -1; | |
| 271 | 298 | pread(fd, g2h(start), len, offset); |
| 272 | 299 | if (!(prot & PROT_WRITE)) { |
| 273 | 300 | ret = target_mprotect(start, len, prot); |
| ... | ... | @@ -300,7 +327,7 @@ abort(); |
| 300 | 327 | prot, flags, fd, |
| 301 | 328 | offset + real_end - qemu_host_page_size - start); |
| 302 | 329 | if (ret == -1) |
| 303 | - return ret; | |
| 330 | + return -1; | |
| 304 | 331 | real_end -= qemu_host_page_size; |
| 305 | 332 | } |
| 306 | 333 | |
| ... | ... | @@ -314,13 +341,13 @@ abort(); |
| 314 | 341 | ret = (long)mmap(g2h(real_start), real_end - real_start, |
| 315 | 342 | prot, flags, fd, offset1); |
| 316 | 343 | if (ret == -1) |
| 317 | - return ret; | |
| 344 | + return -1; | |
| 318 | 345 | } |
| 319 | 346 | the_end1: |
| 320 | 347 | page_set_flags(start, start + len, prot | PAGE_VALID); |
| 321 | 348 | the_end: |
| 322 | 349 | #ifdef DEBUG_MMAP |
| 323 | - printf("ret=0x%lx\n", (long)start); | |
| 350 | + printf("ret=0x%llx\n", start); | |
| 324 | 351 | page_dump(stdout); |
| 325 | 352 | printf("\n"); |
| 326 | 353 | #endif |
| ... | ... | @@ -381,17 +408,18 @@ int target_munmap(target_ulong start, target_ulong len) |
| 381 | 408 | |
| 382 | 409 | /* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED |
| 383 | 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 | 412 | target_ulong new_size, unsigned long flags, |
| 386 | 413 | target_ulong new_addr) |
| 387 | 414 | { |
| 388 | 415 | int prot; |
| 416 | + unsigned long host_addr; | |
| 389 | 417 | |
| 390 | 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 | 423 | prot = page_get_flags(old_addr); |
| 396 | 424 | page_set_flags(old_addr, old_addr + old_size, 0); |
| 397 | 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 | 164 | |
| 165 | 165 | /* mmap.c */ |
| 166 | 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 | 168 | int flags, int fd, target_ulong offset); |
| 169 | 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 | 171 | target_ulong new_size, unsigned long flags, |
| 172 | 172 | target_ulong new_addr); |
| 173 | 173 | int target_msync(target_ulong start, target_ulong len, int flags); | ... | ... |