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,19 +151,53 @@ static int mmap_frag(abi_ulong real_start, | ||
151 | return 0; | 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 | /* NOTE: all the constants are the HOST ones */ | 195 | /* NOTE: all the constants are the HOST ones */ |
155 | abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, | 196 | abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, |
156 | int flags, int fd, abi_ulong offset) | 197 | int flags, int fd, abi_ulong offset) |
157 | { | 198 | { |
158 | abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; | 199 | abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; |
159 | unsigned long host_start; | 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 | #ifdef DEBUG_MMAP | 202 | #ifdef DEBUG_MMAP |
169 | { | 203 | { |
@@ -203,147 +237,101 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, | @@ -203,147 +237,101 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, | ||
203 | real_start = start & qemu_host_page_mask; | 237 | real_start = start & qemu_host_page_mask; |
204 | 238 | ||
205 | if (!(flags & MAP_FIXED)) { | 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 | errno = EINVAL; | 264 | errno = EINVAL; |
292 | return -1; | 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 | prot, flags, fd, offset); | 306 | prot, flags, fd, offset); |
314 | if (ret == -1) | 307 | if (ret == -1) |
315 | return ret; | 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 | the_end1: | 336 | the_end1: |
349 | page_set_flags(start, start + len, prot | PAGE_VALID); | 337 | page_set_flags(start, start + len, prot | PAGE_VALID); |