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); | ... | ... |