xref: /openbmc/qemu/tests/tcg/multiarch/test-mmap.c (revision 9ad4c7c9)
1 /*
2  * Small test program to verify simulated mmap behaviour.
3  *
4  * When running qemu-linux-user with the -p flag, you may need to tell
5  * this test program about the pagesize because getpagesize() will not reflect
6  * the -p choice. Simply pass one argument being the pagesize.
7  *
8  * Copyright (c) 2007 AXIS Communications AB
9  * Written by Edgar E. Iglesias.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <sys/mman.h>
32 
33 #define D(x)
34 
35 #define fail_unless(x)                                         \
36 do                                                             \
37 {                                                              \
38   if (!(x)) {                                                  \
39     fprintf(stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \
40     exit (EXIT_FAILURE);                                       \
41   }                                                            \
42 } while (0)
43 
44 unsigned char *dummybuf;
45 static unsigned int pagesize;
46 static unsigned int pagemask;
47 int test_fd;
48 size_t test_fsize;
49 
check_aligned_anonymous_unfixed_mmaps(void)50 void check_aligned_anonymous_unfixed_mmaps(void)
51 {
52     void *p1;
53     void *p2;
54     void *p3;
55     void *p4;
56     void *p5;
57     uintptr_t p;
58     int i;
59     fprintf(stdout, "%s", __func__);
60     for (i = 0; i < 8; i++) {
61         size_t len;
62         len = pagesize + (pagesize * i);
63         p1 = mmap(NULL, len, PROT_READ,
64                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
65         p2 = mmap(NULL, len, PROT_READ,
66                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
67         p3 = mmap(NULL, len, PROT_READ,
68                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
69         p4 = mmap(NULL, len, PROT_READ,
70                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
71         p5 = mmap(NULL, len, PROT_READ,
72                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
73 
74         /*
75          * Make sure we get pages aligned with the pagesize. The
76          * target expects this.
77          */
78         fail_unless(p1 != MAP_FAILED);
79         fail_unless(p2 != MAP_FAILED);
80         fail_unless(p3 != MAP_FAILED);
81         fail_unless(p4 != MAP_FAILED);
82         fail_unless(p5 != MAP_FAILED);
83         p = (uintptr_t) p1;
84         D(printf("p=%x\n", p));
85         fail_unless((p & pagemask) == 0);
86         p = (uintptr_t) p2;
87         fail_unless((p & pagemask) == 0);
88         p = (uintptr_t) p3;
89         fail_unless((p & pagemask) == 0);
90         p = (uintptr_t) p4;
91         fail_unless((p & pagemask) == 0);
92         p = (uintptr_t) p5;
93         fail_unless((p & pagemask) == 0);
94 
95         /* Make sure we can read from the entire area.  */
96         memcpy(dummybuf, p1, pagesize);
97         memcpy(dummybuf, p2, pagesize);
98         memcpy(dummybuf, p3, pagesize);
99         memcpy(dummybuf, p4, pagesize);
100         memcpy(dummybuf, p5, pagesize);
101         munmap(p1, len);
102         munmap(p2, len);
103         munmap(p3, len);
104         munmap(p4, len);
105         munmap(p5, len);
106     }
107     fprintf(stdout, " passed\n");
108 }
109 
check_large_anonymous_unfixed_mmap(void)110 void check_large_anonymous_unfixed_mmap(void)
111 {
112 	void *p1;
113 	uintptr_t p;
114 	size_t len;
115 
116 	fprintf(stdout, "%s", __func__);
117 
118 	len = 0x02000000;
119 	p1 = mmap(NULL, len, PROT_READ,
120 		  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
121 
122 	/* Make sure we get pages aligned with the pagesize. The
123 	   target expects this.  */
124 	fail_unless (p1 != MAP_FAILED);
125 	p = (uintptr_t) p1;
126 	fail_unless ((p & pagemask) == 0);
127 
128 	/* Make sure we can read from the entire area.  */
129 	memcpy (dummybuf, p1, pagesize);
130 	munmap (p1, len);
131 	fprintf(stdout, " passed\n");
132 }
133 
check_aligned_anonymous_unfixed_colliding_mmaps(void)134 void check_aligned_anonymous_unfixed_colliding_mmaps(void)
135 {
136     char *p1;
137     char *p2;
138     char *p3;
139     uintptr_t p;
140     int i;
141 
142     fprintf(stdout, "%s", __func__);
143     for (i = 0; i < 2; i++) {
144         int nlen;
145         p1 = mmap(NULL, pagesize, PROT_READ,
146                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
147         fail_unless(p1 != MAP_FAILED);
148         p = (uintptr_t) p1;
149         fail_unless((p & pagemask) == 0);
150         memcpy(dummybuf, p1, pagesize);
151 
152         p2 = mmap(NULL, pagesize, PROT_READ,
153                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
154         fail_unless(p2 != MAP_FAILED);
155         p = (uintptr_t) p2;
156         fail_unless((p & pagemask) == 0);
157         memcpy(dummybuf, p2, pagesize);
158 
159 
160         munmap(p1, pagesize);
161         nlen = pagesize * 8;
162         p3 = mmap(NULL, nlen, PROT_READ,
163                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
164         fail_unless(p3 != MAP_FAILED);
165 
166         /* Check if the mmaped areas collide.  */
167         if (p3 < p2
168             && (p3 + nlen) > p2) {
169             fail_unless(0);
170         }
171 
172         memcpy(dummybuf, p3, pagesize);
173 
174         /*
175          * Make sure we get pages aligned with the pagesize. The
176          * target expects this.
177          */
178         p = (uintptr_t) p3;
179         fail_unless((p & pagemask) == 0);
180         munmap(p2, pagesize);
181         munmap(p3, nlen);
182     }
183     fprintf(stdout, " passed\n");
184 }
185 
check_aligned_anonymous_fixed_mmaps(void)186 void check_aligned_anonymous_fixed_mmaps(void)
187 {
188 	char *addr;
189 	void *p1;
190 	uintptr_t p;
191 	int i;
192 
193 	/* Find a suitable address to start with.  */
194 	addr = mmap(NULL, pagesize * 40, PROT_READ | PROT_WRITE,
195 		    MAP_PRIVATE | MAP_ANONYMOUS,
196 		    -1, 0);
197 	fprintf(stdout, "%s addr=%p", __func__, addr);
198 	fail_unless (addr != MAP_FAILED);
199 
200 	for (i = 0; i < 40; i++)
201 	{
202 		/* Create submaps within our unfixed map.  */
203 		p1 = mmap(addr, pagesize, PROT_READ,
204 			  MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
205 			  -1, 0);
206 		/* Make sure we get pages aligned with the pagesize.
207 		   The target expects this.  */
208 		p = (uintptr_t) p1;
209 		fail_unless (p1 == addr);
210 		fail_unless ((p & pagemask) == 0);
211 		memcpy (dummybuf, p1, pagesize);
212 		munmap (p1, pagesize);
213 		addr += pagesize;
214 	}
215 	fprintf(stdout, " passed\n");
216 }
217 
check_aligned_anonymous_fixed_mmaps_collide_with_host(void)218 void check_aligned_anonymous_fixed_mmaps_collide_with_host(void)
219 {
220 	char *addr;
221 	void *p1;
222 	uintptr_t p;
223 	int i;
224 
225 	/* Find a suitable address to start with.  Right were the x86 hosts
226 	 stack is.  */
227 	addr = ((void *)0x80000000);
228 	fprintf(stdout, "%s addr=%p", __func__, addr);
229 	fprintf(stdout, "FIXME: QEMU fails to track pages used by the host.");
230 
231 	for (i = 0; i < 20; i++)
232 	{
233 		/* Create submaps within our unfixed map.  */
234 		p1 = mmap(addr, pagesize, PROT_READ | PROT_WRITE,
235 			  MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
236 			  -1, 0);
237 		/* Make sure we get pages aligned with the pagesize.
238 		   The target expects this.  */
239 		p = (uintptr_t) p1;
240 		fail_unless (p1 == addr);
241 		fail_unless ((p & pagemask) == 0);
242 		memcpy (p1, dummybuf, pagesize);
243 		munmap (p1, pagesize);
244 		addr += pagesize;
245 	}
246 	fprintf(stdout, " passed\n");
247 }
248 
check_file_unfixed_mmaps(void)249 void check_file_unfixed_mmaps(void)
250 {
251 	unsigned int *p1, *p2, *p3;
252 	uintptr_t p;
253 	int i;
254 
255 	fprintf(stdout, "%s", __func__);
256 	for (i = 0; i < 0x10; i++)
257 	{
258 		size_t len;
259 
260 		len = pagesize;
261 		p1 = mmap(NULL, len, PROT_READ,
262 			  MAP_PRIVATE,
263 			  test_fd, 0);
264 		p2 = mmap(NULL, len, PROT_READ,
265 			  MAP_PRIVATE,
266 			  test_fd, pagesize);
267 		p3 = mmap(NULL, len, PROT_READ,
268 			  MAP_PRIVATE,
269 			  test_fd, pagesize * 2);
270 
271 		fail_unless (p1 != MAP_FAILED);
272 		fail_unless (p2 != MAP_FAILED);
273 		fail_unless (p3 != MAP_FAILED);
274 
275 		/* Make sure we get pages aligned with the pagesize. The
276 		   target expects this.  */
277 		p = (uintptr_t) p1;
278 		fail_unless ((p & pagemask) == 0);
279 		p = (uintptr_t) p2;
280 		fail_unless ((p & pagemask) == 0);
281 		p = (uintptr_t) p3;
282 		fail_unless ((p & pagemask) == 0);
283 
284 		/* Verify that the file maps was made correctly.  */
285 		D(printf ("p1=%d p2=%d p3=%d\n", *p1, *p2, *p3));
286 		fail_unless (*p1 == 0);
287 		fail_unless (*p2 == (pagesize / sizeof *p2));
288 		fail_unless (*p3 == ((pagesize * 2) / sizeof *p3));
289 
290 		memcpy (dummybuf, p1, pagesize);
291 		memcpy (dummybuf, p2, pagesize);
292 		memcpy (dummybuf, p3, pagesize);
293 		munmap (p1, len);
294 		munmap (p2, len);
295 		munmap (p3, len);
296 	}
297 	fprintf(stdout, " passed\n");
298 }
299 
check_file_unfixed_eof_mmaps(void)300 void check_file_unfixed_eof_mmaps(void)
301 {
302 	char *cp;
303 	unsigned int *p1;
304 	uintptr_t p;
305 	int i;
306 
307 	fprintf(stdout, "%s", __func__);
308 	for (i = 0; i < 0x10; i++)
309 	{
310 		p1 = mmap(NULL, pagesize, PROT_READ,
311 			  MAP_PRIVATE,
312 			  test_fd,
313 			  (test_fsize - sizeof *p1) & ~pagemask);
314 
315 		fail_unless (p1 != MAP_FAILED);
316 
317 		/* Make sure we get pages aligned with the pagesize. The
318 		   target expects this.  */
319 		p = (uintptr_t) p1;
320 		fail_unless ((p & pagemask) == 0);
321 		/* Verify that the file maps was made correctly.  */
322 		fail_unless (p1[(test_fsize & pagemask) / sizeof *p1 - 1]
323 			     == ((test_fsize - sizeof *p1) / sizeof *p1));
324 
325 		/* Verify that the end of page is accessible and zeroed.  */
326 		cp = (void *) p1;
327 		fail_unless (cp[pagesize - 4] == 0);
328 		munmap (p1, pagesize);
329 	}
330 	fprintf(stdout, " passed\n");
331 }
332 
check_file_fixed_eof_mmaps(void)333 void check_file_fixed_eof_mmaps(void)
334 {
335 	char *addr;
336 	char *cp;
337 	unsigned int *p1;
338 	uintptr_t p;
339 	int i;
340 
341 	/* Find a suitable address to start with.  */
342 	addr = mmap(NULL, pagesize * 44, PROT_READ,
343 		    MAP_PRIVATE | MAP_ANONYMOUS,
344 		    -1, 0);
345 
346 	fprintf(stdout, "%s addr=%p", __func__, (void *)addr);
347 	fail_unless (addr != MAP_FAILED);
348 
349 	for (i = 0; i < 0x10; i++)
350 	{
351 		/* Create submaps within our unfixed map.  */
352 		p1 = mmap(addr, pagesize, PROT_READ,
353 			  MAP_PRIVATE | MAP_FIXED,
354 			  test_fd,
355 			  (test_fsize - sizeof *p1) & ~pagemask);
356 
357 		fail_unless (p1 != MAP_FAILED);
358 
359 		/* Make sure we get pages aligned with the pagesize. The
360 		   target expects this.  */
361 		p = (uintptr_t) p1;
362 		fail_unless ((p & pagemask) == 0);
363 
364 		/* Verify that the file maps was made correctly.  */
365 		fail_unless (p1[(test_fsize & pagemask) / sizeof *p1 - 1]
366 			     == ((test_fsize - sizeof *p1) / sizeof *p1));
367 
368 		/* Verify that the end of page is accessible and zeroed.  */
369 		cp = (void *)p1;
370 		fail_unless (cp[pagesize - 4] == 0);
371 		munmap (p1, pagesize);
372 		addr += pagesize;
373 	}
374 	fprintf(stdout, " passed\n");
375 }
376 
check_file_fixed_mmaps(void)377 void check_file_fixed_mmaps(void)
378 {
379 	unsigned char *addr;
380 	unsigned int *p1, *p2, *p3, *p4;
381 	int i;
382 
383 	/* Find a suitable address to start with.  */
384 	addr = mmap(NULL, pagesize * 40 * 4, PROT_READ,
385 		    MAP_PRIVATE | MAP_ANONYMOUS,
386 		    -1, 0);
387 	fprintf(stdout, "%s addr=%p", __func__, (void *)addr);
388 	fail_unless (addr != MAP_FAILED);
389 
390 	for (i = 0; i < 40; i++)
391 	{
392 		p1 = mmap(addr, pagesize, PROT_READ,
393 			  MAP_PRIVATE | MAP_FIXED,
394 			  test_fd, 0);
395 		p2 = mmap(addr + pagesize, pagesize, PROT_READ,
396 			  MAP_PRIVATE | MAP_FIXED,
397 			  test_fd, pagesize);
398 		p3 = mmap(addr + pagesize * 2, pagesize, PROT_READ,
399 			  MAP_PRIVATE | MAP_FIXED,
400 			  test_fd, pagesize * 2);
401 		p4 = mmap(addr + pagesize * 3, pagesize, PROT_READ,
402 			  MAP_PRIVATE | MAP_FIXED,
403 			  test_fd, pagesize * 3);
404 
405 		/* Make sure we get pages aligned with the pagesize.
406 		   The target expects this.  */
407 		fail_unless (p1 == (void *)addr);
408 		fail_unless (p2 == (void *)addr + pagesize);
409 		fail_unless (p3 == (void *)addr + pagesize * 2);
410 		fail_unless (p4 == (void *)addr + pagesize * 3);
411 
412 		/* Verify that the file maps was made correctly.  */
413 		fail_unless (*p1 == 0);
414 		fail_unless (*p2 == (pagesize / sizeof *p2));
415 		fail_unless (*p3 == ((pagesize * 2) / sizeof *p3));
416 		fail_unless (*p4 == ((pagesize * 3) / sizeof *p4));
417 
418 		memcpy (dummybuf, p1, pagesize);
419 		memcpy (dummybuf, p2, pagesize);
420 		memcpy (dummybuf, p3, pagesize);
421 		memcpy (dummybuf, p4, pagesize);
422 
423 		munmap (p1, pagesize);
424 		munmap (p2, pagesize);
425 		munmap (p3, pagesize);
426 		munmap (p4, pagesize);
427 		addr += pagesize * 4;
428 	}
429 	fprintf(stdout, " passed\n");
430 }
431 
checked_write(int fd,const void * buf,size_t count)432 void checked_write(int fd, const void *buf, size_t count)
433 {
434     ssize_t rc = write(fd, buf, count);
435     fail_unless(rc == count);
436 }
437 
check_invalid_mmaps(void)438 void check_invalid_mmaps(void)
439 {
440     unsigned char *addr;
441 
442     /* Attempt to map a zero length page.  */
443     addr = mmap(NULL, 0, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
444     fprintf(stdout, "%s addr=%p", __func__, (void *)addr);
445     fail_unless(addr == MAP_FAILED);
446     fail_unless(errno == EINVAL);
447 
448     /* Attempt to map a over length page.  */
449     addr = mmap(NULL, -4, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
450     fprintf(stdout, "%s addr=%p", __func__, (void *)addr);
451     fail_unless(addr == MAP_FAILED);
452     fail_unless(errno == ENOMEM);
453 
454     fprintf(stdout, " passed\n");
455 }
456 
main(int argc,char ** argv)457 int main(int argc, char **argv)
458 {
459 	char tempname[] = "/tmp/.cmmapXXXXXX";
460 	unsigned int i;
461 
462 	/* Trust the first argument, otherwise probe the system for our
463 	   pagesize.  */
464 	if (argc > 1)
465 		pagesize = strtoul(argv[1], NULL, 0);
466 	else
467 		pagesize = sysconf(_SC_PAGESIZE);
468 
469 	/* Assume pagesize is a power of two.  */
470 	pagemask = pagesize - 1;
471 	dummybuf = malloc (pagesize);
472 	printf ("pagesize=%u pagemask=%x\n", pagesize, pagemask);
473 
474 	test_fd = mkstemp(tempname);
475 	unlink(tempname);
476 
477 	/* Fill the file with int's counting from zero and up.  */
478     for (i = 0; i < (pagesize * 4) / sizeof i; i++) {
479         checked_write(test_fd, &i, sizeof i);
480     }
481 
482 	/* Append a few extra writes to make the file end at non
483 	   page boundary.  */
484     checked_write(test_fd, &i, sizeof i); i++;
485     checked_write(test_fd, &i, sizeof i); i++;
486     checked_write(test_fd, &i, sizeof i); i++;
487 
488 	test_fsize = lseek(test_fd, 0, SEEK_CUR);
489 
490 	/* Run the tests.  */
491 	check_aligned_anonymous_unfixed_mmaps();
492 	check_aligned_anonymous_unfixed_colliding_mmaps();
493 	check_aligned_anonymous_fixed_mmaps();
494 	check_file_unfixed_mmaps();
495 	check_file_fixed_mmaps();
496 	check_file_fixed_eof_mmaps();
497 	check_file_unfixed_eof_mmaps();
498 	check_invalid_mmaps();
499 
500 	/* Fails at the moment.  */
501 	/* check_aligned_anonymous_fixed_mmaps_collide_with_host(); */
502 
503 	return EXIT_SUCCESS;
504 }
505