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