2 * This tests the Mac OS X Superpage API introduced in 10.7
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
12 #include <mach/mach.h>
13 #include <mach/mach_vm.h>
19 #define SUPERPAGE_SIZE (2*1024*1024)
20 #define SUPERPAGE_MASK (-SUPERPAGE_SIZE)
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 */
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 */
34 test_signal_handler(int signo
)
36 longjmp(resume
, signo
);
50 check_kr(int kr
, char *fn
)
53 sprintf(error
, "%s() returned %d", fn
, kr
);
60 check_addr0(mach_vm_address_t addr
, char *fn
)
63 sprintf(error
, "%s() returned address 0", fn
);
70 check_addr(mach_vm_address_t addr1
, mach_vm_address_t addr2
, char *fn
)
73 sprintf(error
, "%s() returned address %llx instead of %llx", fn
, addr1
, addr2
);
80 check_align(mach_vm_address_t addr
)
82 if (addr
& !SUPERPAGE_MASK
) {
83 sprintf(error
, "address not aligned properly: 0x%llx", addr
);
90 check_r(mach_vm_address_t addr
, mach_vm_size_t size
, int *res
)
92 volatile char *data
= (char*)(uintptr_t)addr
;
95 if ((sig
= setjmp(resume
)) != 0) {
96 sprintf(error
, "%s when reading", signame
[sig
]);
100 for (i
= 0; i
< size
; i
++) {
111 /* check that no subpage of the superpage is readable */
113 check_nr(mach_vm_address_t addr
, mach_vm_size_t size
, int *res
)
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");
127 check_w(mach_vm_address_t addr
, mach_vm_size_t size
)
129 char *data
= (char*)(uintptr_t)addr
;
132 if ((sig
= setjmp(resume
)) != 0) {
133 sprintf(error
, "%s when writing", signame
[sig
]);
137 for (i
= 0; i
< size
; i
++) {
138 (data
)[i
] = i
& 0xFF;
145 check_nw(mach_vm_address_t addr
, mach_vm_size_t size
)
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");
160 check_rw(mach_vm_address_t addr
, mach_vm_size_t size
)
164 if (!(ret
= check_w(addr
, size
))) {
167 if (!(ret
= check_r(addr
, size
, &res
))) {
170 if ((size
== SUPERPAGE_SIZE
) && (res
!= 0xfff00000)) {
171 sprintf(error
, "checksum error");
178 mach_vm_address_t global_addr
= 0;
179 mach_vm_size_t global_size
= 0;
182 * If we allocate a 2 MB superpage read-write without specifying an address,
183 * - the call should succeed
185 * - return a 2 MB aligned address
186 * - the memory should be readable and writable
194 global_size
= SUPERPAGE_SIZE
;
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"))) {
200 if (!(ret
= check_addr0(global_addr
, "mach_vm_allocate"))) {
203 if (!(ret
= check_align(global_addr
))) {
206 if (!(ret
= check_rw(global_addr
, global_size
))) {
214 * If we deallocate a superpage,
215 * - the call should succeed
216 * - make the memory inaccessible
221 mach_vm_size_t size
= SUPERPAGE_SIZE
;
225 sprintf(error
, "skipped deallocation");
228 kr
= mach_vm_deallocate(mach_task_self(), global_addr
, global_size
);
229 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
232 if (!(ret
= check_nr(global_addr
, size
, NULL
))) {
239 * If we allocate a superpage of any size read-write without specifying an address
240 * - the call should succeed
242 * - the memory should be readable and writable
243 * If we deallocate it,
244 * - the call should succeed
245 * - make the memory inaccessible
248 test_allocate_size_any()
252 mach_vm_address_t addr
= 0;
253 mach_vm_size_t size
= 2 * PAGE_SIZE
; /* will be rounded up to some superpage size */
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"))) {
259 if (!(ret
= check_addr0(addr
, "mach_vm_allocate"))) {
262 if (!(ret
= check_rw(addr
, size
))) {
265 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
266 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
269 if (!(ret
= check_nr(addr
, size
, NULL
))) {
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
289 mach_vm_address_t addr
= FIXED_ADDRESS1
;
290 mach_vm_size_t size
= SUPERPAGE_SIZE
;
292 kr
= mach_vm_allocate(mach_task_self(), &addr
, size
, VM_FLAGS_SUPERPAGE_SIZE_2MB
);
293 if (!(ret
= check_kr(kr
, "mach_vm_allocate"))) {
296 if (!(ret
= check_addr(addr
, FIXED_ADDRESS1
, "mach_vm_allocate"))) {
299 if (!(ret
= check_rw(addr
, size
))) {
302 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
303 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
306 if (!(ret
= check_nr(addr
, size
, NULL
))) {
313 * If we allocate a 2 MB superpage read-write at an unaligned address,
314 * - the call should fail
317 test_allocateunalignedfixed()
321 mach_vm_address_t addr
= FIXED_ADDRESS2
;
322 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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");
334 * If we allocate an amount of memory not divisible by 2 MB as a 2 MB superpage
335 * - the call should fail
338 test_allocateoddsize()
342 mach_vm_address_t addr
= FIXED_ADDRESS1
;
343 mach_vm_size_t size
= PAGE_SIZE
; /* != 2 MB */
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");
355 * If we deallocate a sub-page of a superpage,
356 * - the call should succeed
357 * - make the complete memory inaccessible
360 test_deallocatesubpage()
364 mach_vm_address_t addr
= 0;
365 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
371 kr
= mach_vm_deallocate(mach_task_self(), addr
+ PAGE_SIZE
, size
);
372 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
375 if (!(ret
= check_nr(addr
, size
, NULL
))) {
382 * If we try to allocate memory occupied by superpages as normal pages
383 * - the call should fail
388 mach_vm_address_t addr
= 0, addr2
;
389 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
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
;
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
);
409 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
410 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
417 * If we try to wire superpages
418 * - the call should succeed
419 * - the memory should remain readable and writable
426 mach_vm_address_t addr
= 0;
427 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
434 kr
= mach_vm_wire(mach_host_self(), mach_task_self(), addr
, size
, VM_PROT_WRITE
| VM_PROT_READ
);
436 if (!geteuid()) { /* may fail as user */
437 if (!(ret
= check_kr(kr
, "mach_vm_wire"))) {
442 if (!(ret
= check_rw(addr
, size
))) {
446 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
447 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
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.
465 mach_vm_address_t addr
= 0;
466 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
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");
479 if (!(ret
= check_rw(addr
, size
))) {
483 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
484 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
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
502 mach_vm_address_t addr
= 0;
503 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
510 mach_vm_protect(mach_task_self(), addr
, size
, 0, VM_PROT_READ
);
511 if (!(ret
= check_kr(kr
, "mach_vm_protect"))) {
515 if (!(ret
= check_r(addr
, size
, NULL
))) {
518 if (!(ret
= check_nw(addr
, size
))) {
522 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
523 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
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
537 test_readonlysubpage()
541 mach_vm_address_t addr
= 0;
542 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
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"))) {
554 if (!(ret
= check_r(addr
, size
, NULL
))) {
557 if (!(ret
= check_nw(addr
, size
))) {
561 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
562 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
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
577 mach_vm_address_t addr
= 0;
578 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
588 if ((pid
= fork())) { /* parent */
589 if (!(ret
= check_rw(addr
, size
))) {
592 waitpid(pid
, &ret
, 0);
594 sprintf(error
, "child could access superpage");
598 if (!(ret
= check_nr(addr
, size
, NULL
))) {
604 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
605 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
612 * Doing file I/O with superpages
614 * - should behave the same as with base pages (i.e. no bad data)
616 #define FILENAME "/System/Library/Kernels/kernel"
620 mach_vm_address_t addr1
= 0;
621 mach_vm_address_t addr2
= 0;
622 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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)"))) {
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)"))) {
639 if ((fd
= open(FILENAME
, O_RDONLY
)) < 0) {
640 sprintf(error
, "couldn't open %s", FILENAME
);
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)");
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)");
658 if (memcmp((void*)(uintptr_t)addr1
, (void*)(uintptr_t)addr2
, bytes
)) {
659 sprintf(error
, "read data corrupt");
663 kr
= mach_vm_deallocate(mach_task_self(), addr1
, size
);
664 if (!(ret
= check_kr(kr
, "mach_vm_deallocate (1)"))) {
667 kr
= mach_vm_deallocate(mach_task_self(), addr2
, size
);
668 if (!(ret
= check_kr(kr
, "mach_vm_deallocate (2)"))) {
675 * The mmap() interface should work just as well!
682 int size
= SUPERPAGE_SIZE
;
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()");
689 if (!(ret
= check_addr0(addr
, "mach_vm_allocate"))) {
692 if (!(ret
= check_align(addr
))) {
695 if (!(ret
= check_r(addr
, SUPERPAGE_SIZE
, NULL
))) {
698 if (!(ret
= check_nw(addr
, SUPERPAGE_SIZE
))) {
701 kr
= munmap((void*)addr
, size
);
702 if (!(ret
= check_kr(kr
, "munmap"))) {
705 if (!(ret
= check_nr(addr
, size
, NULL
))) {
713 * Tests one allocation/deallocaton cycle; used in a loop this tests for leaks
718 mach_vm_address_t addr
= 0;
719 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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"))) {
726 if (!(ret
= check_addr0(addr
, "mach_vm_allocate"))) {
729 if (!(ret
= check_align(addr
))) {
732 if (!(ret
= check_rw(addr
, size
))) {
735 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
736 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) {
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
},
758 #define TESTS ((int)(sizeof(test)/sizeof(*test)))
766 printf("Test #%d \"%s\"...", i
+ 1, test
[i
].description
);
773 printf(" (%s)\n", error
);
781 main(int argc
, char **argv
)
784 uint64_t time1
, time2
;
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");
795 mode
= atoi(argv
[1]);
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
);
806 if (mode
> 0) { /* one specific test */
810 if (mode
== 0) { /* test all cases */
811 printf("Running %d tests:\n", TESTS
);
812 for (i
= 0; i
< TESTS
; i
++) {
816 if (mode
== -1) { /* alloc/dealloc */
819 ret
= test_alloc_dealloc(TRUE
);
824 printf(" (%s)\n", error
);