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