1 /*
2  *
3  * Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
4  * Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version 2, as
8  * published by the Free Software Foundation.
9 
10  * This program is distributed in the hope that it would be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *
14  */
15 
16 #include <stdio.h>
17 #include <sys/mman.h>
18 #include <string.h>
19 
20 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
21 
22 #ifdef __powerpc64__
23 #define PAGE_SIZE	(64 << 10)
24 /*
25  * This will work with 16M and 2M hugepage size
26  */
27 #define HUGETLB_SIZE	(16 << 20)
28 #else
29 #define PAGE_SIZE	(4 << 10)
30 #define HUGETLB_SIZE	(2 << 20)
31 #endif
32 
33 /*
34  * >= 128TB is the hint addr value we used to select
35  * large address space.
36  */
37 #define ADDR_SWITCH_HINT (1UL << 47)
38 #define LOW_ADDR	((void *) (1UL << 30))
39 #define HIGH_ADDR	((void *) (1UL << 48))
40 
41 struct testcase {
42 	void *addr;
43 	unsigned long size;
44 	unsigned long flags;
45 	const char *msg;
46 	unsigned int low_addr_required:1;
47 	unsigned int keep_mapped:1;
48 };
49 
50 static struct testcase testcases[] = {
51 	{
52 		/*
53 		 * If stack is moved, we could possibly allocate
54 		 * this at the requested address.
55 		 */
56 		.addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
57 		.size = PAGE_SIZE,
58 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
59 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
60 		.low_addr_required = 1,
61 	},
62 	{
63 		/*
64 		 * We should never allocate at the requested address or above it
65 		 * The len cross the 128TB boundary. Without MAP_FIXED
66 		 * we will always search in the lower address space.
67 		 */
68 		.addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
69 		.size = 2 * PAGE_SIZE,
70 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
71 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, (2 * PAGE_SIZE))",
72 		.low_addr_required = 1,
73 	},
74 	{
75 		/*
76 		 * Exact mapping at 128TB, the area is free we should get that
77 		 * even without MAP_FIXED.
78 		 */
79 		.addr = ((void *)(ADDR_SWITCH_HINT)),
80 		.size = PAGE_SIZE,
81 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
82 		.msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
83 		.keep_mapped = 1,
84 	},
85 	{
86 		.addr = (void *)(ADDR_SWITCH_HINT),
87 		.size = 2 * PAGE_SIZE,
88 		.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
89 		.msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
90 	},
91 	{
92 		.addr = NULL,
93 		.size = 2 * PAGE_SIZE,
94 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
95 		.msg = "mmap(NULL)",
96 		.low_addr_required = 1,
97 	},
98 	{
99 		.addr = LOW_ADDR,
100 		.size = 2 * PAGE_SIZE,
101 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
102 		.msg = "mmap(LOW_ADDR)",
103 		.low_addr_required = 1,
104 	},
105 	{
106 		.addr = HIGH_ADDR,
107 		.size = 2 * PAGE_SIZE,
108 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
109 		.msg = "mmap(HIGH_ADDR)",
110 		.keep_mapped = 1,
111 	},
112 	{
113 		.addr = HIGH_ADDR,
114 		.size = 2 * PAGE_SIZE,
115 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
116 		.msg = "mmap(HIGH_ADDR) again",
117 		.keep_mapped = 1,
118 	},
119 	{
120 		.addr = HIGH_ADDR,
121 		.size = 2 * PAGE_SIZE,
122 		.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
123 		.msg = "mmap(HIGH_ADDR, MAP_FIXED)",
124 	},
125 	{
126 		.addr = (void *) -1,
127 		.size = 2 * PAGE_SIZE,
128 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
129 		.msg = "mmap(-1)",
130 		.keep_mapped = 1,
131 	},
132 	{
133 		.addr = (void *) -1,
134 		.size = 2 * PAGE_SIZE,
135 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
136 		.msg = "mmap(-1) again",
137 	},
138 	{
139 		.addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
140 		.size = PAGE_SIZE,
141 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
142 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
143 		.low_addr_required = 1,
144 	},
145 	{
146 		.addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
147 		.size = 2 * PAGE_SIZE,
148 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
149 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2 * PAGE_SIZE)",
150 		.low_addr_required = 1,
151 		.keep_mapped = 1,
152 	},
153 	{
154 		.addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE / 2),
155 		.size = 2 * PAGE_SIZE,
156 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
157 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE/2 , 2 * PAGE_SIZE)",
158 		.low_addr_required = 1,
159 		.keep_mapped = 1,
160 	},
161 	{
162 		.addr = ((void *)(ADDR_SWITCH_HINT)),
163 		.size = PAGE_SIZE,
164 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
165 		.msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
166 	},
167 	{
168 		.addr = (void *)(ADDR_SWITCH_HINT),
169 		.size = 2 * PAGE_SIZE,
170 		.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
171 		.msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
172 	},
173 };
174 
175 static struct testcase hugetlb_testcases[] = {
176 	{
177 		.addr = NULL,
178 		.size = HUGETLB_SIZE,
179 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
180 		.msg = "mmap(NULL, MAP_HUGETLB)",
181 		.low_addr_required = 1,
182 	},
183 	{
184 		.addr = LOW_ADDR,
185 		.size = HUGETLB_SIZE,
186 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
187 		.msg = "mmap(LOW_ADDR, MAP_HUGETLB)",
188 		.low_addr_required = 1,
189 	},
190 	{
191 		.addr = HIGH_ADDR,
192 		.size = HUGETLB_SIZE,
193 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
194 		.msg = "mmap(HIGH_ADDR, MAP_HUGETLB)",
195 		.keep_mapped = 1,
196 	},
197 	{
198 		.addr = HIGH_ADDR,
199 		.size = HUGETLB_SIZE,
200 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
201 		.msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again",
202 		.keep_mapped = 1,
203 	},
204 	{
205 		.addr = HIGH_ADDR,
206 		.size = HUGETLB_SIZE,
207 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
208 		.msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)",
209 	},
210 	{
211 		.addr = (void *) -1,
212 		.size = HUGETLB_SIZE,
213 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
214 		.msg = "mmap(-1, MAP_HUGETLB)",
215 		.keep_mapped = 1,
216 	},
217 	{
218 		.addr = (void *) -1,
219 		.size = HUGETLB_SIZE,
220 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
221 		.msg = "mmap(-1, MAP_HUGETLB) again",
222 	},
223 	{
224 		.addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
225 		.size = 2 * HUGETLB_SIZE,
226 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
227 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2*HUGETLB_SIZE, MAP_HUGETLB)",
228 		.low_addr_required = 1,
229 		.keep_mapped = 1,
230 	},
231 	{
232 		.addr = (void *)(ADDR_SWITCH_HINT),
233 		.size = 2 * HUGETLB_SIZE,
234 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
235 		.msg = "mmap(ADDR_SWITCH_HINT , 2*HUGETLB_SIZE, MAP_FIXED | MAP_HUGETLB)",
236 	},
237 };
238 
run_test(struct testcase * test,int count)239 static int run_test(struct testcase *test, int count)
240 {
241 	void *p;
242 	int i, ret = 0;
243 
244 	for (i = 0; i < count; i++) {
245 		struct testcase *t = test + i;
246 
247 		p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
248 
249 		printf("%s: %p - ", t->msg, p);
250 
251 		if (p == MAP_FAILED) {
252 			printf("FAILED\n");
253 			ret = 1;
254 			continue;
255 		}
256 
257 		if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) {
258 			printf("FAILED\n");
259 			ret = 1;
260 		} else {
261 			/*
262 			 * Do a dereference of the address returned so that we catch
263 			 * bugs in page fault handling
264 			 */
265 			memset(p, 0, t->size);
266 			printf("OK\n");
267 		}
268 		if (!t->keep_mapped)
269 			munmap(p, t->size);
270 	}
271 
272 	return ret;
273 }
274 
supported_arch(void)275 static int supported_arch(void)
276 {
277 #if defined(__powerpc64__)
278 	return 1;
279 #elif defined(__x86_64__)
280 	return 1;
281 #else
282 	return 0;
283 #endif
284 }
285 
main(int argc,char ** argv)286 int main(int argc, char **argv)
287 {
288 	int ret;
289 
290 	if (!supported_arch())
291 		return 0;
292 
293 	ret = run_test(testcases, ARRAY_SIZE(testcases));
294 	if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
295 		ret = run_test(hugetlb_testcases, ARRAY_SIZE(hugetlb_testcases));
296 	return ret;
297 }
298