xref: /qemu/bsd-user/mmap.c (revision 6c173b3c09548fd5cd82de08646dfe939ec9416e)
1 /*
2  *  mmap support for qemu
3  *
4  *  Copyright (c) 2003 - 2008 Fabrice Bellard
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <sys/mman.h>
27 
28 #include "qemu.h"
29 #include "qemu-common.h"
30 #include "bsd-mman.h"
31 
32 //#define DEBUG_MMAP
33 
34 #if defined(USE_NPTL)
35 pthread_mutex_t mmap_mutex;
36 static int __thread mmap_lock_count;
37 
38 void mmap_lock(void)
39 {
40     if (mmap_lock_count++ == 0) {
41         pthread_mutex_lock(&mmap_mutex);
42     }
43 }
44 
45 void mmap_unlock(void)
46 {
47     if (--mmap_lock_count == 0) {
48         pthread_mutex_unlock(&mmap_mutex);
49     }
50 }
51 
52 /* Grab lock to make sure things are in a consistent state after fork().  */
53 void mmap_fork_start(void)
54 {
55     if (mmap_lock_count)
56         abort();
57     pthread_mutex_lock(&mmap_mutex);
58 }
59 
60 void mmap_fork_end(int child)
61 {
62     if (child)
63         pthread_mutex_init(&mmap_mutex, NULL);
64     else
65         pthread_mutex_unlock(&mmap_mutex);
66 }
67 #else
68 /* We aren't threadsafe to start with, so no need to worry about locking.  */
69 void mmap_lock(void)
70 {
71 }
72 
73 void mmap_unlock(void)
74 {
75 }
76 #endif
77 
78 void *qemu_vmalloc(size_t size)
79 {
80     void *p;
81     unsigned long addr;
82     mmap_lock();
83     /* Use map and mark the pages as used.  */
84     p = mmap(NULL, size, PROT_READ | PROT_WRITE,
85              MAP_PRIVATE | MAP_ANON, -1, 0);
86 
87     addr = (unsigned long)p;
88     if (addr == (target_ulong) addr) {
89         /* Allocated region overlaps guest address space.
90            This may recurse.  */
91         page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
92                        PAGE_RESERVED);
93     }
94 
95     mmap_unlock();
96     return p;
97 }
98 
99 void *qemu_malloc(size_t size)
100 {
101     char * p;
102     size += 16;
103     p = qemu_vmalloc(size);
104     *(size_t *)p = size;
105     return p + 16;
106 }
107 
108 /* We use map, which is always zero initialized.  */
109 void * qemu_mallocz(size_t size)
110 {
111     return qemu_malloc(size);
112 }
113 
114 void qemu_free(void *ptr)
115 {
116     /* FIXME: We should unmark the reserved pages here.  However this gets
117        complicated when one target page spans multiple host pages, so we
118        don't bother.  */
119     size_t *p;
120     p = (size_t *)((char *)ptr - 16);
121     munmap(p, *p);
122 }
123 
124 /* NOTE: all the constants are the HOST ones, but addresses are target. */
125 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
126 {
127     abi_ulong end, host_start, host_end, addr;
128     int prot1, ret;
129 
130 #ifdef DEBUG_MMAP
131     printf("mprotect: start=0x" TARGET_FMT_lx
132            " len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
133            prot & PROT_READ ? 'r' : '-',
134            prot & PROT_WRITE ? 'w' : '-',
135            prot & PROT_EXEC ? 'x' : '-');
136 #endif
137 
138     if ((start & ~TARGET_PAGE_MASK) != 0)
139         return -EINVAL;
140     len = TARGET_PAGE_ALIGN(len);
141     end = start + len;
142     if (end < start)
143         return -EINVAL;
144     prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
145     if (len == 0)
146         return 0;
147 
148     mmap_lock();
149     host_start = start & qemu_host_page_mask;
150     host_end = HOST_PAGE_ALIGN(end);
151     if (start > host_start) {
152         /* handle host page containing start */
153         prot1 = prot;
154         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
155             prot1 |= page_get_flags(addr);
156         }
157         if (host_end == host_start + qemu_host_page_size) {
158             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
159                 prot1 |= page_get_flags(addr);
160             }
161             end = host_end;
162         }
163         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
164         if (ret != 0)
165             goto error;
166         host_start += qemu_host_page_size;
167     }
168     if (end < host_end) {
169         prot1 = prot;
170         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
171             prot1 |= page_get_flags(addr);
172         }
173         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
174                        prot1 & PAGE_BITS);
175         if (ret != 0)
176             goto error;
177         host_end -= qemu_host_page_size;
178     }
179 
180     /* handle the pages in the middle */
181     if (host_start < host_end) {
182         ret = mprotect(g2h(host_start), host_end - host_start, prot);
183         if (ret != 0)
184             goto error;
185     }
186     page_set_flags(start, start + len, prot | PAGE_VALID);
187     mmap_unlock();
188     return 0;
189 error:
190     mmap_unlock();
191     return ret;
192 }
193 
194 /* map an incomplete host page */
195 static int mmap_frag(abi_ulong real_start,
196                      abi_ulong start, abi_ulong end,
197                      int prot, int flags, int fd, abi_ulong offset)
198 {
199     abi_ulong real_end, addr;
200     void *host_start;
201     int prot1, prot_new;
202 
203     real_end = real_start + qemu_host_page_size;
204     host_start = g2h(real_start);
205 
206     /* get the protection of the target pages outside the mapping */
207     prot1 = 0;
208     for(addr = real_start; addr < real_end; addr++) {
209         if (addr < start || addr >= end)
210             prot1 |= page_get_flags(addr);
211     }
212 
213     if (prot1 == 0) {
214         /* no page was there, so we allocate one */
215         void *p = mmap(host_start, qemu_host_page_size, prot,
216                        flags | MAP_ANON, -1, 0);
217         if (p == MAP_FAILED)
218             return -1;
219         prot1 = prot;
220     }
221     prot1 &= PAGE_BITS;
222 
223     prot_new = prot | prot1;
224     if (!(flags & MAP_ANON)) {
225         /* msync() won't work here, so we return an error if write is
226            possible while it is a shared mapping */
227         if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
228             (prot & PROT_WRITE))
229             return -EINVAL;
230 
231         /* adjust protection to be able to read */
232         if (!(prot1 & PROT_WRITE))
233             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
234 
235         /* read the corresponding file data */
236         pread(fd, g2h(start), end - start, offset);
237 
238         /* put final protection */
239         if (prot_new != (prot1 | PROT_WRITE))
240             mprotect(host_start, qemu_host_page_size, prot_new);
241     } else {
242         /* just update the protection */
243         if (prot_new != prot1) {
244             mprotect(host_start, qemu_host_page_size, prot_new);
245         }
246     }
247     return 0;
248 }
249 
250 #if defined(__CYGWIN__)
251 /* Cygwin doesn't have a whole lot of address space.  */
252 static abi_ulong mmap_next_start = 0x18000000;
253 #else
254 static abi_ulong mmap_next_start = 0x40000000;
255 #endif
256 
257 unsigned long last_brk;
258 
259 /* find a free memory area of size 'size'. The search starts at
260    'start'. If 'start' == 0, then a default start address is used.
261    Return -1 if error.
262 */
263 /* page_init() marks pages used by the host as reserved to be sure not
264    to use them. */
265 static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
266 {
267     abi_ulong addr, addr1, addr_start;
268     int prot;
269     unsigned long new_brk;
270 
271     new_brk = (unsigned long)sbrk(0);
272     if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
273         /* This is a hack to catch the host allocating memory with brk().
274            If it uses mmap then we loose.
275            FIXME: We really want to avoid the host allocating memory in
276            the first place, and maybe leave some slack to avoid switching
277            to mmap.  */
278         page_set_flags(last_brk & TARGET_PAGE_MASK,
279                        TARGET_PAGE_ALIGN(new_brk),
280                        PAGE_RESERVED);
281     }
282     last_brk = new_brk;
283 
284     size = HOST_PAGE_ALIGN(size);
285     start = start & qemu_host_page_mask;
286     addr = start;
287     if (addr == 0)
288         addr = mmap_next_start;
289     addr_start = addr;
290     for(;;) {
291         prot = 0;
292         for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
293             prot |= page_get_flags(addr1);
294         }
295         if (prot == 0)
296             break;
297         addr += qemu_host_page_size;
298         /* we found nothing */
299         if (addr == addr_start)
300             return (abi_ulong)-1;
301     }
302     if (start == 0)
303         mmap_next_start = addr + size;
304     return addr;
305 }
306 
307 /* NOTE: all the constants are the HOST ones */
308 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
309                      int flags, int fd, abi_ulong offset)
310 {
311     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
312     unsigned long host_start;
313 
314     mmap_lock();
315 #ifdef DEBUG_MMAP
316     {
317         printf("mmap: start=0x" TARGET_FMT_lx
318                " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
319                start, len,
320                prot & PROT_READ ? 'r' : '-',
321                prot & PROT_WRITE ? 'w' : '-',
322                prot & PROT_EXEC ? 'x' : '-');
323         if (flags & MAP_FIXED)
324             printf("MAP_FIXED ");
325         if (flags & MAP_ANON)
326             printf("MAP_ANON ");
327         switch(flags & TARGET_BSD_MAP_FLAGMASK) {
328         case MAP_PRIVATE:
329             printf("MAP_PRIVATE ");
330             break;
331         case MAP_SHARED:
332             printf("MAP_SHARED ");
333             break;
334         default:
335             printf("[MAP_FLAGMASK=0x%x] ", flags & TARGET_BSD_MAP_FLAGMASK);
336             break;
337         }
338         printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
339     }
340 #endif
341 
342     if (offset & ~TARGET_PAGE_MASK) {
343         errno = EINVAL;
344         goto fail;
345     }
346 
347     len = TARGET_PAGE_ALIGN(len);
348     if (len == 0)
349         goto the_end;
350     real_start = start & qemu_host_page_mask;
351 
352     if (!(flags & MAP_FIXED)) {
353         abi_ulong mmap_start;
354         void *p;
355         host_offset = offset & qemu_host_page_mask;
356         host_len = len + offset - host_offset;
357         host_len = HOST_PAGE_ALIGN(host_len);
358         mmap_start = mmap_find_vma(real_start, host_len);
359         if (mmap_start == (abi_ulong)-1) {
360             errno = ENOMEM;
361             goto fail;
362         }
363         /* Note: we prefer to control the mapping address. It is
364            especially important if qemu_host_page_size >
365            qemu_real_host_page_size */
366         p = mmap(g2h(mmap_start),
367                  host_len, prot, flags | MAP_FIXED, fd, host_offset);
368         if (p == MAP_FAILED)
369             goto fail;
370         /* update start so that it points to the file position at 'offset' */
371         host_start = (unsigned long)p;
372         if (!(flags & MAP_ANON))
373             host_start += offset - host_offset;
374         start = h2g(host_start);
375     } else {
376         int flg;
377         target_ulong addr;
378 
379         if (start & ~TARGET_PAGE_MASK) {
380             errno = EINVAL;
381             goto fail;
382         }
383         end = start + len;
384         real_end = HOST_PAGE_ALIGN(end);
385 
386         for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
387             flg = page_get_flags(addr);
388             if (flg & PAGE_RESERVED) {
389                 errno = ENXIO;
390                 goto fail;
391             }
392         }
393 
394         /* worst case: we cannot map the file because the offset is not
395            aligned, so we read it */
396         if (!(flags & MAP_ANON) &&
397             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
398             /* msync() won't work here, so we return an error if write is
399                possible while it is a shared mapping */
400             if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
401                 (prot & PROT_WRITE)) {
402                 errno = EINVAL;
403                 goto fail;
404             }
405             retaddr = target_mmap(start, len, prot | PROT_WRITE,
406                                   MAP_FIXED | MAP_PRIVATE | MAP_ANON,
407                                   -1, 0);
408             if (retaddr == -1)
409                 goto fail;
410             pread(fd, g2h(start), len, offset);
411             if (!(prot & PROT_WRITE)) {
412                 ret = target_mprotect(start, len, prot);
413                 if (ret != 0) {
414                     start = ret;
415                     goto the_end;
416                 }
417             }
418             goto the_end;
419         }
420 
421         /* handle the start of the mapping */
422         if (start > real_start) {
423             if (real_end == real_start + qemu_host_page_size) {
424                 /* one single host page */
425                 ret = mmap_frag(real_start, start, end,
426                                 prot, flags, fd, offset);
427                 if (ret == -1)
428                     goto fail;
429                 goto the_end1;
430             }
431             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
432                             prot, flags, fd, offset);
433             if (ret == -1)
434                 goto fail;
435             real_start += qemu_host_page_size;
436         }
437         /* handle the end of the mapping */
438         if (end < real_end) {
439             ret = mmap_frag(real_end - qemu_host_page_size,
440                             real_end - qemu_host_page_size, real_end,
441                             prot, flags, fd,
442                             offset + real_end - qemu_host_page_size - start);
443             if (ret == -1)
444                 goto fail;
445             real_end -= qemu_host_page_size;
446         }
447 
448         /* map the middle (easier) */
449         if (real_start < real_end) {
450             void *p;
451             unsigned long offset1;
452             if (flags & MAP_ANON)
453                 offset1 = 0;
454             else
455                 offset1 = offset + real_start - start;
456             p = mmap(g2h(real_start), real_end - real_start,
457                      prot, flags, fd, offset1);
458             if (p == MAP_FAILED)
459                 goto fail;
460         }
461     }
462  the_end1:
463     page_set_flags(start, start + len, prot | PAGE_VALID);
464  the_end:
465 #ifdef DEBUG_MMAP
466     printf("ret=0x" TARGET_FMT_lx "\n", start);
467     page_dump(stdout);
468     printf("\n");
469 #endif
470     mmap_unlock();
471     return start;
472 fail:
473     mmap_unlock();
474     return -1;
475 }
476 
477 int target_munmap(abi_ulong start, abi_ulong len)
478 {
479     abi_ulong end, real_start, real_end, addr;
480     int prot, ret;
481 
482 #ifdef DEBUG_MMAP
483     printf("munmap: start=0x%lx len=0x%lx\n", start, len);
484 #endif
485     if (start & ~TARGET_PAGE_MASK)
486         return -EINVAL;
487     len = TARGET_PAGE_ALIGN(len);
488     if (len == 0)
489         return -EINVAL;
490     mmap_lock();
491     end = start + len;
492     real_start = start & qemu_host_page_mask;
493     real_end = HOST_PAGE_ALIGN(end);
494 
495     if (start > real_start) {
496         /* handle host page containing start */
497         prot = 0;
498         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
499             prot |= page_get_flags(addr);
500         }
501         if (real_end == real_start + qemu_host_page_size) {
502             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
503                 prot |= page_get_flags(addr);
504             }
505             end = real_end;
506         }
507         if (prot != 0)
508             real_start += qemu_host_page_size;
509     }
510     if (end < real_end) {
511         prot = 0;
512         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
513             prot |= page_get_flags(addr);
514         }
515         if (prot != 0)
516             real_end -= qemu_host_page_size;
517     }
518 
519     ret = 0;
520     /* unmap what we can */
521     if (real_start < real_end) {
522         ret = munmap(g2h(real_start), real_end - real_start);
523     }
524 
525     if (ret == 0)
526         page_set_flags(start, start + len, 0);
527     mmap_unlock();
528     return ret;
529 }
530 
531 int target_msync(abi_ulong start, abi_ulong len, int flags)
532 {
533     abi_ulong end;
534 
535     if (start & ~TARGET_PAGE_MASK)
536         return -EINVAL;
537     len = TARGET_PAGE_ALIGN(len);
538     end = start + len;
539     if (end < start)
540         return -EINVAL;
541     if (end == start)
542         return 0;
543 
544     start &= qemu_host_page_mask;
545     return msync(g2h(start), end - start, flags);
546 }
547