Update to a hopefully more future proof FSF address
[sdk/emulator/qemu.git] / linux-user / mmap.c
1 /*
2  *  mmap support for qemu
3  *
4  *  Copyright (c) 2003 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, see <http://www.gnu.org/licenses/>.
18  */
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <linux/mman.h>
29 #include <linux/unistd.h>
30
31 #include "qemu.h"
32 #include "qemu-common.h"
33
34 //#define DEBUG_MMAP
35
36 #if defined(USE_NPTL)
37 pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
38 static int __thread mmap_lock_count;
39
40 void mmap_lock(void)
41 {
42     if (mmap_lock_count++ == 0) {
43         pthread_mutex_lock(&mmap_mutex);
44     }
45 }
46
47 void mmap_unlock(void)
48 {
49     if (--mmap_lock_count == 0) {
50         pthread_mutex_unlock(&mmap_mutex);
51     }
52 }
53
54 /* Grab lock to make sure things are in a consistent state after fork().  */
55 void mmap_fork_start(void)
56 {
57     if (mmap_lock_count)
58         abort();
59     pthread_mutex_lock(&mmap_mutex);
60 }
61
62 void mmap_fork_end(int child)
63 {
64     if (child)
65         pthread_mutex_init(&mmap_mutex, NULL);
66     else
67         pthread_mutex_unlock(&mmap_mutex);
68 }
69 #else
70 /* We aren't threadsafe to start with, so no need to worry about locking.  */
71 void mmap_lock(void)
72 {
73 }
74
75 void mmap_unlock(void)
76 {
77 }
78 #endif
79
80 void *qemu_vmalloc(size_t size)
81 {
82     void *p;
83     unsigned long addr;
84     mmap_lock();
85     /* Use map and mark the pages as used.  */
86     p = mmap(NULL, size, PROT_READ | PROT_WRITE,
87              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
88
89     addr = (unsigned long)p;
90     if (addr == (target_ulong) addr) {
91         /* Allocated region overlaps guest address space.
92            This may recurse.  */
93         page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
94                        PAGE_RESERVED);
95     }
96
97     mmap_unlock();
98     return p;
99 }
100
101 void *qemu_malloc(size_t size)
102 {
103     char * p;
104     size += 16;
105     p = qemu_vmalloc(size);
106     *(size_t *)p = size;
107     return p + 16;
108 }
109
110 /* We use map, which is always zero initialized.  */
111 void * qemu_mallocz(size_t size)
112 {
113     return qemu_malloc(size);
114 }
115
116 void qemu_free(void *ptr)
117 {
118     /* FIXME: We should unmark the reserved pages here.  However this gets
119        complicated when one target page spans multiple host pages, so we
120        don't bother.  */
121     size_t *p;
122     p = (size_t *)((char *)ptr - 16);
123     munmap(p, *p);
124 }
125
126 void *qemu_realloc(void *ptr, size_t size)
127 {
128     size_t old_size, copy;
129     void *new_ptr;
130
131     if (!ptr)
132         return qemu_malloc(size);
133     old_size = *(size_t *)((char *)ptr - 16);
134     copy = old_size < size ? old_size : size;
135     new_ptr = qemu_malloc(size);
136     memcpy(new_ptr, ptr, copy);
137     qemu_free(ptr);
138     return new_ptr;
139 }
140
141 /* NOTE: all the constants are the HOST ones, but addresses are target. */
142 int target_mprotect(abi_ulong start, abi_ulong len, int prot)
143 {
144     abi_ulong end, host_start, host_end, addr;
145     int prot1, ret;
146
147 #ifdef DEBUG_MMAP
148     printf("mprotect: start=0x" TARGET_FMT_lx
149            "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
150            prot & PROT_READ ? 'r' : '-',
151            prot & PROT_WRITE ? 'w' : '-',
152            prot & PROT_EXEC ? 'x' : '-');
153 #endif
154
155     if ((start & ~TARGET_PAGE_MASK) != 0)
156         return -EINVAL;
157     len = TARGET_PAGE_ALIGN(len);
158     end = start + len;
159     if (end < start)
160         return -EINVAL;
161     prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
162     if (len == 0)
163         return 0;
164
165     mmap_lock();
166     host_start = start & qemu_host_page_mask;
167     host_end = HOST_PAGE_ALIGN(end);
168     if (start > host_start) {
169         /* handle host page containing start */
170         prot1 = prot;
171         for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
172             prot1 |= page_get_flags(addr);
173         }
174         if (host_end == host_start + qemu_host_page_size) {
175             for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
176                 prot1 |= page_get_flags(addr);
177             }
178             end = host_end;
179         }
180         ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
181         if (ret != 0)
182             goto error;
183         host_start += qemu_host_page_size;
184     }
185     if (end < host_end) {
186         prot1 = prot;
187         for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
188             prot1 |= page_get_flags(addr);
189         }
190         ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
191                        prot1 & PAGE_BITS);
192         if (ret != 0)
193             goto error;
194         host_end -= qemu_host_page_size;
195     }
196
197     /* handle the pages in the middle */
198     if (host_start < host_end) {
199         ret = mprotect(g2h(host_start), host_end - host_start, prot);
200         if (ret != 0)
201             goto error;
202     }
203     page_set_flags(start, start + len, prot | PAGE_VALID);
204     mmap_unlock();
205     return 0;
206 error:
207     mmap_unlock();
208     return ret;
209 }
210
211 /* map an incomplete host page */
212 static int mmap_frag(abi_ulong real_start,
213                      abi_ulong start, abi_ulong end,
214                      int prot, int flags, int fd, abi_ulong offset)
215 {
216     abi_ulong real_end, addr;
217     void *host_start;
218     int prot1, prot_new;
219
220     real_end = real_start + qemu_host_page_size;
221     host_start = g2h(real_start);
222
223     /* get the protection of the target pages outside the mapping */
224     prot1 = 0;
225     for(addr = real_start; addr < real_end; addr++) {
226         if (addr < start || addr >= end)
227             prot1 |= page_get_flags(addr);
228     }
229
230     if (prot1 == 0) {
231         /* no page was there, so we allocate one */
232         void *p = mmap(host_start, qemu_host_page_size, prot,
233                        flags | MAP_ANONYMOUS, -1, 0);
234         if (p == MAP_FAILED)
235             return -1;
236         prot1 = prot;
237     }
238     prot1 &= PAGE_BITS;
239
240     prot_new = prot | prot1;
241     if (!(flags & MAP_ANONYMOUS)) {
242         /* msync() won't work here, so we return an error if write is
243            possible while it is a shared mapping */
244         if ((flags & MAP_TYPE) == MAP_SHARED &&
245             (prot & PROT_WRITE))
246             return -EINVAL;
247
248         /* adjust protection to be able to read */
249         if (!(prot1 & PROT_WRITE))
250             mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
251
252         /* read the corresponding file data */
253         pread(fd, g2h(start), end - start, offset);
254
255         /* put final protection */
256         if (prot_new != (prot1 | PROT_WRITE))
257             mprotect(host_start, qemu_host_page_size, prot_new);
258     } else {
259         /* just update the protection */
260         if (prot_new != prot1) {
261             mprotect(host_start, qemu_host_page_size, prot_new);
262         }
263     }
264     return 0;
265 }
266
267 #if defined(__CYGWIN__)
268 /* Cygwin doesn't have a whole lot of address space.  */
269 static abi_ulong mmap_next_start = 0x18000000;
270 #else
271 static abi_ulong mmap_next_start = 0x40000000;
272 #endif
273
274 unsigned long last_brk;
275
276 /* find a free memory area of size 'size'. The search starts at
277    'start'. If 'start' == 0, then a default start address is used.
278    Return -1 if error.
279 */
280 /* page_init() marks pages used by the host as reserved to be sure not
281    to use them. */
282 abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
283 {
284     abi_ulong addr, addr1, addr_start;
285     int prot;
286     unsigned long new_brk;
287
288     new_brk = (unsigned long)sbrk(0);
289     if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
290         /* This is a hack to catch the host allocating memory with brk().
291            If it uses mmap then we loose.
292            FIXME: We really want to avoid the host allocating memory in
293            the first place, and maybe leave some slack to avoid switching
294            to mmap.  */
295         page_set_flags(last_brk & TARGET_PAGE_MASK,
296                        TARGET_PAGE_ALIGN(new_brk),
297                        PAGE_RESERVED); 
298     }
299     last_brk = new_brk;
300
301     size = HOST_PAGE_ALIGN(size);
302     start = start & qemu_host_page_mask;
303     addr = start;
304     if (addr == 0)
305         addr = mmap_next_start;
306     addr_start = addr;
307     for(;;) {
308         prot = 0;
309         for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
310             prot |= page_get_flags(addr1);
311         }
312         if (prot == 0)
313             break;
314         addr += qemu_host_page_size;
315         /* we found nothing */
316         if (addr == addr_start)
317             return (abi_ulong)-1;
318     }
319     if (start == 0)
320         mmap_next_start = addr + size;
321     return addr;
322 }
323
324 /* NOTE: all the constants are the HOST ones */
325 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
326                      int flags, int fd, abi_ulong offset)
327 {
328     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
329     unsigned long host_start;
330
331     mmap_lock();
332 #ifdef DEBUG_MMAP
333     {
334         printf("mmap: start=0x" TARGET_FMT_lx
335                " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
336                start, len,
337                prot & PROT_READ ? 'r' : '-',
338                prot & PROT_WRITE ? 'w' : '-',
339                prot & PROT_EXEC ? 'x' : '-');
340         if (flags & MAP_FIXED)
341             printf("MAP_FIXED ");
342         if (flags & MAP_ANONYMOUS)
343             printf("MAP_ANON ");
344         switch(flags & MAP_TYPE) {
345         case MAP_PRIVATE:
346             printf("MAP_PRIVATE ");
347             break;
348         case MAP_SHARED:
349             printf("MAP_SHARED ");
350             break;
351         default:
352             printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
353             break;
354         }
355         printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
356     }
357 #endif
358
359     if (offset & ~TARGET_PAGE_MASK) {
360         errno = EINVAL;
361         goto fail;
362     }
363
364     len = TARGET_PAGE_ALIGN(len);
365     if (len == 0)
366         goto the_end;
367     real_start = start & qemu_host_page_mask;
368
369     /* When mapping files into a memory area larger than the file, accesses
370        to pages beyond the file size will cause a SIGBUS. 
371
372        For example, if mmaping a file of 100 bytes on a host with 4K pages
373        emulating a target with 8K pages, the target expects to be able to
374        access the first 8K. But the host will trap us on any access beyond
375        4K.  
376
377        When emulating a target with a larger page-size than the hosts, we
378        may need to truncate file maps at EOF and add extra anonymous pages
379        up to the targets page boundary.  */
380
381     if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
382         && !(flags & MAP_ANONYMOUS)) {
383        struct stat sb;
384
385        if (fstat (fd, &sb) == -1)
386            goto fail;
387
388        /* Are we trying to create a map beyond EOF?.  */
389        if (offset + len > sb.st_size) {
390            /* If so, truncate the file map at eof aligned with 
391               the hosts real pagesize. Additional anonymous maps
392               will be created beyond EOF.  */
393            len = (sb.st_size - offset);
394            len += qemu_real_host_page_size - 1;
395            len &= ~(qemu_real_host_page_size - 1);
396        }
397     }
398
399     if (!(flags & MAP_FIXED)) {
400         abi_ulong mmap_start;
401         void *p;
402         host_offset = offset & qemu_host_page_mask;
403         host_len = len + offset - host_offset;
404         host_len = HOST_PAGE_ALIGN(host_len);
405         mmap_start = mmap_find_vma(real_start, host_len);
406         if (mmap_start == (abi_ulong)-1) {
407             errno = ENOMEM;
408             goto fail;
409         }
410         /* Note: we prefer to control the mapping address. It is
411            especially important if qemu_host_page_size >
412            qemu_real_host_page_size */
413         p = mmap(g2h(mmap_start),
414                  host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
415         if (p == MAP_FAILED)
416             goto fail;
417         /* update start so that it points to the file position at 'offset' */
418         host_start = (unsigned long)p;
419         if (!(flags & MAP_ANONYMOUS)) {
420             p = mmap(g2h(mmap_start), len, prot, 
421                      flags | MAP_FIXED, fd, host_offset);
422             host_start += offset - host_offset;
423         }
424         start = h2g(host_start);
425     } else {
426         int flg;
427         target_ulong addr;
428
429         if (start & ~TARGET_PAGE_MASK) {
430             errno = EINVAL;
431             goto fail;
432         }
433         end = start + len;
434         real_end = HOST_PAGE_ALIGN(end);
435
436         /*
437          * Test if requested memory area fits target address space
438          * It can fail only on 64-bit host with 32-bit target.
439          * On any other target/host host mmap() handles this error correctly.
440          */
441         if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
442             errno = EINVAL;
443             goto fail;
444         }
445
446         for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
447             flg = page_get_flags(addr);
448             if (flg & PAGE_RESERVED) {
449                 errno = ENXIO;
450                 goto fail;
451             }
452         }
453
454         /* worst case: we cannot map the file because the offset is not
455            aligned, so we read it */
456         if (!(flags & MAP_ANONYMOUS) &&
457             (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
458             /* msync() won't work here, so we return an error if write is
459                possible while it is a shared mapping */
460             if ((flags & MAP_TYPE) == MAP_SHARED &&
461                 (prot & PROT_WRITE)) {
462                 errno = EINVAL;
463                 goto fail;
464             }
465             retaddr = target_mmap(start, len, prot | PROT_WRITE,
466                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
467                                   -1, 0);
468             if (retaddr == -1)
469                 goto fail;
470             pread(fd, g2h(start), len, offset);
471             if (!(prot & PROT_WRITE)) {
472                 ret = target_mprotect(start, len, prot);
473                 if (ret != 0) {
474                     start = ret;
475                     goto the_end;
476                 }
477             }
478             goto the_end;
479         }
480         
481         /* handle the start of the mapping */
482         if (start > real_start) {
483             if (real_end == real_start + qemu_host_page_size) {
484                 /* one single host page */
485                 ret = mmap_frag(real_start, start, end,
486                                 prot, flags, fd, offset);
487                 if (ret == -1)
488                     goto fail;
489                 goto the_end1;
490             }
491             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
492                             prot, flags, fd, offset);
493             if (ret == -1)
494                 goto fail;
495             real_start += qemu_host_page_size;
496         }
497         /* handle the end of the mapping */
498         if (end < real_end) {
499             ret = mmap_frag(real_end - qemu_host_page_size,
500                             real_end - qemu_host_page_size, real_end,
501                             prot, flags, fd,
502                             offset + real_end - qemu_host_page_size - start);
503             if (ret == -1)
504                 goto fail;
505             real_end -= qemu_host_page_size;
506         }
507
508         /* map the middle (easier) */
509         if (real_start < real_end) {
510             void *p;
511             unsigned long offset1;
512             if (flags & MAP_ANONYMOUS)
513                 offset1 = 0;
514             else
515                 offset1 = offset + real_start - start;
516             p = mmap(g2h(real_start), real_end - real_start,
517                      prot, flags, fd, offset1);
518             if (p == MAP_FAILED)
519                 goto fail;
520         }
521     }
522  the_end1:
523     page_set_flags(start, start + len, prot | PAGE_VALID);
524  the_end:
525 #ifdef DEBUG_MMAP
526     printf("ret=0x" TARGET_FMT_lx "\n", start);
527     page_dump(stdout);
528     printf("\n");
529 #endif
530     mmap_unlock();
531     return start;
532 fail:
533     mmap_unlock();
534     return -1;
535 }
536
537 int target_munmap(abi_ulong start, abi_ulong len)
538 {
539     abi_ulong end, real_start, real_end, addr;
540     int prot, ret;
541
542 #ifdef DEBUG_MMAP
543     printf("munmap: start=0x%lx len=0x%lx\n", start, len);
544 #endif
545     if (start & ~TARGET_PAGE_MASK)
546         return -EINVAL;
547     len = TARGET_PAGE_ALIGN(len);
548     if (len == 0)
549         return -EINVAL;
550     mmap_lock();
551     end = start + len;
552     real_start = start & qemu_host_page_mask;
553     real_end = HOST_PAGE_ALIGN(end);
554
555     if (start > real_start) {
556         /* handle host page containing start */
557         prot = 0;
558         for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
559             prot |= page_get_flags(addr);
560         }
561         if (real_end == real_start + qemu_host_page_size) {
562             for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
563                 prot |= page_get_flags(addr);
564             }
565             end = real_end;
566         }
567         if (prot != 0)
568             real_start += qemu_host_page_size;
569     }
570     if (end < real_end) {
571         prot = 0;
572         for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
573             prot |= page_get_flags(addr);
574         }
575         if (prot != 0)
576             real_end -= qemu_host_page_size;
577     }
578
579     ret = 0;
580     /* unmap what we can */
581     if (real_start < real_end) {
582         ret = munmap(g2h(real_start), real_end - real_start);
583     }
584
585     if (ret == 0)
586         page_set_flags(start, start + len, 0);
587     mmap_unlock();
588     return ret;
589 }
590
591 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
592                        abi_ulong new_size, unsigned long flags,
593                        abi_ulong new_addr)
594 {
595     int prot;
596     void *host_addr;
597
598     mmap_lock();
599
600     if (flags & MREMAP_FIXED)
601         host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
602                                      old_size, new_size,
603                                      flags,
604                                      new_addr);
605     else if (flags & MREMAP_MAYMOVE) {
606         abi_ulong mmap_start;
607
608         mmap_start = mmap_find_vma(0, new_size);
609
610         if (mmap_start == -1) {
611             errno = ENOMEM;
612             host_addr = MAP_FAILED;
613         } else
614             host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
615                                          old_size, new_size,
616                                          flags | MREMAP_FIXED,
617                                          g2h(mmap_start));
618     } else {
619         host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
620         /* Check if address fits target address space */
621         if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
622             /* Revert mremap() changes */
623             host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
624             errno = ENOMEM;
625             host_addr = MAP_FAILED;
626         }
627     }
628
629     if (host_addr == MAP_FAILED) {
630         new_addr = -1;
631     } else {
632         new_addr = h2g(host_addr);
633         prot = page_get_flags(old_addr);
634         page_set_flags(old_addr, old_addr + old_size, 0);
635         page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
636     }
637     mmap_unlock();
638     return new_addr;
639 }
640
641 int target_msync(abi_ulong start, abi_ulong len, int flags)
642 {
643     abi_ulong end;
644
645     if (start & ~TARGET_PAGE_MASK)
646         return -EINVAL;
647     len = TARGET_PAGE_ALIGN(len);
648     end = start + len;
649     if (end < start)
650         return -EINVAL;
651     if (end == start)
652         return 0;
653
654     start &= qemu_host_page_mask;
655     return msync(g2h(start), end - start, flags);
656 }