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

source code of linux/tools/testing/selftests/mm/va_high_addr_switch.c