1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 *
4 * Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
5 * Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
6 */
7
8 #include <stdio.h>
9 #include <sys/mman.h>
10 #include <string.h>
11
12 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
13
14 #ifdef __powerpc64__
15 #define PAGE_SIZE (64 << 10)
16 /*
17 * This will work with 16M and 2M hugepage size
18 */
19 #define HUGETLB_SIZE (16 << 20)
20 #else
21 #define PAGE_SIZE (4 << 10)
22 #define HUGETLB_SIZE (2 << 20)
23 #endif
24
25 /*
26 * >= 128TB is the hint addr value we used to select
27 * large address space.
28 */
29 #define ADDR_SWITCH_HINT (1UL << 47)
30 #define LOW_ADDR ((void *) (1UL << 30))
31 #define HIGH_ADDR ((void *) (1UL << 48))
32
33 struct testcase {
34 void *addr;
35 unsigned long size;
36 unsigned long flags;
37 const char *msg;
38 unsigned int low_addr_required:1;
39 unsigned int keep_mapped:1;
40 };
41
42 static struct testcase testcases[] = {
43 {
44 /*
45 * If stack is moved, we could possibly allocate
46 * this at the requested address.
47 */
48 .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
49 .size = PAGE_SIZE,
50 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
51 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
52 .low_addr_required = 1,
53 },
54 {
55 /*
56 * We should never allocate at the requested address or above it
57 * The len cross the 128TB boundary. Without MAP_FIXED
58 * we will always search in the lower address space.
59 */
60 .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
61 .size = 2 * PAGE_SIZE,
62 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
63 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, (2 * PAGE_SIZE))",
64 .low_addr_required = 1,
65 },
66 {
67 /*
68 * Exact mapping at 128TB, the area is free we should get that
69 * even without MAP_FIXED.
70 */
71 .addr = ((void *)(ADDR_SWITCH_HINT)),
72 .size = PAGE_SIZE,
73 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
74 .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
75 .keep_mapped = 1,
76 },
77 {
78 .addr = (void *)(ADDR_SWITCH_HINT),
79 .size = 2 * PAGE_SIZE,
80 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
81 .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
82 },
83 {
84 .addr = NULL,
85 .size = 2 * PAGE_SIZE,
86 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
87 .msg = "mmap(NULL)",
88 .low_addr_required = 1,
89 },
90 {
91 .addr = LOW_ADDR,
92 .size = 2 * PAGE_SIZE,
93 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
94 .msg = "mmap(LOW_ADDR)",
95 .low_addr_required = 1,
96 },
97 {
98 .addr = HIGH_ADDR,
99 .size = 2 * PAGE_SIZE,
100 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
101 .msg = "mmap(HIGH_ADDR)",
102 .keep_mapped = 1,
103 },
104 {
105 .addr = HIGH_ADDR,
106 .size = 2 * PAGE_SIZE,
107 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
108 .msg = "mmap(HIGH_ADDR) again",
109 .keep_mapped = 1,
110 },
111 {
112 .addr = HIGH_ADDR,
113 .size = 2 * PAGE_SIZE,
114 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
115 .msg = "mmap(HIGH_ADDR, MAP_FIXED)",
116 },
117 {
118 .addr = (void *) -1,
119 .size = 2 * PAGE_SIZE,
120 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
121 .msg = "mmap(-1)",
122 .keep_mapped = 1,
123 },
124 {
125 .addr = (void *) -1,
126 .size = 2 * PAGE_SIZE,
127 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
128 .msg = "mmap(-1) again",
129 },
130 {
131 .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
132 .size = PAGE_SIZE,
133 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
134 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
135 .low_addr_required = 1,
136 },
137 {
138 .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
139 .size = 2 * PAGE_SIZE,
140 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
141 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2 * PAGE_SIZE)",
142 .low_addr_required = 1,
143 .keep_mapped = 1,
144 },
145 {
146 .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE / 2),
147 .size = 2 * PAGE_SIZE,
148 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
149 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE/2 , 2 * PAGE_SIZE)",
150 .low_addr_required = 1,
151 .keep_mapped = 1,
152 },
153 {
154 .addr = ((void *)(ADDR_SWITCH_HINT)),
155 .size = PAGE_SIZE,
156 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
157 .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
158 },
159 {
160 .addr = (void *)(ADDR_SWITCH_HINT),
161 .size = 2 * PAGE_SIZE,
162 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
163 .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
164 },
165 };
166
167 static struct testcase hugetlb_testcases[] = {
168 {
169 .addr = NULL,
170 .size = HUGETLB_SIZE,
171 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
172 .msg = "mmap(NULL, MAP_HUGETLB)",
173 .low_addr_required = 1,
174 },
175 {
176 .addr = LOW_ADDR,
177 .size = HUGETLB_SIZE,
178 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
179 .msg = "mmap(LOW_ADDR, MAP_HUGETLB)",
180 .low_addr_required = 1,
181 },
182 {
183 .addr = HIGH_ADDR,
184 .size = HUGETLB_SIZE,
185 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
186 .msg = "mmap(HIGH_ADDR, MAP_HUGETLB)",
187 .keep_mapped = 1,
188 },
189 {
190 .addr = HIGH_ADDR,
191 .size = HUGETLB_SIZE,
192 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
193 .msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again",
194 .keep_mapped = 1,
195 },
196 {
197 .addr = HIGH_ADDR,
198 .size = HUGETLB_SIZE,
199 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
200 .msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)",
201 },
202 {
203 .addr = (void *) -1,
204 .size = HUGETLB_SIZE,
205 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
206 .msg = "mmap(-1, MAP_HUGETLB)",
207 .keep_mapped = 1,
208 },
209 {
210 .addr = (void *) -1,
211 .size = HUGETLB_SIZE,
212 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
213 .msg = "mmap(-1, MAP_HUGETLB) again",
214 },
215 {
216 .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
217 .size = 2 * HUGETLB_SIZE,
218 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
219 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2*HUGETLB_SIZE, MAP_HUGETLB)",
220 .low_addr_required = 1,
221 .keep_mapped = 1,
222 },
223 {
224 .addr = (void *)(ADDR_SWITCH_HINT),
225 .size = 2 * HUGETLB_SIZE,
226 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
227 .msg = "mmap(ADDR_SWITCH_HINT , 2*HUGETLB_SIZE, MAP_FIXED | MAP_HUGETLB)",
228 },
229 };
230
run_test(struct testcase * test,int count)231 static int run_test(struct testcase *test, int count)
232 {
233 void *p;
234 int i, ret = 0;
235
236 for (i = 0; i < count; i++) {
237 struct testcase *t = test + i;
238
239 p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
240
241 printf("%s: %p - ", t->msg, p);
242
243 if (p == MAP_FAILED) {
244 printf("FAILED\n");
245 ret = 1;
246 continue;
247 }
248
249 if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) {
250 printf("FAILED\n");
251 ret = 1;
252 } else {
253 /*
254 * Do a dereference of the address returned so that we catch
255 * bugs in page fault handling
256 */
257 memset(p, 0, t->size);
258 printf("OK\n");
259 }
260 if (!t->keep_mapped)
261 munmap(p, t->size);
262 }
263
264 return ret;
265 }
266
supported_arch(void)267 static int supported_arch(void)
268 {
269 #if defined(__powerpc64__)
270 return 1;
271 #elif defined(__x86_64__)
272 return 1;
273 #else
274 return 0;
275 #endif
276 }
277
main(int argc,char ** argv)278 int main(int argc, char **argv)
279 {
280 int ret;
281
282 if (!supported_arch())
283 return 0;
284
285 ret = run_test(testcases, ARRAY_SIZE(testcases));
286 if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
287 ret = run_test(hugetlb_testcases, ARRAY_SIZE(hugetlb_testcases));
288 return ret;
289 }
290