]>
Commit | Line | Data |
---|---|---|
794e8f30 MT |
1 | /* |
2 | * Support for RAM backed by mmaped host memory. | |
3 | * | |
4 | * Copyright (c) 2015 Red Hat, Inc. | |
5 | * | |
6 | * Authors: | |
7 | * Michael S. Tsirkin <[email protected]> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or | |
10 | * later. See the COPYING file in the top-level directory. | |
11 | */ | |
a9c94277 | 12 | |
aafd7584 | 13 | #include "qemu/osdep.h" |
a9c94277 | 14 | #include "qemu/mmap-alloc.h" |
794e8f30 | 15 | |
7197fb40 MT |
16 | #define HUGETLBFS_MAGIC 0x958458f6 |
17 | ||
18 | #ifdef CONFIG_LINUX | |
19 | #include <sys/vfs.h> | |
20 | #endif | |
21 | ||
22 | size_t qemu_fd_getpagesize(int fd) | |
23 | { | |
24 | #ifdef CONFIG_LINUX | |
25 | struct statfs fs; | |
26 | int ret; | |
27 | ||
28 | if (fd != -1) { | |
29 | do { | |
30 | ret = fstatfs(fd, &fs); | |
31 | } while (ret != 0 && errno == EINTR); | |
32 | ||
33 | if (ret == 0 && fs.f_type == HUGETLBFS_MAGIC) { | |
34 | return fs.f_bsize; | |
35 | } | |
36 | } | |
37 | #endif | |
38 | ||
39 | return getpagesize(); | |
40 | } | |
41 | ||
794e8f30 MT |
42 | void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) |
43 | { | |
44 | /* | |
45 | * Note: this always allocates at least one extra page of virtual address | |
46 | * space, even if size is already aligned. | |
47 | */ | |
48 | size_t total = size + align; | |
7197fb40 MT |
49 | #if defined(__powerpc64__) && defined(__linux__) |
50 | /* On ppc64 mappings in the same segment (aka slice) must share the same | |
51 | * page size. Since we will be re-allocating part of this segment | |
097a50d0 MT |
52 | * from the supplied fd, we should make sure to use the same page size, to |
53 | * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to | |
54 | * avoid allocating backing store memory. | |
55 | * We do this unless we are using the system page size, in which case | |
56 | * anonymous memory is OK. | |
7197fb40 MT |
57 | */ |
58 | int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; | |
59 | int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; | |
60 | void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0); | |
61 | #else | |
794e8f30 | 62 | void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
7197fb40 | 63 | #endif |
794e8f30 MT |
64 | size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; |
65 | void *ptr1; | |
66 | ||
67 | if (ptr == MAP_FAILED) { | |
9d4ec937 | 68 | return MAP_FAILED; |
794e8f30 MT |
69 | } |
70 | ||
71 | /* Make sure align is a power of 2 */ | |
72 | assert(!(align & (align - 1))); | |
73 | /* Always align to host page size */ | |
74 | assert(align >= getpagesize()); | |
75 | ||
76 | ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE, | |
77 | MAP_FIXED | | |
78 | (fd == -1 ? MAP_ANONYMOUS : 0) | | |
79 | (shared ? MAP_SHARED : MAP_PRIVATE), | |
80 | fd, 0); | |
81 | if (ptr1 == MAP_FAILED) { | |
82 | munmap(ptr, total); | |
9d4ec937 | 83 | return MAP_FAILED; |
794e8f30 MT |
84 | } |
85 | ||
86 | ptr += offset; | |
87 | total -= offset; | |
88 | ||
89 | if (offset > 0) { | |
90 | munmap(ptr - offset, offset); | |
91 | } | |
92 | ||
93 | /* | |
94 | * Leave a single PROT_NONE page allocated after the RAM block, to serve as | |
95 | * a guard page guarding against potential buffer overflows. | |
96 | */ | |
97 | if (total > size + getpagesize()) { | |
98 | munmap(ptr + size + getpagesize(), total - size - getpagesize()); | |
99 | } | |
100 | ||
101 | return ptr; | |
102 | } | |
103 | ||
104 | void qemu_ram_munmap(void *ptr, size_t size) | |
105 | { | |
106 | if (ptr) { | |
107 | /* Unmap both the RAM block and the guard page */ | |
108 | munmap(ptr, size + getpagesize()); | |
109 | } | |
110 | } |