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 #include "vm_util.h"
13 #include "../kselftest.h"
14
15 /*
16 * The hint addr value is used to allocate addresses
17 * beyond the high address switch boundary.
18 */
19
20 #define ADDR_MARK_128TB (1UL << 47)
21 #define ADDR_MARK_256TB (1UL << 48)
22
23 #define HIGH_ADDR_128TB (1UL << 48)
24 #define HIGH_ADDR_256TB (1UL << 49)
25
26 struct testcase {
27 void *addr;
28 unsigned long size;
29 unsigned long flags;
30 const char *msg;
31 unsigned int low_addr_required:1;
32 unsigned int keep_mapped:1;
33 };
34
35 static struct testcase *testcases;
36 static struct testcase *hugetlb_testcases;
37 static int sz_testcases, sz_hugetlb_testcases;
38 static unsigned long switch_hint;
39
40 /* Initialize testcases inside a function to compute parameters at runtime */
testcases_init(void)41 void testcases_init(void)
42 {
43 unsigned long pagesize = getpagesize();
44 unsigned long hugepagesize = default_huge_page_size();
45 unsigned long low_addr = (1UL << 30);
46 unsigned long addr_switch_hint = ADDR_MARK_128TB;
47 unsigned long high_addr = HIGH_ADDR_128TB;
48
49 #ifdef __aarch64__
50
51 /* Post LPA2, the lower userspace VA on a 16K pagesize is 47 bits. */
52 if (pagesize != (16UL << 10)) {
53 addr_switch_hint = ADDR_MARK_256TB;
54 high_addr = HIGH_ADDR_256TB;
55 }
56 #endif
57
58 struct testcase t[] = {
59 {
60 /*
61 * If stack is moved, we could possibly allocate
62 * this at the requested address.
63 */
64 .addr = ((void *)(addr_switch_hint - pagesize)),
65 .size = pagesize,
66 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
67 .msg = "mmap(addr_switch_hint - pagesize, pagesize)",
68 .low_addr_required = 1,
69 },
70 {
71 /*
72 * Unless MAP_FIXED is specified, allocation based on hint
73 * addr is never at requested address or above it, which is
74 * beyond high address switch boundary in this case. Instead,
75 * a suitable allocation is found in lower address space.
76 */
77 .addr = ((void *)(addr_switch_hint - pagesize)),
78 .size = 2 * pagesize,
79 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
80 .msg = "mmap(addr_switch_hint - pagesize, (2 * pagesize))",
81 .low_addr_required = 1,
82 },
83 {
84 /*
85 * Exact mapping at high address switch boundary, should
86 * be obtained even without MAP_FIXED as area is free.
87 */
88 .addr = ((void *)(addr_switch_hint)),
89 .size = pagesize,
90 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
91 .msg = "mmap(addr_switch_hint, pagesize)",
92 .keep_mapped = 1,
93 },
94 {
95 .addr = (void *)(addr_switch_hint),
96 .size = 2 * pagesize,
97 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
98 .msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)",
99 },
100 {
101 .addr = NULL,
102 .size = 2 * pagesize,
103 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
104 .msg = "mmap(NULL)",
105 .low_addr_required = 1,
106 },
107 {
108 .addr = (void *)low_addr,
109 .size = 2 * pagesize,
110 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
111 .msg = "mmap(low_addr)",
112 .low_addr_required = 1,
113 },
114 {
115 .addr = (void *)high_addr,
116 .size = 2 * pagesize,
117 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
118 .msg = "mmap(high_addr)",
119 .keep_mapped = 1,
120 },
121 {
122 .addr = (void *)high_addr,
123 .size = 2 * pagesize,
124 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
125 .msg = "mmap(high_addr) again",
126 .keep_mapped = 1,
127 },
128 {
129 .addr = (void *)high_addr,
130 .size = 2 * pagesize,
131 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
132 .msg = "mmap(high_addr, MAP_FIXED)",
133 },
134 {
135 .addr = (void *) -1,
136 .size = 2 * pagesize,
137 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
138 .msg = "mmap(-1)",
139 .keep_mapped = 1,
140 },
141 {
142 .addr = (void *) -1,
143 .size = 2 * pagesize,
144 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
145 .msg = "mmap(-1) again",
146 },
147 {
148 .addr = ((void *)(addr_switch_hint - pagesize)),
149 .size = pagesize,
150 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
151 .msg = "mmap(addr_switch_hint - pagesize, pagesize)",
152 .low_addr_required = 1,
153 },
154 {
155 .addr = (void *)(addr_switch_hint - pagesize),
156 .size = 2 * pagesize,
157 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
158 .msg = "mmap(addr_switch_hint - pagesize, 2 * pagesize)",
159 .low_addr_required = 1,
160 .keep_mapped = 1,
161 },
162 {
163 .addr = (void *)(addr_switch_hint - pagesize / 2),
164 .size = 2 * pagesize,
165 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
166 .msg = "mmap(addr_switch_hint - pagesize/2 , 2 * pagesize)",
167 .low_addr_required = 1,
168 .keep_mapped = 1,
169 },
170 {
171 .addr = ((void *)(addr_switch_hint)),
172 .size = pagesize,
173 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
174 .msg = "mmap(addr_switch_hint, pagesize)",
175 },
176 {
177 .addr = (void *)(addr_switch_hint),
178 .size = 2 * pagesize,
179 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
180 .msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)",
181 },
182 };
183
184 struct testcase ht[] = {
185 {
186 .addr = NULL,
187 .size = hugepagesize,
188 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
189 .msg = "mmap(NULL, MAP_HUGETLB)",
190 .low_addr_required = 1,
191 },
192 {
193 .addr = (void *)low_addr,
194 .size = hugepagesize,
195 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
196 .msg = "mmap(low_addr, MAP_HUGETLB)",
197 .low_addr_required = 1,
198 },
199 {
200 .addr = (void *)high_addr,
201 .size = hugepagesize,
202 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
203 .msg = "mmap(high_addr, MAP_HUGETLB)",
204 .keep_mapped = 1,
205 },
206 {
207 .addr = (void *)high_addr,
208 .size = hugepagesize,
209 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
210 .msg = "mmap(high_addr, MAP_HUGETLB) again",
211 .keep_mapped = 1,
212 },
213 {
214 .addr = (void *)high_addr,
215 .size = hugepagesize,
216 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
217 .msg = "mmap(high_addr, MAP_FIXED | MAP_HUGETLB)",
218 },
219 {
220 .addr = (void *) -1,
221 .size = hugepagesize,
222 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
223 .msg = "mmap(-1, MAP_HUGETLB)",
224 .keep_mapped = 1,
225 },
226 {
227 .addr = (void *) -1,
228 .size = hugepagesize,
229 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
230 .msg = "mmap(-1, MAP_HUGETLB) again",
231 },
232 {
233 .addr = (void *)(addr_switch_hint - pagesize),
234 .size = 2 * hugepagesize,
235 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
236 .msg = "mmap(addr_switch_hint - pagesize, 2*hugepagesize, MAP_HUGETLB)",
237 .low_addr_required = 1,
238 .keep_mapped = 1,
239 },
240 {
241 .addr = (void *)(addr_switch_hint),
242 .size = 2 * hugepagesize,
243 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
244 .msg = "mmap(addr_switch_hint , 2*hugepagesize, MAP_FIXED | MAP_HUGETLB)",
245 },
246 };
247
248 testcases = malloc(sizeof(t));
249 hugetlb_testcases = malloc(sizeof(ht));
250
251 /* Copy into global arrays */
252 memcpy(testcases, t, sizeof(t));
253 memcpy(hugetlb_testcases, ht, sizeof(ht));
254
255 sz_testcases = ARRAY_SIZE(t);
256 sz_hugetlb_testcases = ARRAY_SIZE(ht);
257 switch_hint = addr_switch_hint;
258 }
259
run_test(struct testcase * test,int count)260 static int run_test(struct testcase *test, int count)
261 {
262 void *p;
263 int i, ret = KSFT_PASS;
264
265 for (i = 0; i < count; i++) {
266 struct testcase *t = test + i;
267
268 p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
269
270 printf("%s: %p - ", t->msg, p);
271
272 if (p == MAP_FAILED) {
273 printf("FAILED\n");
274 ret = KSFT_FAIL;
275 continue;
276 }
277
278 if (t->low_addr_required && p >= (void *)(switch_hint)) {
279 printf("FAILED\n");
280 ret = KSFT_FAIL;
281 } else {
282 /*
283 * Do a dereference of the address returned so that we catch
284 * bugs in page fault handling
285 */
286 memset(p, 0, t->size);
287 printf("OK\n");
288 }
289 if (!t->keep_mapped)
290 munmap(p, t->size);
291 }
292
293 return ret;
294 }
295
296 #ifdef __aarch64__
297 /* Check if userspace VA > 48 bits */
high_address_present(void)298 static int high_address_present(void)
299 {
300 void *ptr = mmap((void *)(1UL << 50), 1, PROT_READ | PROT_WRITE,
301 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
302 if (ptr == MAP_FAILED)
303 return 0;
304
305 munmap(ptr, 1);
306 return 1;
307 }
308 #endif
309
supported_arch(void)310 static int supported_arch(void)
311 {
312 #if defined(__powerpc64__)
313 return 1;
314 #elif defined(__x86_64__)
315 return 1;
316 #elif defined(__aarch64__)
317 return high_address_present();
318 #else
319 return 0;
320 #endif
321 }
322
main(int argc,char ** argv)323 int main(int argc, char **argv)
324 {
325 int ret;
326
327 if (!supported_arch())
328 return KSFT_SKIP;
329
330 testcases_init();
331
332 ret = run_test(testcases, sz_testcases);
333 if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
334 ret = run_test(hugetlb_testcases, sz_hugetlb_testcases);
335 return ret;
336 }
337