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