Commit a03e2d421e7f33316750d6b7396d1a7e14b18d53

Authored by bellard
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);
... ...