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