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