Commit a03e2d421e7f33316750d6b7396d1a7e14b18d53
1 parent
dae3270c
fixed target_mmap() if host page size < TARGET_PAGE_SIZE
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3642 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
126 additions
and
138 deletions
linux-user/mmap.c
| ... | ... | @@ -151,19 +151,53 @@ static int mmap_frag(abi_ulong real_start, |
| 151 | 151 | return 0; |
| 152 | 152 | } |
| 153 | 153 | |
| 154 | +#if defined(__CYGWIN__) | |
| 155 | +/* Cygwin doesn't have a whole lot of address space. */ | |
| 156 | +static abi_ulong mmap_next_start = 0x18000000; | |
| 157 | +#else | |
| 158 | +static abi_ulong mmap_next_start = 0x40000000; | |
| 159 | +#endif | |
| 160 | + | |
| 161 | +/* find a free memory area of size 'size'. The search starts at | |
| 162 | + 'start'. If 'start' == 0, then a default start address is used. | |
| 163 | + Return -1 if error. | |
| 164 | +*/ | |
| 165 | +/* XXX: should mark pages used by the host as reserved to be sure not | |
| 166 | + to use them. */ | |
| 167 | +static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) | |
| 168 | +{ | |
| 169 | + abi_ulong addr, addr1, addr_start; | |
| 170 | + int prot; | |
| 171 | + | |
| 172 | + size = HOST_PAGE_ALIGN(size); | |
| 173 | + start = start & qemu_host_page_mask; | |
| 174 | + addr = start; | |
| 175 | + if (addr == 0) | |
| 176 | + addr = mmap_next_start; | |
| 177 | + addr_start = addr; | |
| 178 | + for(;;) { | |
| 179 | + prot = 0; | |
| 180 | + for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) { | |
| 181 | + prot |= page_get_flags(addr1); | |
| 182 | + } | |
| 183 | + if (prot == 0) | |
| 184 | + break; | |
| 185 | + addr += qemu_host_page_size; | |
| 186 | + /* we found nothing */ | |
| 187 | + if (addr == addr_start) | |
| 188 | + return (abi_ulong)-1; | |
| 189 | + } | |
| 190 | + if (start == 0) | |
| 191 | + mmap_next_start = addr + size; | |
| 192 | + return addr; | |
| 193 | +} | |
| 194 | + | |
| 154 | 195 | /* NOTE: all the constants are the HOST ones */ |
| 155 | 196 | abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, |
| 156 | 197 | int flags, int fd, abi_ulong offset) |
| 157 | 198 | { |
| 158 | 199 | abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; |
| 159 | 200 | unsigned long host_start; |
| 160 | -#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ | |
| 161 | - defined(__ia64) || defined(__mips__) | |
| 162 | - static abi_ulong last_start = 0x40000000; | |
| 163 | -#elif defined(__CYGWIN__) | |
| 164 | - /* Cygwin doesn't have a whole lot of address space. */ | |
| 165 | - static abi_ulong last_start = 0x18000000; | |
| 166 | -#endif | |
| 167 | 201 | |
| 168 | 202 | #ifdef DEBUG_MMAP |
| 169 | 203 | { |
| ... | ... | @@ -203,147 +237,101 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, |
| 203 | 237 | real_start = start & qemu_host_page_mask; |
| 204 | 238 | |
| 205 | 239 | if (!(flags & MAP_FIXED)) { |
| 206 | -#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__) || \ | |
| 207 | - defined(__ia64) || defined(__mips__) || defined(__CYGWIN__) | |
| 208 | - /* tell the kernel to search at the same place as i386 */ | |
| 209 | - if (real_start == 0) { | |
| 210 | - real_start = last_start; | |
| 211 | - last_start += HOST_PAGE_ALIGN(len); | |
| 212 | - } | |
| 213 | -#endif | |
| 214 | - host_offset = offset & qemu_host_page_mask; | |
| 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 | - abi_ulong host_end; | |
| 232 | - unsigned long host_aligned_start; | |
| 233 | - void *p; | |
| 234 | - | |
| 235 | - host_len = HOST_PAGE_ALIGN(host_len + qemu_host_page_size | |
| 236 | - - qemu_real_host_page_size); | |
| 237 | - p = mmap(real_start ? g2h(real_start) : NULL, | |
| 238 | - host_len, prot, flags, fd, host_offset); | |
| 239 | - if (p == MAP_FAILED) | |
| 240 | - return -1; | |
| 241 | - | |
| 242 | - host_start = (unsigned long)p; | |
| 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 */ | |
| 263 | - void *p = mmap(real_start ? g2h(real_start) : NULL, | |
| 264 | - host_len, prot, flags, fd, host_offset); | |
| 265 | - if (p == MAP_FAILED) | |
| 266 | - return -1; | |
| 267 | - /* update start so that it points to the file position at 'offset' */ | |
| 268 | - host_start = (unsigned long)p; | |
| 269 | - if (!(flags & MAP_ANONYMOUS)) | |
| 270 | - host_start += offset - host_offset; | |
| 271 | - start = h2g(host_start); | |
| 272 | - goto the_end1; | |
| 240 | + abi_ulong mmap_start; | |
| 241 | + void *p; | |
| 242 | + host_offset = offset & qemu_host_page_mask; | |
| 243 | + host_len = len + offset - host_offset; | |
| 244 | + host_len = HOST_PAGE_ALIGN(host_len); | |
| 245 | + mmap_start = mmap_find_vma(real_start, host_len); | |
| 246 | + if (mmap_start == (abi_ulong)-1) { | |
| 247 | + errno = ENOMEM; | |
| 248 | + return -1; | |
| 273 | 249 | } |
| 274 | - } | |
| 275 | - | |
| 276 | - if (start & ~TARGET_PAGE_MASK) { | |
| 277 | - errno = EINVAL; | |
| 278 | - return -1; | |
| 279 | - } | |
| 280 | - end = start + len; | |
| 281 | - real_end = HOST_PAGE_ALIGN(end); | |
| 282 | - | |
| 283 | - /* worst case: we cannot map the file because the offset is not | |
| 284 | - aligned, so we read it */ | |
| 285 | - if (!(flags & MAP_ANONYMOUS) && | |
| 286 | - (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { | |
| 287 | - /* msync() won't work here, so we return an error if write is | |
| 288 | - possible while it is a shared mapping */ | |
| 289 | - if ((flags & MAP_TYPE) == MAP_SHARED && | |
| 290 | - (prot & PROT_WRITE)) { | |
| 250 | + /* Note: we prefer to control the mapping address. It is | |
| 251 | + especially important if qemu_host_page_size > | |
| 252 | + qemu_real_host_page_size */ | |
| 253 | + p = mmap(g2h(mmap_start), | |
| 254 | + host_len, prot, flags | MAP_FIXED, fd, host_offset); | |
| 255 | + if (p == MAP_FAILED) | |
| 256 | + return -1; | |
| 257 | + /* update start so that it points to the file position at 'offset' */ | |
| 258 | + host_start = (unsigned long)p; | |
| 259 | + if (!(flags & MAP_ANONYMOUS)) | |
| 260 | + host_start += offset - host_offset; | |
| 261 | + start = h2g(host_start); | |
| 262 | + } else { | |
| 263 | + if (start & ~TARGET_PAGE_MASK) { | |
| 291 | 264 | errno = EINVAL; |
| 292 | 265 | return -1; |
| 293 | 266 | } |
| 294 | - retaddr = target_mmap(start, len, prot | PROT_WRITE, | |
| 295 | - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, | |
| 296 | - -1, 0); | |
| 297 | - if (retaddr == -1) | |
| 298 | - return -1; | |
| 299 | - pread(fd, g2h(start), len, offset); | |
| 300 | - if (!(prot & PROT_WRITE)) { | |
| 301 | - ret = target_mprotect(start, len, prot); | |
| 302 | - if (ret != 0) | |
| 303 | - return ret; | |
| 267 | + end = start + len; | |
| 268 | + real_end = HOST_PAGE_ALIGN(end); | |
| 269 | + | |
| 270 | + /* worst case: we cannot map the file because the offset is not | |
| 271 | + aligned, so we read it */ | |
| 272 | + if (!(flags & MAP_ANONYMOUS) && | |
| 273 | + (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { | |
| 274 | + /* msync() won't work here, so we return an error if write is | |
| 275 | + possible while it is a shared mapping */ | |
| 276 | + if ((flags & MAP_TYPE) == MAP_SHARED && | |
| 277 | + (prot & PROT_WRITE)) { | |
| 278 | + errno = EINVAL; | |
| 279 | + return -1; | |
| 280 | + } | |
| 281 | + retaddr = target_mmap(start, len, prot | PROT_WRITE, | |
| 282 | + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, | |
| 283 | + -1, 0); | |
| 284 | + if (retaddr == -1) | |
| 285 | + return -1; | |
| 286 | + pread(fd, g2h(start), len, offset); | |
| 287 | + if (!(prot & PROT_WRITE)) { | |
| 288 | + ret = target_mprotect(start, len, prot); | |
| 289 | + if (ret != 0) | |
| 290 | + return ret; | |
| 291 | + } | |
| 292 | + goto the_end; | |
| 304 | 293 | } |
| 305 | - goto the_end; | |
| 306 | - } | |
| 307 | - | |
| 308 | - /* handle the start of the mapping */ | |
| 309 | - if (start > real_start) { | |
| 310 | - if (real_end == real_start + qemu_host_page_size) { | |
| 311 | - /* one single host page */ | |
| 312 | - ret = mmap_frag(real_start, start, end, | |
| 294 | + | |
| 295 | + /* handle the start of the mapping */ | |
| 296 | + if (start > real_start) { | |
| 297 | + if (real_end == real_start + qemu_host_page_size) { | |
| 298 | + /* one single host page */ | |
| 299 | + ret = mmap_frag(real_start, start, end, | |
| 300 | + prot, flags, fd, offset); | |
| 301 | + if (ret == -1) | |
| 302 | + return ret; | |
| 303 | + goto the_end1; | |
| 304 | + } | |
| 305 | + ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, | |
| 313 | 306 | prot, flags, fd, offset); |
| 314 | 307 | if (ret == -1) |
| 315 | 308 | return ret; |
| 316 | - goto the_end1; | |
| 309 | + real_start += qemu_host_page_size; | |
| 310 | + } | |
| 311 | + /* handle the end of the mapping */ | |
| 312 | + if (end < real_end) { | |
| 313 | + ret = mmap_frag(real_end - qemu_host_page_size, | |
| 314 | + real_end - qemu_host_page_size, real_end, | |
| 315 | + prot, flags, fd, | |
| 316 | + offset + real_end - qemu_host_page_size - start); | |
| 317 | + if (ret == -1) | |
| 318 | + return -1; | |
| 319 | + real_end -= qemu_host_page_size; | |
| 317 | 320 | } |
| 318 | - ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, | |
| 319 | - prot, flags, fd, offset); | |
| 320 | - if (ret == -1) | |
| 321 | - return ret; | |
| 322 | - real_start += qemu_host_page_size; | |
| 323 | - } | |
| 324 | - /* handle the end of the mapping */ | |
| 325 | - if (end < real_end) { | |
| 326 | - ret = mmap_frag(real_end - qemu_host_page_size, | |
| 327 | - real_end - qemu_host_page_size, real_end, | |
| 328 | - prot, flags, fd, | |
| 329 | - offset + real_end - qemu_host_page_size - start); | |
| 330 | - if (ret == -1) | |
| 331 | - return -1; | |
| 332 | - real_end -= qemu_host_page_size; | |
| 333 | - } | |
| 334 | 321 | |
| 335 | - /* map the middle (easier) */ | |
| 336 | - if (real_start < real_end) { | |
| 337 | - void *p; | |
| 338 | - unsigned long offset1; | |
| 339 | - if (flags & MAP_ANONYMOUS) | |
| 340 | - offset1 = 0; | |
| 341 | - else | |
| 342 | - offset1 = offset + real_start - start; | |
| 343 | - p = mmap(g2h(real_start), real_end - real_start, | |
| 344 | - prot, flags, fd, offset1); | |
| 345 | - if (p == MAP_FAILED) | |
| 346 | - return -1; | |
| 322 | + /* map the middle (easier) */ | |
| 323 | + if (real_start < real_end) { | |
| 324 | + void *p; | |
| 325 | + unsigned long offset1; | |
| 326 | + if (flags & MAP_ANONYMOUS) | |
| 327 | + offset1 = 0; | |
| 328 | + else | |
| 329 | + offset1 = offset + real_start - start; | |
| 330 | + p = mmap(g2h(real_start), real_end - real_start, | |
| 331 | + prot, flags, fd, offset1); | |
| 332 | + if (p == MAP_FAILED) | |
| 333 | + return -1; | |
| 334 | + } | |
| 347 | 335 | } |
| 348 | 336 | the_end1: |
| 349 | 337 | page_set_flags(start, start + len, prot | PAGE_VALID); | ... | ... |