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 */
33 void test_signal_handler(int signo
)
35 longjmp(resume
, signo
);
49 check_kr(int kr
, char *fn
) {
51 sprintf(error
, "%s() returned %d", fn
, kr
);
58 check_addr0(mach_vm_address_t addr
, char *fn
) {
60 sprintf(error
, "%s() returned address 0", fn
);
67 check_addr(mach_vm_address_t addr1
, mach_vm_address_t addr2
, char *fn
) {
69 sprintf(error
, "%s() returned address %llx instead of %llx", fn
, addr1
, addr2
);
76 check_align(mach_vm_address_t addr
) {
77 if (addr
& !SUPERPAGE_MASK
) {
78 sprintf(error
, "address not aligned properly: 0x%llx", addr
);
85 check_r(mach_vm_address_t addr
, mach_vm_size_t size
, int *res
) {
86 volatile char *data
= (char*)(uintptr_t)addr
;
89 if ((sig
= setjmp(resume
)) != 0) {
90 sprintf(error
, "%s when reading", signame
[sig
]);
94 for (i
=0; i
<size
; i
++)
103 /* check that no subpage of the superpage is readable */
105 check_nr(mach_vm_address_t addr
, mach_vm_size_t size
, int *res
) {
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");
118 check_w(mach_vm_address_t addr
, mach_vm_size_t size
) {
119 char *data
= (char*)(uintptr_t)addr
;
122 if ((sig
= setjmp(resume
)) != 0) {
123 sprintf(error
, "%s when writing", signame
[sig
]);
127 for (i
=0; i
<size
; i
++)
128 (data
)[i
] = i
& 0xFF;
134 check_nw(mach_vm_address_t addr
, mach_vm_size_t size
) {
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");
148 check_rw(mach_vm_address_t addr
, mach_vm_size_t size
) {
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");
161 mach_vm_address_t global_addr
= 0;
162 mach_vm_size_t global_size
= 0;
165 * If we allocate a 2 MB superpage read-write without specifying an address,
166 * - the call should succeed
168 * - return a 2 MB aligned address
169 * - the memory should be readable and writable
176 global_size
= SUPERPAGE_SIZE
;
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
;
188 * If we deallocate a superpage,
189 * - the call should succeed
190 * - make the memory inaccessible
194 mach_vm_size_t size
= SUPERPAGE_SIZE
;
198 sprintf(error
, "skipped deallocation");
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
;
208 * If we allocate a superpage of any size read-write without specifying an address
209 * - the call should succeed
211 * - the memory should be readable and writable
212 * If we deallocate it,
213 * - the call should succeed
214 * - make the memory inaccessible
217 test_allocate_size_any() {
220 mach_vm_address_t addr
= 0;
221 mach_vm_size_t size
= 2*PAGE_SIZE
; /* will be rounded up to some superpage size */
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
;
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
243 test_allocatefixed() {
246 mach_vm_address_t addr
= FIXED_ADDRESS1
;
247 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
260 * If we allocate a 2 MB superpage read-write at an unaligned address,
261 * - the call should fail
264 test_allocateunalignedfixed() {
267 mach_vm_address_t addr
= FIXED_ADDRESS2
;
268 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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");
280 * If we allocate an amount of memory not divisible by 2 MB as a 2 MB superpage
281 * - the call should fail
284 test_allocateoddsize() {
287 mach_vm_address_t addr
= FIXED_ADDRESS1
;
288 mach_vm_size_t size
= PAGE_SIZE
; /* != 2 MB */
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");
300 * If we deallocate a sub-page of a superpage,
301 * - the call should succeed
302 * - make the complete memory inaccessible
305 test_deallocatesubpage() {
308 mach_vm_address_t addr
= 0;
309 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
320 * If we try to allocate memory occupied by superpages as normal pages
321 * - the call should fail
325 mach_vm_address_t addr
= 0, addr2
;
326 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
;
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
);
344 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
345 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
350 * If we try to wire superpages
351 * - the call should succeed
352 * - the memory should remain readable and writable
358 mach_vm_address_t addr
= 0;
359 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
364 kr
= mach_vm_wire(mach_host_self(), mach_task_self(), addr
, size
, VM_PROT_WRITE
| VM_PROT_READ
);
366 if (!geteuid()) /* may fail as user */
367 if (!(ret
= check_kr(kr
, "mach_vm_wire"))) return ret
;
369 if (!(ret
= check_rw(addr
, size
))) return ret
;
371 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
372 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
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.
387 mach_vm_address_t addr
= 0;
388 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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");
399 if (!(ret
= check_rw(addr
, size
))) return ret
;
401 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
402 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
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
417 mach_vm_address_t addr
= 0;
418 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
423 mach_vm_protect(mach_task_self(), addr
, size
, 0, VM_PROT_READ
);
424 if (!(ret
= check_kr(kr
, "mach_vm_protect"))) return ret
;
426 if (!(ret
= check_r(addr
, size
, NULL
))) return ret
;
427 if (!(ret
= check_nw(addr
, size
))) return ret
;
429 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
430 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
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
442 test_readonlysubpage() {
445 mach_vm_address_t addr
= 0;
446 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
;
454 if (!(ret
= check_r(addr
, size
, NULL
))) return ret
;
455 if (!(ret
= check_nw(addr
, size
))) return ret
;
457 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
458 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
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
470 mach_vm_address_t addr
= 0;
471 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
479 if ((pid
=fork())) { /* parent */
480 if (!(ret
= check_rw(addr
, size
))) return ret
;
481 waitpid(pid
, &ret
, 0);
483 sprintf(error
, "child could access superpage");
487 if (!(ret
= check_nr(addr
, size
, NULL
))) exit(ret
);
491 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
492 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
497 * Doing file I/O with superpages
499 * - should behave the same as with base pages (i.e. no bad data)
501 #define FILENAME "/System/Library/Kernels/kernel"
504 mach_vm_address_t addr1
= 0;
505 mach_vm_address_t addr2
= 0;
506 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
;
519 if ((fd
= open(FILENAME
, O_RDONLY
))<0) {
520 sprintf(error
, "couldn't open %s", FILENAME
);
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)");
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)");
538 if (memcmp((void*)(uintptr_t)addr1
, (void*)(uintptr_t)addr2
, bytes
)) {
539 sprintf(error
, "read data corrupt");
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
;
551 * The mmap() interface should work just as well!
557 int size
= SUPERPAGE_SIZE
;
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()");
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
;
576 * Tests one allocation/deallocaton cycle; used in a loop this tests for leaks
579 test_alloc_dealloc() {
580 mach_vm_address_t addr
= 0;
581 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
},
610 #define TESTS ((int)(sizeof(test)/sizeof(*test)))
617 printf ("Test #%d \"%s\"...", i
+1, test
[i
].description
);
624 printf (" (%s)\n", error
);
630 int main(int argc
, char **argv
) {
632 uint64_t time1
, time2
;
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");
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
);
654 if (mode
>0) /* one specific test */
657 if (mode
==0) { /* test all cases */
658 printf("Running %d tests:\n", TESTS
);
659 for (i
=0; i
<TESTS
; i
++) {
663 if (mode
==-1) { /* alloc/dealloc */
666 ret
= test_alloc_dealloc(TRUE
);
671 printf (" (%s)\n", error
);