]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/superpages/testsp.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / tools / tests / superpages / testsp.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <setjmp.h>
5 #include <mach/mach.h>
6 #include <mach/mach_vm.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <sys/mman.h>
11
12 #define SUPERPAGE_SIZE (2*1024*1024)
13 #define SUPERPAGE_MASK (-SUPERPAGE_SIZE)
14
15 #define MAP_SUPERPAGE 0x2000
16
17 #ifdef __LP64__
18 #define FIXED_ADDRESS1 (0x100000000ULL+500*1024*1024) /* at 4 GB + 500 MB virtual */
19 #define FIXED_ADDRESS2 (0x100000000ULL+502*1024*1024 + 4*1024) /* at 4 GB + 502 MB + 4 KB virtual */
20 #else
21 #define FIXED_ADDRESS1 (500*1024*1024) /* at 500 MB virtual */
22 #define FIXED_ADDRESS2 (502*1024*1024 + 4*1024) /* at 502 MB + 4 KB virtual */
23 #endif
24
25 char error[100];
26
27 jmp_buf resume;
28 void test_signal_handler(int signo)
29 {
30 longjmp(resume, signo);
31 }
32
33 char *signame[32] = {
34 [SIGBUS] "SIGBUS",
35 [SIGSEGV] "SIGSEGV"
36 };
37
38 typedef struct {
39 char *description;
40 boolean_t (*fn)();
41 } test_t;
42
43 boolean_t
44 check_kr(int kr, char *fn) {
45 if (kr) {
46 sprintf(error, "%s() returned %d", fn, kr);
47 return FALSE;
48 }
49 return TRUE;
50 }
51
52 boolean_t
53 check_addr0(mach_vm_address_t addr, char *fn) {
54 if (!addr) {
55 sprintf(error, "%s() returned address 0", fn);
56 return FALSE;
57 }
58 return TRUE;
59 }
60
61 boolean_t
62 check_addr(mach_vm_address_t addr1, mach_vm_address_t addr2, char *fn) {
63 if (addr1 != addr2) {
64 sprintf(error, "%s() returned address %llx instead of %llx", fn, addr1, addr2);
65 return FALSE;
66 }
67 return TRUE;
68 }
69
70 boolean_t
71 check_align(mach_vm_address_t addr) {
72 if (addr & !SUPERPAGE_MASK) {
73 sprintf(error, "address not aligned properly: 0x%llx", addr);
74 return FALSE;
75 }
76 return TRUE;
77 }
78
79 boolean_t
80 check_r(mach_vm_address_t addr, mach_vm_size_t size, int *res) {
81 volatile char *data = (char*)(uintptr_t)addr;
82 int i, sig, test;
83
84 if ((sig = setjmp(resume)) != 0) {
85 sprintf(error, "%s when reading", signame[sig]);
86 return FALSE;
87 }
88 test = 0;
89 for (i=0; i<size; i++)
90 test += (data)[i];
91
92 if (res)
93 *res = test;
94
95 return TRUE;
96 }
97
98 /* check that no subpage of the superpage is readable */
99 boolean_t
100 check_nr(mach_vm_address_t addr, mach_vm_size_t size, int *res) {
101 int i;
102 boolean_t ret;
103 //printf("%d\n", __LINE__);
104 for (i=0; i<size/PAGE_SIZE; i++) {
105 if ((ret = check_r(addr+i*PAGE_SIZE, PAGE_SIZE, res))) {
106 sprintf(error, "page still readable");
107 return FALSE;
108 }
109 }
110 return TRUE;
111 }
112
113 boolean_t
114 check_w(mach_vm_address_t addr, mach_vm_size_t size) {
115 char *data = (char*)(uintptr_t)addr;
116 int i, sig;
117
118 if ((sig = setjmp(resume)) != 0) {
119 sprintf(error, "%s when writing", signame[sig]);
120 return FALSE;
121 }
122
123 for (i=0; i<size; i++)
124 (data)[i] = i & 0xFF;
125
126 return TRUE;
127 }
128
129 boolean_t
130 check_nw(mach_vm_address_t addr, mach_vm_size_t size) {
131 int i;
132 boolean_t ret;
133
134 for (i=0; i<size/PAGE_SIZE; i++) {
135 if ((ret = check_w(addr+i*PAGE_SIZE, PAGE_SIZE))) {
136 sprintf(error, "page still writable");
137 return FALSE;
138 }
139 }
140 return TRUE;
141 }
142
143 boolean_t
144 check_rw(mach_vm_address_t addr, mach_vm_size_t size) {
145 int ret;
146 int res;
147 if (!(ret = check_w(addr, size))) return ret;
148 if (!(ret = check_r(addr, size, &res))) return ret;
149 // printf("res = %x\n", res);
150 if ((size==SUPERPAGE_SIZE) && (res!=0xfff00000)) {
151 sprintf(error, "checksum error");
152 return FALSE;
153 }
154
155 return TRUE;
156 }
157
158 mach_vm_address_t global_addr = 0;
159 mach_vm_size_t global_size = 0;
160
161 boolean_t
162 test_allocate() {
163 int kr, ret;
164
165 global_addr = 0;
166 global_size = SUPERPAGE_SIZE;
167
168 kr = mach_vm_allocate(mach_task_self(), &global_addr, global_size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
169 //printf("%llx", addr);
170 //printf("\n%d\n", __LINE__);
171 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
172 if (!(ret = check_addr0(global_addr, "mach_vm_allocate"))) return ret;
173 if (!(ret = check_align(global_addr))) return ret;
174 if (!(ret = check_rw(global_addr, global_size))) return ret;
175
176 return TRUE;
177 }
178
179 boolean_t
180 test_deallocate() {
181 mach_vm_address_t addr = 0;
182 mach_vm_size_t size = SUPERPAGE_SIZE;
183 int kr, ret;
184
185 if (!global_addr) {
186 sprintf(error, "skipped deallocation");
187 return FALSE;
188 }
189 kr = mach_vm_deallocate(mach_task_self(), global_addr, global_size);
190 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
191 return TRUE;
192 }
193
194
195 boolean_t
196 test_allocatefixed() {
197 int kr;
198 int ret;
199 mach_vm_address_t addr = FIXED_ADDRESS1;
200 mach_vm_size_t size = SUPERPAGE_SIZE;
201
202 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB);
203 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
204 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
205 if (!(ret = check_addr(addr, FIXED_ADDRESS1, "mach_vm_allocate"))) return ret;
206 if (!(ret = check_align(addr))) return ret;
207 if (!(ret = check_rw(addr, size))) return ret;
208 kr = mach_vm_deallocate(mach_task_self(), addr, size);
209 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
210 return TRUE;
211 }
212
213 boolean_t
214 test_allocateunalignedfixed() {
215 int kr;
216 int ret;
217 mach_vm_address_t addr = FIXED_ADDRESS2;
218 mach_vm_size_t size = SUPERPAGE_SIZE;
219
220 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB);
221 #if 0
222 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
223 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
224 if (!(ret = check_addr(addr, FIXED_ADDRESS2 & SUPERPAGE_MASK, "mach_vm_allocate"))) return ret;
225 if (!(ret = check_align(addr))) return ret;
226 if (!(ret = check_rw(addr, size))) return ret;
227 kr = mach_vm_deallocate(mach_task_self(), addr & SUPERPAGE_MASK, size);
228 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
229 #else /* is supposed to fail */
230 if ((ret = check_kr(kr, "mach_vm_allocate"))) {
231 sprintf(error, "mach_vm_allocate() should have failed");
232 return FALSE;
233 }
234 #endif
235 return TRUE;
236 }
237
238 boolean_t
239 test_deallocatesubpage() {
240 int kr;
241 int ret;
242 mach_vm_address_t addr = 0;
243 mach_vm_size_t size = SUPERPAGE_SIZE;
244
245 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
246 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
247 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
248 if (!(ret = check_align(addr))) return ret;
249 if (!(ret = check_rw(addr, size))) return ret;
250 kr = mach_vm_deallocate(mach_task_self(), addr + PAGE_SIZE, size);
251 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
252 if (!(ret = check_nr(addr, size, NULL))) return ret;
253 return TRUE;
254 }
255
256 boolean_t
257 test_reallocate() {
258 mach_vm_address_t addr = 0, addr2;
259 mach_vm_size_t size = SUPERPAGE_SIZE;
260 int kr, ret;
261 int i;
262
263 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
264 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
265 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
266 if (!(ret = check_align(addr))) return ret;
267 if (!(ret = check_rw(addr, size))) return ret;
268
269 /* attempt to allocate every sub-page of superpage */
270 for (i=0; i<SUPERPAGE_SIZE/PAGE_SIZE; i++) {
271 addr2 = addr + i*PAGE_SIZE;
272 size = PAGE_SIZE;
273 kr = mach_vm_allocate(mach_task_self(), &addr2, size, 0);
274 if ((ret = check_kr(kr, "mach_vm_allocate"))) {
275 sprintf(error, "could allocate already allocated space, page %d", i);
276 mach_vm_deallocate(mach_task_self(), addr, size);
277 return FALSE;
278 }
279 }
280 kr = mach_vm_deallocate(mach_task_self(), addr, size);
281 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
282 return TRUE;
283 }
284
285 boolean_t
286 test_wire() {
287 int kr;
288 int ret;
289 mach_vm_address_t addr = 0;
290 mach_vm_size_t size = SUPERPAGE_SIZE;
291
292 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
293
294 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
295 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
296 if (!(ret = check_align(addr))) return ret;
297 if (!(ret = check_rw(addr, size))) return ret;
298
299 kr = mach_vm_wire(mach_host_self(), mach_task_self(), addr, size, VM_PROT_WRITE | VM_PROT_READ);
300
301 if (kr && geteuid()) /* may fail as user */
302 return TRUE;
303
304 if (!(ret = check_kr(kr, "mach_vm_wire"))) return ret;
305
306 if (!(ret = check_rw(addr, size))) return ret;
307
308 kr = mach_vm_deallocate(mach_task_self(), addr, size);
309 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
310
311 return TRUE;
312 }
313
314 boolean_t
315 test_unwire() {
316 int kr;
317 int ret;
318 mach_vm_address_t addr = 0;
319 mach_vm_size_t size = SUPERPAGE_SIZE;
320
321 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
322 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
323 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
324 if (!(ret = check_align(addr))) return ret;
325
326 kr = mach_vm_wire(mach_host_self(), mach_task_self(), addr, size, VM_PROT_NONE);
327 if ((ret = check_kr(kr, "mach_vm_wire"))) {
328 sprintf(error, "could unwire");
329 return FALSE;
330 }
331
332 if (!(ret = check_rw(addr, size))) return ret;
333
334 kr = mach_vm_deallocate(mach_task_self(), addr, size);
335 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
336
337 return TRUE;
338 }
339
340 boolean_t
341 test_readonly() {
342 int kr;
343 int ret;
344 mach_vm_address_t addr = 0;
345 mach_vm_size_t size = SUPERPAGE_SIZE;
346
347 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
348 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
349 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
350 if (!(ret = check_align(addr))) return ret;
351
352 mach_vm_protect(mach_task_self(), addr, size, 0, VM_PROT_READ);
353 if (!(ret = check_kr(kr, "mach_vm_protect"))) return ret;
354
355 if (!(ret = check_r(addr, size, NULL))) return ret;
356 if (!(ret = check_nw(addr, size))) return ret;
357
358 kr = mach_vm_deallocate(mach_task_self(), addr, size);
359 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
360
361 return TRUE;
362 }
363
364 boolean_t
365 test_readonlysubpage() {
366 int kr;
367 int ret;
368 mach_vm_address_t addr = 0;
369 // mach_vm_size_t size = SUPERPAGE_SIZE;
370 mach_vm_size_t size = SUPERPAGE_SIZE;
371
372 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
373 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
374 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
375 if (!(ret = check_align(addr))) return ret;
376
377 /* changing protection on a single sub-page has to change protection for the whole superpage */
378 /* write protect second page from start */
379 //printf("+mach_vm_protect\n");
380 mach_vm_protect(mach_task_self(), addr+PAGE_SIZE, PAGE_SIZE, 0, VM_PROT_READ);
381 //printf("-mach_vm_protect\n");
382 if (!(ret = check_kr(kr, "mach_vm_protect"))) return ret;
383
384 if (!(ret = check_r(addr, size, NULL))) return ret;
385 if (!(ret = check_nw(addr, size))) return ret;
386
387 kr = mach_vm_deallocate(mach_task_self(), addr, size);
388 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
389
390 return TRUE;
391 }
392
393 boolean_t
394 test_fork() {
395 mach_vm_address_t addr = 0;
396 mach_vm_size_t size = SUPERPAGE_SIZE;
397 int kr, ret;
398 pid_t pid;
399
400 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
401 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
402 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
403 if (!(ret = check_align(addr))) return ret;
404 if (!(ret = check_rw(addr, size))) return ret;
405
406 fflush(stdout);
407 if ((pid=fork())) {
408 if (!(ret = check_rw(addr, size))) return ret;
409 waitpid(pid, &ret, 0);
410 if (!ret) {
411 sprintf(error, "child could access superpage");
412 return ret;
413 }
414 } else {
415 /* for the child, the superpage should not be mapped */
416 if (!(ret = check_nr(addr, size, NULL))) exit(ret);
417 exit(TRUE);
418 }
419
420 kr = mach_vm_deallocate(mach_task_self(), addr, size);
421 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
422 return TRUE;
423 }
424
425 #define FILENAME "/mach_kernel"
426 boolean_t
427 test_fileio() {
428 mach_vm_address_t addr1 = 0;
429 mach_vm_address_t addr2 = 0;
430 mach_vm_size_t size = SUPERPAGE_SIZE;
431 int kr, ret;
432 int fd;
433 unsigned int bytes;
434
435 /* allocate one superpage */
436 kr = mach_vm_allocate(mach_task_self(), &addr1, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
437 if (!(ret = check_kr(kr, "mach_vm_allocate (1)"))) return ret;
438
439 /* allocate base pages (superpage-sized) */
440 kr = mach_vm_allocate(mach_task_self(), &addr2, size, VM_FLAGS_ANYWHERE);
441 if (!(ret = check_kr(kr, "mach_vm_allocate (2)"))) return ret;
442
443 if ((fd = open(FILENAME, O_RDONLY))<0) {
444 sprintf(error, "couldn't open %s", FILENAME);
445 return FALSE;
446 }
447 fcntl(fd, F_NOCACHE, 1);
448 /* read kernel into superpage */
449 if ((bytes = read(fd, (void*)(uintptr_t)addr1, SUPERPAGE_SIZE)) < SUPERPAGE_SIZE) {
450 sprintf(error, "short read (1)");
451 return FALSE;
452 }
453 lseek(fd, 0, SEEK_SET);
454 /* read kernel into base pages */
455 if ((bytes = read(fd, (void*)(uintptr_t)addr2, SUPERPAGE_SIZE)) < SUPERPAGE_SIZE) {
456 sprintf(error, "short read (2)");
457 return FALSE;
458 }
459 close(fd);
460
461 /* compare */
462 if (memcmp((void*)(uintptr_t)addr1, (void*)(uintptr_t)addr2, bytes)) {
463 sprintf(error, "read data corrupt");
464 return FALSE;
465 }
466
467 kr = mach_vm_deallocate(mach_task_self(), addr1, size);
468 if (!(ret = check_kr(kr, "mach_vm_deallocate (1)"))) return ret;
469 kr = mach_vm_deallocate(mach_task_self(), addr2, size);
470 if (!(ret = check_kr(kr, "mach_vm_deallocate (2)"))) return ret;
471 return TRUE;
472 }
473
474 #ifdef MMAP
475 /*
476 * this tests several things at once:
477 * - we pass a non-superpage-aligned address and expect it to be rounded up
478 * - we pass a size < SUPERPAGE_SIZE and expect SUPERPAGE_SIZE bytes to be mapped
479 * - we set the address range to read-only and make sure it's readable, but not writable
480 */
481 boolean_t
482 test_mmap() {
483 int kr, ret;
484 void *addr = (void*)(1*1024*1024*1024 + 4096); /* 1 GB + base page (i.e. not superpage-aligned) */
485 int size = 4096;
486
487 addr = mmap(addr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_SUPERPAGE, -1, 0);
488 if (addr == MAP_FAILED) {
489 sprintf(error, "mmap()");
490 return FALSE;
491 }
492 if (!(ret = check_align((uintptr_t)addr))) return ret;
493 if (!(ret = check_r((uintptr_t)addr, SUPERPAGE_SIZE, NULL))) return ret;
494 if (!(ret = check_nw((uintptr_t)addr, SUPERPAGE_SIZE))) return ret;
495
496 kr = munmap(addr, size);
497 if (!(ret = check_kr(kr, "mach_vm_deallocate (2)"))) return ret;
498
499 return TRUE;
500 }
501 #endif
502
503 boolean_t
504 test_alloc_dealloc() {
505 mach_vm_address_t addr = 0;
506 mach_vm_size_t size = SUPERPAGE_SIZE;
507 int kr, ret;
508
509 kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB);
510 if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret;
511 if (!(ret = check_addr0(addr, "mach_vm_allocate"))) return ret;
512 if (!(ret = check_align(addr))) return ret;
513 if (!(ret = check_rw(addr, size))) return ret;
514 kr = mach_vm_deallocate(mach_task_self(), addr, size);
515 if (!(ret = check_kr(kr, "mach_vm_deallocate"))) return ret;
516 return TRUE;
517 }
518
519 test_t test[] = {
520 { "allocate one page anywhere", test_allocate },
521 { "deallocate a page", test_deallocate },
522 { "allocate one page at a fixed address", test_allocatefixed },
523 { "allocate one page at an unaligned fixed address", test_allocateunalignedfixed },
524 { "deallocate sub-page", test_deallocatesubpage },
525 { "allocate already allocated subpage", test_reallocate },
526 { "wire a page", test_wire },
527 { "unwire a page", test_unwire },
528 { "make page readonly", test_readonly },
529 { "make sub-page readonly", test_readonlysubpage },
530 { "file I/O", test_fileio },
531 #ifdef MMAP
532 { "mmap()", test_mmap },
533 #endif
534 { "fork", test_fork },
535 };
536 #define TESTS ((int)(sizeof(test)/sizeof(*test)))
537
538 boolean_t
539 testit(int i) {
540 boolean_t ret;
541
542 error[0] = 0;
543 printf ("Test #%d \"%s\"...", i+1, test[i].description);
544 ret = test[i].fn();
545 if (ret)
546 printf ("OK\n");
547 else {
548 printf ("FAILED!");
549 if (error[0])
550 printf (" (%s)\n", error);
551 else
552 printf ("\n");
553 }
554 }
555
556 int main(int argc, char **argv) {
557 int i;
558 uint64_t time1, time2;
559
560 int mode = 0;
561 if (argc>1) {
562 if (!strcmp(argv[1], "-h")) {
563 printf("Usage: %s <mode>\n", argv[0]);
564 printf("\tmode = 0: test all cases\n");
565 printf("\tmode = -1: allocate/deallocate until failure\n");
566 printf("\tmode > 0: run test <tmode>\n");
567 exit(0);
568 }
569 mode=atoi(argv[1]);
570 }
571
572 /* install SIGBUS handler */
573 struct sigaction my_sigaction;
574 my_sigaction.sa_handler = test_signal_handler;
575 my_sigaction.sa_flags = SA_RESTART;
576 my_sigaction.sa_mask = 0;
577 sigaction( SIGBUS, &my_sigaction, NULL );
578 sigaction( SIGSEGV, &my_sigaction, NULL );
579
580 if (mode>0) /* one specific test */
581 testit(mode-1);
582
583 if (mode==0) { /* test all cases */
584 printf("Running %d tests:\n", TESTS);
585 for (i=0; i<TESTS; i++) {
586 testit(i);
587 }
588 }
589 if (mode==-1) { /* alloc/dealloc */
590 boolean_t ret;
591 do {
592 ret = test_alloc_dealloc(TRUE);
593 printf(".");
594 fflush(stdout);
595 } while (ret);
596 if (error[0])
597 printf (" (%s)\n", error);
598 }
599 return 0;
600 }