Statistics
| Branch: | Revision:

root / linux-user / mmap.c @ 1e6eec8b

History | View | Annotate | Download (19.4 kB)

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(CONFIG_USE_NPTL)
37
static 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_ABI_FMT_lx
149
           "len=0x" TARGET_ABI_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
/*
277
 * Find and reserve a free memory area of size 'size'. The search
278
 * starts at 'start'.
279
 * It must be called with mmap_lock() held.
280
 * Return -1 if error.
281
 */
282
abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
283
{
284
    void *ptr;
285
    abi_ulong addr;
286

    
287
    size = HOST_PAGE_ALIGN(size);
288
    start &= qemu_host_page_mask;
289

    
290
    /* If 'start' == 0, then a default start address is used. */
291
    if (start == 0)
292
        start = mmap_next_start;
293

    
294
    addr = start;
295

    
296
    for(;;) {
297
        /*
298
         * Reserve needed memory area to avoid a race.
299
         * It should be discarded using:
300
         *  - mmap() with MAP_FIXED flag
301
         *  - mremap() with MREMAP_FIXED flag
302
         *  - shmat() with SHM_REMAP flag
303
         */
304
        ptr = mmap((void *)(unsigned long)addr, size, PROT_NONE,
305
                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
306

    
307
        /* ENOMEM, if host address space has no memory */
308
        if (ptr == MAP_FAILED)
309
            return (abi_ulong)-1;
310

    
311
        /* If address fits target address space we've found what we need */
312
        if ((unsigned long)ptr + size - 1 <= (abi_ulong)-1)
313
            break;
314

    
315
        /* Unmap and try again with new page */
316
        munmap(ptr, size);
317
        addr += qemu_host_page_size;
318

    
319
        /* ENOMEM if we check whole of target address space */
320
        if (addr == start)
321
            return (abi_ulong)-1;
322
    }
323

    
324
    /* Update default start address */
325
    if (start == mmap_next_start)
326
        mmap_next_start = (unsigned long)ptr + size;
327

    
328
    return h2g(ptr);
329
}
330

    
331
/* NOTE: all the constants are the HOST ones */
332
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
333
                     int flags, int fd, abi_ulong offset)
334
{
335
    abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
336
    unsigned long host_start;
337

    
338
    mmap_lock();
339
#ifdef DEBUG_MMAP
340
    {
341
        printf("mmap: start=0x" TARGET_ABI_FMT_lx
342
               " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
343
               start, len,
344
               prot & PROT_READ ? 'r' : '-',
345
               prot & PROT_WRITE ? 'w' : '-',
346
               prot & PROT_EXEC ? 'x' : '-');
347
        if (flags & MAP_FIXED)
348
            printf("MAP_FIXED ");
349
        if (flags & MAP_ANONYMOUS)
350
            printf("MAP_ANON ");
351
        switch(flags & MAP_TYPE) {
352
        case MAP_PRIVATE:
353
            printf("MAP_PRIVATE ");
354
            break;
355
        case MAP_SHARED:
356
            printf("MAP_SHARED ");
357
            break;
358
        default:
359
            printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
360
            break;
361
        }
362
        printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
363
    }
364
#endif
365

    
366
    if (offset & ~TARGET_PAGE_MASK) {
367
        errno = EINVAL;
368
        goto fail;
369
    }
370

    
371
    len = TARGET_PAGE_ALIGN(len);
372
    if (len == 0)
373
        goto the_end;
374
    real_start = start & qemu_host_page_mask;
375

    
376
    /* When mapping files into a memory area larger than the file, accesses
377
       to pages beyond the file size will cause a SIGBUS. 
378

379
       For example, if mmaping a file of 100 bytes on a host with 4K pages
380
       emulating a target with 8K pages, the target expects to be able to
381
       access the first 8K. But the host will trap us on any access beyond
382
       4K.  
383

384
       When emulating a target with a larger page-size than the hosts, we
385
       may need to truncate file maps at EOF and add extra anonymous pages
386
       up to the targets page boundary.  */
387

    
388
    if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
389
        && !(flags & MAP_ANONYMOUS)) {
390
       struct stat sb;
391

    
392
       if (fstat (fd, &sb) == -1)
393
           goto fail;
394

    
395
       /* Are we trying to create a map beyond EOF?.  */
396
       if (offset + len > sb.st_size) {
397
           /* If so, truncate the file map at eof aligned with 
398
              the hosts real pagesize. Additional anonymous maps
399
              will be created beyond EOF.  */
400
           len = (sb.st_size - offset);
401
           len += qemu_real_host_page_size - 1;
402
           len &= ~(qemu_real_host_page_size - 1);
403
       }
404
    }
405

    
406
    if (!(flags & MAP_FIXED)) {
407
        abi_ulong mmap_start;
408
        void *p;
409
        host_offset = offset & qemu_host_page_mask;
410
        host_len = len + offset - host_offset;
411
        host_len = HOST_PAGE_ALIGN(host_len);
412
        mmap_start = mmap_find_vma(real_start, host_len);
413
        if (mmap_start == (abi_ulong)-1) {
414
            errno = ENOMEM;
415
            goto fail;
416
        }
417
        /* Note: we prefer to control the mapping address. It is
418
           especially important if qemu_host_page_size >
419
           qemu_real_host_page_size */
420
        p = mmap(g2h(mmap_start),
421
                 host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
422
        if (p == MAP_FAILED)
423
            goto fail;
424
        /* update start so that it points to the file position at 'offset' */
425
        host_start = (unsigned long)p;
426
        if (!(flags & MAP_ANONYMOUS)) {
427
            p = mmap(g2h(mmap_start), len, prot, 
428
                     flags | MAP_FIXED, fd, host_offset);
429
            host_start += offset - host_offset;
430
        }
431
        start = h2g(host_start);
432
    } else {
433
        int flg;
434
        target_ulong addr;
435

    
436
        if (start & ~TARGET_PAGE_MASK) {
437
            errno = EINVAL;
438
            goto fail;
439
        }
440
        end = start + len;
441
        real_end = HOST_PAGE_ALIGN(end);
442

    
443
        /*
444
         * Test if requested memory area fits target address space
445
         * It can fail only on 64-bit host with 32-bit target.
446
         * On any other target/host host mmap() handles this error correctly.
447
         */
448
        if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
449
            errno = EINVAL;
450
            goto fail;
451
        }
452

    
453
        for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
454
            flg = page_get_flags(addr);
455
            if (flg & PAGE_RESERVED) {
456
                errno = ENXIO;
457
                goto fail;
458
            }
459
        }
460

    
461
        /* worst case: we cannot map the file because the offset is not
462
           aligned, so we read it */
463
        if (!(flags & MAP_ANONYMOUS) &&
464
            (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
465
            /* msync() won't work here, so we return an error if write is
466
               possible while it is a shared mapping */
467
            if ((flags & MAP_TYPE) == MAP_SHARED &&
468
                (prot & PROT_WRITE)) {
469
                errno = EINVAL;
470
                goto fail;
471
            }
472
            retaddr = target_mmap(start, len, prot | PROT_WRITE,
473
                                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
474
                                  -1, 0);
475
            if (retaddr == -1)
476
                goto fail;
477
            pread(fd, g2h(start), len, offset);
478
            if (!(prot & PROT_WRITE)) {
479
                ret = target_mprotect(start, len, prot);
480
                if (ret != 0) {
481
                    start = ret;
482
                    goto the_end;
483
                }
484
            }
485
            goto the_end;
486
        }
487
        
488
        /* handle the start of the mapping */
489
        if (start > real_start) {
490
            if (real_end == real_start + qemu_host_page_size) {
491
                /* one single host page */
492
                ret = mmap_frag(real_start, start, end,
493
                                prot, flags, fd, offset);
494
                if (ret == -1)
495
                    goto fail;
496
                goto the_end1;
497
            }
498
            ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
499
                            prot, flags, fd, offset);
500
            if (ret == -1)
501
                goto fail;
502
            real_start += qemu_host_page_size;
503
        }
504
        /* handle the end of the mapping */
505
        if (end < real_end) {
506
            ret = mmap_frag(real_end - qemu_host_page_size,
507
                            real_end - qemu_host_page_size, real_end,
508
                            prot, flags, fd,
509
                            offset + real_end - qemu_host_page_size - start);
510
            if (ret == -1)
511
                goto fail;
512
            real_end -= qemu_host_page_size;
513
        }
514

    
515
        /* map the middle (easier) */
516
        if (real_start < real_end) {
517
            void *p;
518
            unsigned long offset1;
519
            if (flags & MAP_ANONYMOUS)
520
                offset1 = 0;
521
            else
522
                offset1 = offset + real_start - start;
523
            p = mmap(g2h(real_start), real_end - real_start,
524
                     prot, flags, fd, offset1);
525
            if (p == MAP_FAILED)
526
                goto fail;
527
        }
528
    }
529
 the_end1:
530
    page_set_flags(start, start + len, prot | PAGE_VALID);
531
 the_end:
532
#ifdef DEBUG_MMAP
533
    printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
534
    page_dump(stdout);
535
    printf("\n");
536
#endif
537
    mmap_unlock();
538
    return start;
539
fail:
540
    mmap_unlock();
541
    return -1;
542
}
543

    
544
int target_munmap(abi_ulong start, abi_ulong len)
545
{
546
    abi_ulong end, real_start, real_end, addr;
547
    int prot, ret;
548

    
549
#ifdef DEBUG_MMAP
550
    printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
551
           TARGET_ABI_FMT_lx "\n",
552
           start, len);
553
#endif
554
    if (start & ~TARGET_PAGE_MASK)
555
        return -EINVAL;
556
    len = TARGET_PAGE_ALIGN(len);
557
    if (len == 0)
558
        return -EINVAL;
559
    mmap_lock();
560
    end = start + len;
561
    real_start = start & qemu_host_page_mask;
562
    real_end = HOST_PAGE_ALIGN(end);
563

    
564
    if (start > real_start) {
565
        /* handle host page containing start */
566
        prot = 0;
567
        for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
568
            prot |= page_get_flags(addr);
569
        }
570
        if (real_end == real_start + qemu_host_page_size) {
571
            for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
572
                prot |= page_get_flags(addr);
573
            }
574
            end = real_end;
575
        }
576
        if (prot != 0)
577
            real_start += qemu_host_page_size;
578
    }
579
    if (end < real_end) {
580
        prot = 0;
581
        for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
582
            prot |= page_get_flags(addr);
583
        }
584
        if (prot != 0)
585
            real_end -= qemu_host_page_size;
586
    }
587

    
588
    ret = 0;
589
    /* unmap what we can */
590
    if (real_start < real_end) {
591
        ret = munmap(g2h(real_start), real_end - real_start);
592
    }
593

    
594
    if (ret == 0)
595
        page_set_flags(start, start + len, 0);
596
    mmap_unlock();
597
    return ret;
598
}
599

    
600
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
601
                       abi_ulong new_size, unsigned long flags,
602
                       abi_ulong new_addr)
603
{
604
    int prot;
605
    void *host_addr;
606

    
607
    mmap_lock();
608

    
609
    if (flags & MREMAP_FIXED)
610
        host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
611
                                     old_size, new_size,
612
                                     flags,
613
                                     new_addr);
614
    else if (flags & MREMAP_MAYMOVE) {
615
        abi_ulong mmap_start;
616

    
617
        mmap_start = mmap_find_vma(0, new_size);
618

    
619
        if (mmap_start == -1) {
620
            errno = ENOMEM;
621
            host_addr = MAP_FAILED;
622
        } else
623
            host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
624
                                         old_size, new_size,
625
                                         flags | MREMAP_FIXED,
626
                                         g2h(mmap_start));
627
    } else {
628
        host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
629
        /* Check if address fits target address space */
630
        if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
631
            /* Revert mremap() changes */
632
            host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
633
            errno = ENOMEM;
634
            host_addr = MAP_FAILED;
635
        }
636
    }
637

    
638
    if (host_addr == MAP_FAILED) {
639
        new_addr = -1;
640
    } else {
641
        new_addr = h2g(host_addr);
642
        prot = page_get_flags(old_addr);
643
        page_set_flags(old_addr, old_addr + old_size, 0);
644
        page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
645
    }
646
    mmap_unlock();
647
    return new_addr;
648
}
649

    
650
int target_msync(abi_ulong start, abi_ulong len, int flags)
651
{
652
    abi_ulong end;
653

    
654
    if (start & ~TARGET_PAGE_MASK)
655
        return -EINVAL;
656
    len = TARGET_PAGE_ALIGN(len);
657
    end = start + len;
658
    if (end < start)
659
        return -EINVAL;
660
    if (end == start)
661
        return 0;
662

    
663
    start &= qemu_host_page_mask;
664
    return msync(g2h(start), end - start, flags);
665
}