xref: /linux/tools/testing/selftests/mm/va_high_addr_switch.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
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 */
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 
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 */
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 
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 
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