xref: /xnu-10002.81.5/tools/tests/superpages/testsp.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1 /*
2  * This tests the Mac OS X Superpage API introduced in 10.7
3  *
4  * Note that most of these calls go through the mach_vm_allocate() interface,
5  * but the actually supported and documented interface is the mmap() one
6  * (see mmap(2)).
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <signal.h>
11 #include <setjmp.h>
12 #include <mach/mach.h>
13 #include <mach/mach_vm.h>
14 #include <time.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <sys/mman.h>
18 
19 #define SUPERPAGE_SIZE (2*1024*1024)
20 #define SUPERPAGE_MASK (-SUPERPAGE_SIZE)
21 
22 #ifdef __LP64__
23 #define FIXED_ADDRESS1 (0x100000000ULL+500*1024*1024) /* at 4 GB + 500 MB virtual */
24 #define FIXED_ADDRESS2 (0x100000000ULL+502*1024*1024 + 4*1024) /* at 4 GB + 502 MB + 4 KB virtual */
25 #else
26 #define FIXED_ADDRESS1 (500*1024*1024) /* at 500 MB virtual */
27 #define FIXED_ADDRESS2 (502*1024*1024 + 4*1024) /* at 502 MB + 4 KB virtual */
28 #endif
29 
30 char error[100];
31 
32 jmp_buf resume;
33 void
test_signal_handler(int signo)34 test_signal_handler(int signo)
35 {
36 	longjmp(resume, signo);
37 }
38 
39 char *signame[32] = {
40 	[SIGBUS] "SIGBUS",
41 	[SIGSEGV] "SIGSEGV"
42 };
43 
44 typedef struct {
45 	char *description;
46 	boolean_t (*fn)();
47 } test_t;
48 
49 boolean_t
check_kr(int kr,char * fn)50 check_kr(int kr, char *fn)
51 {
52 	if (kr) {
53 		sprintf(error, "%s() returned %d", fn, kr);
54 		return FALSE;
55 	}
56 	return TRUE;
57 }
58 
59 boolean_t
check_addr0(mach_vm_address_t addr,char * fn)60 check_addr0(mach_vm_address_t addr, char *fn)
61 {
62 	if (!addr) {
63 		sprintf(error, "%s() returned address 0", fn);
64 		return FALSE;
65 	}
66 	return TRUE;
67 }
68 
69 boolean_t
check_addr(mach_vm_address_t addr1,mach_vm_address_t addr2,char * fn)70 check_addr(mach_vm_address_t addr1, mach_vm_address_t addr2, char *fn)
71 {
72 	if (addr1 != addr2) {
73 		sprintf(error, "%s() returned address %llx instead of %llx", fn, addr1, addr2);
74 		return FALSE;
75 	}
76 	return TRUE;
77 }
78 
79 boolean_t
check_align(mach_vm_address_t addr)80 check_align(mach_vm_address_t addr)
81 {
82 	if (addr & !SUPERPAGE_MASK) {
83 		sprintf(error, "address not aligned properly: 0x%llx", addr);
84 		return FALSE;
85 	}
86 	return TRUE;
87 }
88 
89 boolean_t
check_r(mach_vm_address_t addr,mach_vm_size_t size,int * res)90 check_r(mach_vm_address_t addr, mach_vm_size_t size, int *res)
91 {
92 	volatile char *data = (char*)(uintptr_t)addr;
93 	int i, sig, test;
94 
95 	if ((sig = setjmp(resume)) != 0) {
96 		sprintf(error, "%s when reading", signame[sig]);
97 		return FALSE;
98 	}
99 	test = 0;
100 	for (i = 0; i < size; i++) {
101 		test += (data)[i];
102 	}
103 
104 	if (res) {
105 		*res = test;
106 	}
107 
108 	return TRUE;
109 }
110 
111 /* check that no subpage of the superpage is readable */
112 boolean_t
check_nr(mach_vm_address_t addr,mach_vm_size_t size,int * res)113 check_nr(mach_vm_address_t addr, mach_vm_size_t size, int *res)
114 {
115 	int i;
116 	boolean_t ret;
117 	for (i = 0; i < size / PAGE_SIZE; i++) {
118 		if ((ret = check_r(addr + i * PAGE_SIZE, PAGE_SIZE, res))) {
119 			sprintf(error, "page still readable");
120 			return FALSE;
121 		}
122 	}
123 	return TRUE;
124 }
125 
126 boolean_t
check_w(mach_vm_address_t addr,mach_vm_size_t size)127 check_w(mach_vm_address_t addr, mach_vm_size_t size)
128 {
129 	char *data = (char*)(uintptr_t)addr;
130 	int i, sig;
131 
132 	if ((sig = setjmp(resume)) != 0) {
133 		sprintf(error, "%s when writing", signame[sig]);
134 		return FALSE;
135 	}
136 
137 	for (i = 0; i < size; i++) {
138 		(data)[i] = i & 0xFF;
139 	}
140 
141 	return TRUE;
142 }
143 
144 boolean_t
check_nw(mach_vm_address_t addr,mach_vm_size_t size)145 check_nw(mach_vm_address_t addr, mach_vm_size_t size)
146 {
147 	int i;
148 	boolean_t ret;
149 
150 	for (i = 0; i < size / PAGE_SIZE; i++) {
151 		if ((ret = check_w(addr + i * PAGE_SIZE, PAGE_SIZE))) {
152 			sprintf(error, "page still writable");
153 			return FALSE;
154 		}
155 	}
156 	return TRUE;
157 }
158 
159 boolean_t
check_rw(mach_vm_address_t addr,mach_vm_size_t size)160 check_rw(mach_vm_address_t addr, mach_vm_size_t size)
161 {
162 	int ret;
163 	int res;
164 	if (!(ret = check_w(addr, size))) {
165 		return ret;
166 	}
167 	if (!(ret = check_r(addr, size, &res))) {
168 		return ret;
169 	}
170 	if ((size == SUPERPAGE_SIZE) && (res != 0xfff00000)) {
171 		sprintf(error, "checksum error");
172 		return FALSE;
173 	}
174 
175 	return TRUE;
176 }
177 
178 mach_vm_address_t global_addr = 0;
179 mach_vm_size_t  global_size = 0;
180 
181 /*
182  * If we allocate a 2 MB superpage read-write without specifying an address,
183  * - the call should succeed
184  * - not return 0
185  * - return a 2 MB aligned address
186  * - the memory should be readable and writable
187  */
188 boolean_t
test_allocate()189 test_allocate()
190 {
191 	int kr, ret;
192 
193 	global_addr = 0;
194 	global_size = SUPERPAGE_SIZE;
195 
196 	kr = mach_vm_allocate(mach_task_self(), &global_addr, global_size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
197 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
198 		return ret;
199 	}
200 	if (!(ret = check_addr0(global_addr, "mach_vm_allocate"))) {
201 		return ret;
202 	}
203 	if (!(ret = check_align(global_addr))) {
204 		return ret;
205 	}
206 	if (!(ret = check_rw(global_addr, global_size))) {
207 		return ret;
208 	}
209 
210 	return TRUE;
211 }
212 
213 /*
214  * If we deallocate a superpage,
215  * - the call should succeed
216  * - make the memory inaccessible
217  */
218 boolean_t
test_deallocate()219 test_deallocate()
220 {
221 	mach_vm_size_t  size = SUPERPAGE_SIZE;
222 	int kr, ret;
223 
224 	if (!global_addr) {
225 		sprintf(error, "skipped deallocation");
226 		return FALSE;
227 	}
228 	kr = mach_vm_deallocate(mach_task_self(), global_addr, global_size);
229 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
230 		return ret;
231 	}
232 	if (!(ret = check_nr(global_addr, size, NULL))) {
233 		return ret;
234 	}
235 	return TRUE;
236 }
237 
238 /*
239  * If we allocate a superpage of any size read-write without specifying an address
240  * - the call should succeed
241  * - not return 0
242  * - the memory should be readable and writable
243  * If we deallocate it,
244  * - the call should succeed
245  * - make the memory inaccessible
246  */
247 boolean_t
test_allocate_size_any()248 test_allocate_size_any()
249 {
250 	int kr;
251 	int ret;
252 	mach_vm_address_t addr = 0;
253 	mach_vm_size_t  size = 2 * PAGE_SIZE; /* will be rounded up to some superpage size */
254 
255 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_ANY);
256 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
257 		return ret;
258 	}
259 	if (!(ret = check_addr0(addr, "mach_vm_allocate"))) {
260 		return ret;
261 	}
262 	if (!(ret = check_rw(addr, size))) {
263 		return ret;
264 	}
265 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
266 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
267 		return ret;
268 	}
269 	if (!(ret = check_nr(addr, size, NULL))) {
270 		return ret;
271 	}
272 	return TRUE;
273 }
274 
275 /*
276  * If we allocate a 2 MB superpage read-write at a 2 MB aligned address,
277  * - the call should succeed
278  * - return the address we wished for
279  * - the memory should be readable and writable
280  * If we deallocate it,
281  * - the call should succeed
282  * - make the memory inaccessible
283  */
284 boolean_t
test_allocatefixed()285 test_allocatefixed()
286 {
287 	int kr;
288 	int ret;
289 	mach_vm_address_t addr = FIXED_ADDRESS1;
290 	mach_vm_size_t  size = SUPERPAGE_SIZE;
291 
292 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB);
293 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
294 		return ret;
295 	}
296 	if (!(ret = check_addr(addr, FIXED_ADDRESS1, "mach_vm_allocate"))) {
297 		return ret;
298 	}
299 	if (!(ret = check_rw(addr, size))) {
300 		return ret;
301 	}
302 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
303 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
304 		return ret;
305 	}
306 	if (!(ret = check_nr(addr, size, NULL))) {
307 		return ret;
308 	}
309 	return TRUE;
310 }
311 
312 /*
313  * If we allocate a 2 MB superpage read-write at an unaligned address,
314  * - the call should fail
315  */
316 boolean_t
test_allocateunalignedfixed()317 test_allocateunalignedfixed()
318 {
319 	int kr;
320 	int ret;
321 	mach_vm_address_t addr = FIXED_ADDRESS2;
322 	mach_vm_size_t  size = SUPERPAGE_SIZE;
323 
324 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB);
325 	/* is supposed to fail */
326 	if ((ret = check_kr(kr, "mach_vm_allocate"))) {
327 		sprintf(error, "mach_vm_allocate() should have failed");
328 		return FALSE;
329 	}
330 	return TRUE;
331 }
332 
333 /*
334  * If we allocate an amount of memory not divisible by 2 MB as a 2 MB superpage
335  * - the call should fail
336  */
337 boolean_t
test_allocateoddsize()338 test_allocateoddsize()
339 {
340 	int kr;
341 	int ret;
342 	mach_vm_address_t addr = FIXED_ADDRESS1;
343 	mach_vm_size_t  size = PAGE_SIZE; /* != 2 MB */
344 
345 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB);
346 	/* is supposed to fail */
347 	if ((ret = check_kr(kr, "mach_vm_allocate"))) {
348 		sprintf(error, "mach_vm_allocate() should have failed");
349 		return FALSE;
350 	}
351 	return TRUE;
352 }
353 
354 /*
355  * If we deallocate a sub-page of a superpage,
356  * - the call should succeed
357  * - make the complete memory inaccessible
358  */
359 boolean_t
test_deallocatesubpage()360 test_deallocatesubpage()
361 {
362 	int kr;
363 	int ret;
364 	mach_vm_address_t addr = 0;
365 	mach_vm_size_t  size = SUPERPAGE_SIZE;
366 
367 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
368 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
369 		return ret;
370 	}
371 	kr = mach_vm_deallocate(mach_task_self(), addr + PAGE_SIZE, size);
372 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
373 		return ret;
374 	}
375 	if (!(ret = check_nr(addr, size, NULL))) {
376 		return ret;
377 	}
378 	return TRUE;
379 }
380 
381 /*
382  * If we try to allocate memory occupied by superpages as normal pages
383  * - the call should fail
384  */
385 boolean_t
test_reallocate()386 test_reallocate()
387 {
388 	mach_vm_address_t addr = 0, addr2;
389 	mach_vm_size_t  size = SUPERPAGE_SIZE;
390 	int kr, ret;
391 	int i;
392 
393 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
394 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
395 		return ret;
396 	}
397 
398 	/* attempt to allocate every sub-page of superpage */
399 	for (i = 0; i < SUPERPAGE_SIZE / PAGE_SIZE; i++) {
400 		addr2 = addr + i * PAGE_SIZE;
401 		size = PAGE_SIZE;
402 		kr = mach_vm_allocate(mach_task_self(), &addr2, size, 0);
403 		if ((ret = check_kr(kr, "mach_vm_allocate"))) {
404 			sprintf(error, "could allocate already allocated space, page %d", i);
405 			mach_vm_deallocate(mach_task_self(), addr, size);
406 			return FALSE;
407 		}
408 	}
409 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
410 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
411 		return ret;
412 	}
413 	return TRUE;
414 }
415 
416 /*
417  * If we try to wire superpages
418  * - the call should succeed
419  * - the memory should remain readable and writable
420  */
421 boolean_t
test_wire()422 test_wire()
423 {
424 	int kr;
425 	int ret;
426 	mach_vm_address_t addr = 0;
427 	mach_vm_size_t  size = SUPERPAGE_SIZE;
428 
429 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
430 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
431 		return ret;
432 	}
433 
434 	kr = mach_vm_wire(mach_host_self(), mach_task_self(), addr, size, VM_PROT_WRITE | VM_PROT_READ);
435 
436 	if (!geteuid()) { /* may fail as user */
437 		if (!(ret = check_kr(kr, "mach_vm_wire"))) {
438 			return ret;
439 		}
440 	}
441 
442 	if (!(ret = check_rw(addr, size))) {
443 		return ret;
444 	}
445 
446 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
447 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
448 		return ret;
449 	}
450 
451 	return TRUE;
452 }
453 
454 /*
455  * If we try to wire superpages
456  * - the call should fail
457  * - the memory should remain readable and writable
458  * Currently, superpages are always wired.
459  */
460 boolean_t
test_unwire()461 test_unwire()
462 {
463 	int kr;
464 	int ret;
465 	mach_vm_address_t addr = 0;
466 	mach_vm_size_t  size = SUPERPAGE_SIZE;
467 
468 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
469 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
470 		return ret;
471 	}
472 
473 	kr = mach_vm_wire(mach_host_self(), mach_task_self(), addr, size, VM_PROT_NONE);
474 	if ((ret = check_kr(kr, "mach_vm_wire"))) {
475 		sprintf(error, "could unwire");
476 		return FALSE;
477 	}
478 
479 	if (!(ret = check_rw(addr, size))) {
480 		return ret;
481 	}
482 
483 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
484 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
485 		return ret;
486 	}
487 
488 	return TRUE;
489 }
490 
491 /*
492  * If we try to write-protect superpages
493  * - the call should succeed
494  * - the memory should remain readable
495  * - the memory should not be writable
496  */
497 boolean_t
test_readonly()498 test_readonly()
499 {
500 	int kr;
501 	int ret;
502 	mach_vm_address_t addr = 0;
503 	mach_vm_size_t  size = SUPERPAGE_SIZE;
504 
505 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
506 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
507 		return ret;
508 	}
509 
510 	mach_vm_protect(mach_task_self(), addr, size, 0, VM_PROT_READ);
511 	if (!(ret = check_kr(kr, "mach_vm_protect"))) {
512 		return ret;
513 	}
514 
515 	if (!(ret = check_r(addr, size, NULL))) {
516 		return ret;
517 	}
518 	if (!(ret = check_nw(addr, size))) {
519 		return ret;
520 	}
521 
522 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
523 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
524 		return ret;
525 	}
526 
527 	return TRUE;
528 }
529 
530 /*
531  * If we try to write-protect a sub-page of a superpage
532  * - the call should succeed
533  * - the complete memory should remain readable
534  * - the complete memory should not be writable
535  */
536 boolean_t
test_readonlysubpage()537 test_readonlysubpage()
538 {
539 	int kr;
540 	int ret;
541 	mach_vm_address_t addr = 0;
542 	mach_vm_size_t  size = SUPERPAGE_SIZE;
543 
544 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
545 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
546 		return ret;
547 	}
548 
549 	mach_vm_protect(mach_task_self(), addr + PAGE_SIZE, PAGE_SIZE, 0, VM_PROT_READ);
550 	if (!(ret = check_kr(kr, "mach_vm_protect"))) {
551 		return ret;
552 	}
553 
554 	if (!(ret = check_r(addr, size, NULL))) {
555 		return ret;
556 	}
557 	if (!(ret = check_nw(addr, size))) {
558 		return ret;
559 	}
560 
561 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
562 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
563 		return ret;
564 	}
565 
566 	return TRUE;
567 }
568 
569 /*
570  * If we fork with active superpages
571  * - the parent should still be able to access the superpages
572  * - the child should not be able to access the superpages
573  */
574 boolean_t
test_fork()575 test_fork()
576 {
577 	mach_vm_address_t addr = 0;
578 	mach_vm_size_t  size = SUPERPAGE_SIZE;
579 	int kr, ret;
580 	pid_t pid;
581 
582 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
583 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
584 		return ret;
585 	}
586 
587 	fflush(stdout);
588 	if ((pid = fork())) { /* parent */
589 		if (!(ret = check_rw(addr, size))) {
590 			return ret;
591 		}
592 		waitpid(pid, &ret, 0);
593 		if (!ret) {
594 			sprintf(error, "child could access superpage");
595 			return ret;
596 		}
597 	} else { /* child */
598 		if (!(ret = check_nr(addr, size, NULL))) {
599 			exit(ret);
600 		}
601 		exit(TRUE);
602 	}
603 
604 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
605 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
606 		return ret;
607 	}
608 	return TRUE;
609 }
610 
611 /*
612  * Doing file I/O with superpages
613  * - should succeed
614  * - should behave the same as with base pages (i.e. no bad data)
615  */
616 #define FILENAME "/System/Library/Kernels/kernel"
617 boolean_t
test_fileio()618 test_fileio()
619 {
620 	mach_vm_address_t addr1 = 0;
621 	mach_vm_address_t addr2 = 0;
622 	mach_vm_size_t  size = SUPERPAGE_SIZE;
623 	int kr, ret;
624 	int fd;
625 	unsigned int bytes;
626 
627 	/* allocate one superpage */
628 	kr = mach_vm_allocate(mach_task_self(), &addr1, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
629 	if (!(ret = check_kr(kr, "mach_vm_allocate (1)"))) {
630 		return ret;
631 	}
632 
633 	/* allocate base pages (superpage-sized) */
634 	kr = mach_vm_allocate(mach_task_self(), &addr2, size, VM_FLAGS_ANYWHERE);
635 	if (!(ret = check_kr(kr, "mach_vm_allocate (2)"))) {
636 		return ret;
637 	}
638 
639 	if ((fd = open(FILENAME, O_RDONLY)) < 0) {
640 		sprintf(error, "couldn't open %s", FILENAME);
641 		return FALSE;
642 	}
643 	fcntl(fd, F_NOCACHE, 1);
644 	/* read kernel into superpage */
645 	if ((bytes = read(fd, (void*)(uintptr_t)addr1, SUPERPAGE_SIZE)) < SUPERPAGE_SIZE) {
646 		sprintf(error, "short read (1)");
647 		return FALSE;
648 	}
649 	lseek(fd, 0, SEEK_SET);
650 	/* read kernel into base pages */
651 	if ((bytes = read(fd, (void*)(uintptr_t)addr2, SUPERPAGE_SIZE)) < SUPERPAGE_SIZE) {
652 		sprintf(error, "short read (2)");
653 		return FALSE;
654 	}
655 	close(fd);
656 
657 	/* compare */
658 	if (memcmp((void*)(uintptr_t)addr1, (void*)(uintptr_t)addr2, bytes)) {
659 		sprintf(error, "read data corrupt");
660 		return FALSE;
661 	}
662 
663 	kr = mach_vm_deallocate(mach_task_self(), addr1, size);
664 	if (!(ret = check_kr(kr, "mach_vm_deallocate (1)"))) {
665 		return ret;
666 	}
667 	kr = mach_vm_deallocate(mach_task_self(), addr2, size);
668 	if (!(ret = check_kr(kr, "mach_vm_deallocate (2)"))) {
669 		return ret;
670 	}
671 	return TRUE;
672 }
673 
674 /*
675  * The mmap() interface should work just as well!
676  */
677 boolean_t
test_mmap()678 test_mmap()
679 {
680 	int kr, ret;
681 	uintptr_t addr = 0;
682 	int size = SUPERPAGE_SIZE;
683 
684 	addr = (uintptr_t)mmap((void*)addr, size, PROT_READ, MAP_ANON | MAP_PRIVATE, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
685 	if (addr == (uintptr_t)MAP_FAILED) {
686 		sprintf(error, "mmap()");
687 		return FALSE;
688 	}
689 	if (!(ret = check_addr0(addr, "mach_vm_allocate"))) {
690 		return ret;
691 	}
692 	if (!(ret = check_align(addr))) {
693 		return ret;
694 	}
695 	if (!(ret = check_r(addr, SUPERPAGE_SIZE, NULL))) {
696 		return ret;
697 	}
698 	if (!(ret = check_nw(addr, SUPERPAGE_SIZE))) {
699 		return ret;
700 	}
701 	kr = munmap((void*)addr, size);
702 	if (!(ret = check_kr(kr, "munmap"))) {
703 		return ret;
704 	}
705 	if (!(ret = check_nr(addr, size, NULL))) {
706 		return ret;
707 	}
708 
709 	return TRUE;
710 }
711 
712 /*
713  * Tests one allocation/deallocaton cycle; used in a loop this tests for leaks
714  */
715 boolean_t
test_alloc_dealloc()716 test_alloc_dealloc()
717 {
718 	mach_vm_address_t addr = 0;
719 	mach_vm_size_t  size = SUPERPAGE_SIZE;
720 	int kr, ret;
721 
722 	kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
723 	if (!(ret = check_kr(kr, "mach_vm_allocate"))) {
724 		return ret;
725 	}
726 	if (!(ret = check_addr0(addr, "mach_vm_allocate"))) {
727 		return ret;
728 	}
729 	if (!(ret = check_align(addr))) {
730 		return ret;
731 	}
732 	if (!(ret = check_rw(addr, size))) {
733 		return ret;
734 	}
735 	kr = mach_vm_deallocate(mach_task_self(), addr, size);
736 	if (!(ret = check_kr(kr, "mach_vm_deallocate"))) {
737 		return ret;
738 	}
739 	return TRUE;
740 }
741 
742 test_t test[] = {
743 	{ "allocate one page anywhere", test_allocate },
744 	{ "deallocate a page", test_deallocate },
745 	{ "allocate a SIZE_ANY page anywhere", test_allocate_size_any },
746 	{ "allocate one page at a fixed address", test_allocatefixed },
747 	{ "allocate one page at an unaligned fixed address", test_allocateunalignedfixed },
748 	{ "deallocate sub-page", test_deallocatesubpage },
749 	{ "allocate already allocated subpage", test_reallocate },
750 	{ "wire a page", test_wire },
751 	{ "unwire a page", test_unwire },
752 	{ "make page readonly", test_readonly },
753 	{ "make sub-page readonly", test_readonlysubpage },
754 	{ "file I/O", test_fileio },
755 	{ "mmap()", test_mmap },
756 	{ "fork", test_fork },
757 };
758 #define TESTS ((int)(sizeof(test)/sizeof(*test)))
759 
760 boolean_t
testit(int i)761 testit(int i)
762 {
763 	boolean_t ret;
764 
765 	error[0] = 0;
766 	printf("Test #%d \"%s\"...", i + 1, test[i].description);
767 	ret = test[i].fn();
768 	if (ret) {
769 		printf("OK\n");
770 	} else {
771 		printf("FAILED!");
772 		if (error[0]) {
773 			printf(" (%s)\n", error);
774 		} else {
775 			printf("\n");
776 		}
777 	}
778 }
779 
780 int
main(int argc,char ** argv)781 main(int argc, char **argv)
782 {
783 	int i;
784 	uint64_t time1, time2;
785 
786 	int mode = 0;
787 	if (argc > 1) {
788 		if (!strcmp(argv[1], "-h")) {
789 			printf("Usage: %s <mode>\n", argv[0]);
790 			printf("\tmode = 0:  test all cases\n");
791 			printf("\tmode = -1: allocate/deallocate until failure\n");
792 			printf("\tmode > 0:  run test <tmode>\n");
793 			exit(0);
794 		}
795 		mode = atoi(argv[1]);
796 	}
797 
798 	/* install SIGBUS handler */
799 	struct sigaction my_sigaction;
800 	my_sigaction.sa_handler = test_signal_handler;
801 	my_sigaction.sa_flags = SA_RESTART;
802 	my_sigaction.sa_mask = 0;
803 	sigaction( SIGBUS, &my_sigaction, NULL );
804 	sigaction( SIGSEGV, &my_sigaction, NULL );
805 
806 	if (mode > 0) {           /* one specific test */
807 		testit(mode - 1);
808 	}
809 
810 	if (mode == 0) {  /* test all cases */
811 		printf("Running %d tests:\n", TESTS);
812 		for (i = 0; i < TESTS; i++) {
813 			testit(i);
814 		}
815 	}
816 	if (mode == -1) { /* alloc/dealloc */
817 		boolean_t ret;
818 		do {
819 			ret = test_alloc_dealloc(TRUE);
820 			printf(".");
821 			fflush(stdout);
822 		} while (ret);
823 		if (error[0]) {
824 			printf(" (%s)\n", error);
825 		}
826 	}
827 	return 0;
828 }
829