Statistics
| Branch: | Revision:

root / linux-user / mmap.c @ df377d03

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, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19
 *  MA 02110-1301, USA.
20
 */
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <stdarg.h>
24
#include <string.h>
25
#include <unistd.h>
26
#include <errno.h>
27
#include <sys/types.h>
28
#include <sys/stat.h>
29
#include <sys/mman.h>
30
#include <linux/mman.h>
31
#include <linux/unistd.h>
32

    
33
#include "qemu.h"
34
#include "qemu-common.h"
35

    
36
//#define DEBUG_MMAP
37

    
38
#if defined(USE_NPTL)
39
pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
40
static int __thread mmap_lock_count;
41

    
42
void mmap_lock(void)
43
{
44
    if (mmap_lock_count++ == 0) {
45
        pthread_mutex_lock(&mmap_mutex);
46
    }
47
}
48

    
49
void mmap_unlock(void)
50
{
51
    if (--mmap_lock_count == 0) {
52
        pthread_mutex_unlock(&mmap_mutex);
53
    }
54
}
55

    
56
/* Grab lock to make sure things are in a consistent state after fork().  */
57
void mmap_fork_start(void)
58
{
59
    if (mmap_lock_count)
60
        abort();
61
    pthread_mutex_lock(&mmap_mutex);
62
}
63

    
64
void mmap_fork_end(int child)
65
{
66
    if (child)
67
        pthread_mutex_init(&mmap_mutex, NULL);
68
    else
69
        pthread_mutex_unlock(&mmap_mutex);
70
}
71
#else
72
/* We aren't threadsafe to start with, so no need to worry about locking.  */
73
void mmap_lock(void)
74
{
75
}
76

    
77
void mmap_unlock(void)
78
{
79
}
80
#endif
81

    
82
void *qemu_vmalloc(size_t size)
83
{
84
    void *p;
85
    unsigned long addr;
86
    mmap_lock();
87
    /* Use map and mark the pages as used.  */
88
    p = mmap(NULL, size, PROT_READ | PROT_WRITE,
89
             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
90

    
91
    addr = (unsigned long)p;
92
    if (addr == (target_ulong) addr) {
93
        /* Allocated region overlaps guest address space.
94
           This may recurse.  */
95
        page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
96
                       PAGE_RESERVED);
97
    }
98

    
99
    mmap_unlock();
100
    return p;
101
}
102

    
103
void *qemu_malloc(size_t size)
104
{
105
    char * p;
106
    size += 16;
107
    p = qemu_vmalloc(size);
108
    *(size_t *)p = size;
109
    return p + 16;
110
}
111

    
112
/* We use map, which is always zero initialized.  */
113
void * qemu_mallocz(size_t size)
114
{
115
    return qemu_malloc(size);
116
}
117

    
118
void qemu_free(void *ptr)
119
{
120
    /* FIXME: We should unmark the reserved pages here.  However this gets
121
       complicated when one target page spans multiple host pages, so we
122
       don't bother.  */
123
    size_t *p;
124
    p = (size_t *)((char *)ptr - 16);
125
    munmap(p, *p);
126
}
127

    
128
void *qemu_realloc(void *ptr, size_t size)
129
{
130
    size_t old_size, copy;
131
    void *new_ptr;
132

    
133
    if (!ptr)
134
        return qemu_malloc(size);
135
    old_size = *(size_t *)((char *)ptr - 16);
136
    copy = old_size < size ? old_size : size;
137
    new_ptr = qemu_malloc(size);
138
    memcpy(new_ptr, ptr, copy);
139
    qemu_free(ptr);
140
    return new_ptr;
141
}
142

    
143
/* NOTE: all the constants are the HOST ones, but addresses are target. */
144
int target_mprotect(abi_ulong start, abi_ulong len, int prot)
145
{
146
    abi_ulong end, host_start, host_end, addr;
147
    int prot1, ret;
148

    
149
#ifdef DEBUG_MMAP
150
    printf("mprotect: start=0x" TARGET_FMT_lx
151
           "len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
152
           prot & PROT_READ ? 'r' : '-',
153
           prot & PROT_WRITE ? 'w' : '-',
154
           prot & PROT_EXEC ? 'x' : '-');
155
#endif
156

    
157
    if ((start & ~TARGET_PAGE_MASK) != 0)
158
        return -EINVAL;
159
    len = TARGET_PAGE_ALIGN(len);
160
    end = start + len;
161
    if (end < start)
162
        return -EINVAL;
163
    prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
164
    if (len == 0)
165
        return 0;
166

    
167
    mmap_lock();
168
    host_start = start & qemu_host_page_mask;
169
    host_end = HOST_PAGE_ALIGN(end);
170
    if (start > host_start) {
171
        /* handle host page containing start */
172
        prot1 = prot;
173
        for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
174
            prot1 |= page_get_flags(addr);
175
        }
176
        if (host_end == host_start + qemu_host_page_size) {
177
            for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
178
                prot1 |= page_get_flags(addr);
179
            }
180
            end = host_end;
181
        }
182
        ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
183
        if (ret != 0)
184
            goto error;
185
        host_start += qemu_host_page_size;
186
    }
187
    if (end < host_end) {
188
        prot1 = prot;
189
        for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
190
            prot1 |= page_get_flags(addr);
191
        }
192
        ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
193
                       prot1 & PAGE_BITS);
194
        if (ret != 0)
195
            goto error;
196
        host_end -= qemu_host_page_size;
197
    }
198

    
199
    /* handle the pages in the middle */
200
    if (host_start < host_end) {
201
        ret = mprotect(g2h(host_start), host_end - host_start, prot);
202
        if (ret != 0)
203
            goto error;
204
    }
205
    page_set_flags(start, start + len, prot | PAGE_VALID);
206
    mmap_unlock();
207
    return 0;
208
error:
209
    mmap_unlock();
210
    return ret;
211
}
212

    
213
/* map an incomplete host page */
214
static int mmap_frag(abi_ulong real_start,
215
                     abi_ulong start, abi_ulong end,
216
                     int prot, int flags, int fd, abi_ulong offset)
217
{
218
    abi_ulong real_end, addr;
219
    void *host_start;
220
    int prot1, prot_new;
221

    
222
    real_end = real_start + qemu_host_page_size;
223
    host_start = g2h(real_start);
224

    
225
    /* get the protection of the target pages outside the mapping */
226
    prot1 = 0;
227
    for(addr = real_start; addr < real_end; addr++) {
228
        if (addr < start || addr >= end)
229
            prot1 |= page_get_flags(addr);
230
    }
231

    
232
    if (prot1 == 0) {
233
        /* no page was there, so we allocate one */
234
        void *p = mmap(host_start, qemu_host_page_size, prot,
235
                       flags | MAP_ANONYMOUS, -1, 0);
236
        if (p == MAP_FAILED)
237
            return -1;
238
        prot1 = prot;
239
    }
240
    prot1 &= PAGE_BITS;
241

    
242
    prot_new = prot | prot1;
243
    if (!(flags & MAP_ANONYMOUS)) {
244
        /* msync() won't work here, so we return an error if write is
245
           possible while it is a shared mapping */
246
        if ((flags & MAP_TYPE) == MAP_SHARED &&
247
            (prot & PROT_WRITE))
248
            return -EINVAL;
249

    
250
        /* adjust protection to be able to read */
251
        if (!(prot1 & PROT_WRITE))
252
            mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
253

    
254
        /* read the corresponding file data */
255
        pread(fd, g2h(start), end - start, offset);
256

    
257
        /* put final protection */
258
        if (prot_new != (prot1 | PROT_WRITE))
259
            mprotect(host_start, qemu_host_page_size, prot_new);
260
    } else {
261
        /* just update the protection */
262
        if (prot_new != prot1) {
263
            mprotect(host_start, qemu_host_page_size, prot_new);
264
        }
265
    }
266
    return 0;
267
}
268

    
269
#if defined(__CYGWIN__)
270
/* Cygwin doesn't have a whole lot of address space.  */
271
static abi_ulong mmap_next_start = 0x18000000;
272
#else
273
static abi_ulong mmap_next_start = 0x40000000;
274
#endif
275

    
276
unsigned long last_brk;
277

    
278
/* find a free memory area of size 'size'. The search starts at
279
   'start'. If 'start' == 0, then a default start address is used.
280
   Return -1 if error.
281
*/
282
/* page_init() marks pages used by the host as reserved to be sure not
283
   to use them. */
284
abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
285
{
286
    abi_ulong addr, addr1, addr_start;
287
    int prot;
288
    unsigned long new_brk;
289

    
290
    new_brk = (unsigned long)sbrk(0);
291
    if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
292
        /* This is a hack to catch the host allocating memory with brk().
293
           If it uses mmap then we loose.
294
           FIXME: We really want to avoid the host allocating memory in
295
           the first place, and maybe leave some slack to avoid switching
296
           to mmap.  */
297
        page_set_flags(last_brk & TARGET_PAGE_MASK,
298
                       TARGET_PAGE_ALIGN(new_brk),
299
                       PAGE_RESERVED); 
300
    }
301
    last_brk = new_brk;
302

    
303
    size = HOST_PAGE_ALIGN(size);
304
    start = start & qemu_host_page_mask;
305
    addr = start;
306
    if (addr == 0)
307
        addr = mmap_next_start;
308
    addr_start = addr;
309
    for(;;) {
310
        prot = 0;
311
        for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
312
            prot |= page_get_flags(addr1);
313
        }
314
        if (prot == 0)
315
            break;
316
        addr += qemu_host_page_size;
317
        /* we found nothing */
318
        if (addr == addr_start)
319
            return (abi_ulong)-1;
320
    }
321
    if (start == 0)
322
        mmap_next_start = addr + size;
323
    return addr;
324
}
325

    
326
/* NOTE: all the constants are the HOST ones */
327
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
328
                     int flags, int fd, abi_ulong offset)
329
{
330
    abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
331
    unsigned long host_start;
332

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

    
361
    if (offset & ~TARGET_PAGE_MASK) {
362
        errno = EINVAL;
363
        goto fail;
364
    }
365

    
366
    len = TARGET_PAGE_ALIGN(len);
367
    if (len == 0)
368
        goto the_end;
369
    real_start = start & qemu_host_page_mask;
370

    
371
    /* When mapping files into a memory area larger than the file, accesses
372
       to pages beyond the file size will cause a SIGBUS. 
373

374
       For example, if mmaping a file of 100 bytes on a host with 4K pages
375
       emulating a target with 8K pages, the target expects to be able to
376
       access the first 8K. But the host will trap us on any access beyond
377
       4K.  
378

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

    
383
    if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
384
        && !(flags & MAP_ANONYMOUS)) {
385
       struct stat sb;
386

    
387
       if (fstat (fd, &sb) == -1)
388
           goto fail;
389

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

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

    
431
        if (start & ~TARGET_PAGE_MASK) {
432
            errno = EINVAL;
433
            goto fail;
434
        }
435
        end = start + len;
436
        real_end = HOST_PAGE_ALIGN(end);
437

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

    
448
        for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
449
            flg = page_get_flags(addr);
450
            if (flg & PAGE_RESERVED) {
451
                errno = ENXIO;
452
                goto fail;
453
            }
454
        }
455

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

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

    
539
int target_munmap(abi_ulong start, abi_ulong len)
540
{
541
    abi_ulong end, real_start, real_end, addr;
542
    int prot, ret;
543

    
544
#ifdef DEBUG_MMAP
545
    printf("munmap: start=0x%lx len=0x%lx\n", start, len);
546
#endif
547
    if (start & ~TARGET_PAGE_MASK)
548
        return -EINVAL;
549
    len = TARGET_PAGE_ALIGN(len);
550
    if (len == 0)
551
        return -EINVAL;
552
    mmap_lock();
553
    end = start + len;
554
    real_start = start & qemu_host_page_mask;
555
    real_end = HOST_PAGE_ALIGN(end);
556

    
557
    if (start > real_start) {
558
        /* handle host page containing start */
559
        prot = 0;
560
        for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
561
            prot |= page_get_flags(addr);
562
        }
563
        if (real_end == real_start + qemu_host_page_size) {
564
            for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
565
                prot |= page_get_flags(addr);
566
            }
567
            end = real_end;
568
        }
569
        if (prot != 0)
570
            real_start += qemu_host_page_size;
571
    }
572
    if (end < real_end) {
573
        prot = 0;
574
        for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
575
            prot |= page_get_flags(addr);
576
        }
577
        if (prot != 0)
578
            real_end -= qemu_host_page_size;
579
    }
580

    
581
    ret = 0;
582
    /* unmap what we can */
583
    if (real_start < real_end) {
584
        ret = munmap(g2h(real_start), real_end - real_start);
585
    }
586

    
587
    if (ret == 0)
588
        page_set_flags(start, start + len, 0);
589
    mmap_unlock();
590
    return ret;
591
}
592

    
593
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
594
                       abi_ulong new_size, unsigned long flags,
595
                       abi_ulong new_addr)
596
{
597
    int prot;
598
    void *host_addr;
599

    
600
    mmap_lock();
601

    
602
    if (flags & MREMAP_FIXED)
603
        host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
604
                                     old_size, new_size,
605
                                     flags,
606
                                     new_addr);
607
    else if (flags & MREMAP_MAYMOVE) {
608
        abi_ulong mmap_start;
609

    
610
        mmap_start = mmap_find_vma(0, new_size);
611

    
612
        if (mmap_start == -1) {
613
            errno = ENOMEM;
614
            host_addr = MAP_FAILED;
615
        } else
616
            host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
617
                                         old_size, new_size,
618
                                         flags | MREMAP_FIXED,
619
                                         g2h(mmap_start));
620
    } else {
621
        host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
622
        /* Check if address fits target address space */
623
        if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
624
            /* Revert mremap() changes */
625
            host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
626
            errno = ENOMEM;
627
            host_addr = MAP_FAILED;
628
        }
629
    }
630

    
631
    if (host_addr == MAP_FAILED) {
632
        new_addr = -1;
633
    } else {
634
        new_addr = h2g(host_addr);
635
        prot = page_get_flags(old_addr);
636
        page_set_flags(old_addr, old_addr + old_size, 0);
637
        page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
638
    }
639
    mmap_unlock();
640
    return new_addr;
641
}
642

    
643
int target_msync(abi_ulong start, abi_ulong len, int flags)
644
{
645
    abi_ulong end;
646

    
647
    if (start & ~TARGET_PAGE_MASK)
648
        return -EINVAL;
649
    len = TARGET_PAGE_ALIGN(len);
650
    end = start + len;
651
    if (end < start)
652
        return -EINVAL;
653
    if (end == start)
654
        return 0;
655

    
656
    start &= qemu_host_page_mask;
657
    return msync(g2h(start), end - start, flags);
658
}