6 #include <mach/mach_vm.h>
12 #define SUPERPAGE_SIZE (2*1024*1024)
13 #define SUPERPAGE_MASK (-SUPERPAGE_SIZE)
15 #define MAP_SUPERPAGE 0x2000
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 */
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 */
28 void test_signal_handler(int signo
)
30 longjmp(resume
, signo
);
44 check_kr(int kr
, char *fn
) {
46 sprintf(error
, "%s() returned %d", fn
, kr
);
53 check_addr0(mach_vm_address_t addr
, char *fn
) {
55 sprintf(error
, "%s() returned address 0", fn
);
62 check_addr(mach_vm_address_t addr1
, mach_vm_address_t addr2
, char *fn
) {
64 sprintf(error
, "%s() returned address %llx instead of %llx", fn
, addr1
, addr2
);
71 check_align(mach_vm_address_t addr
) {
72 if (addr
& !SUPERPAGE_MASK
) {
73 sprintf(error
, "address not aligned properly: 0x%llx", addr
);
80 check_r(mach_vm_address_t addr
, mach_vm_size_t size
, int *res
) {
81 volatile char *data
= (char*)(uintptr_t)addr
;
84 if ((sig
= setjmp(resume
)) != 0) {
85 sprintf(error
, "%s when reading", signame
[sig
]);
89 for (i
=0; i
<size
; i
++)
98 /* check that no subpage of the superpage is readable */
100 check_nr(mach_vm_address_t addr
, mach_vm_size_t size
, int *res
) {
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");
114 check_w(mach_vm_address_t addr
, mach_vm_size_t size
) {
115 char *data
= (char*)(uintptr_t)addr
;
118 if ((sig
= setjmp(resume
)) != 0) {
119 sprintf(error
, "%s when writing", signame
[sig
]);
123 for (i
=0; i
<size
; i
++)
124 (data
)[i
] = i
& 0xFF;
130 check_nw(mach_vm_address_t addr
, mach_vm_size_t size
) {
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");
144 check_rw(mach_vm_address_t addr
, mach_vm_size_t size
) {
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");
158 mach_vm_address_t global_addr
= 0;
159 mach_vm_size_t global_size
= 0;
166 global_size
= SUPERPAGE_SIZE
;
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
;
181 mach_vm_address_t addr
= 0;
182 mach_vm_size_t size
= SUPERPAGE_SIZE
;
186 sprintf(error
, "skipped deallocation");
189 kr
= mach_vm_deallocate(mach_task_self(), global_addr
, global_size
);
190 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
196 test_allocatefixed() {
199 mach_vm_address_t addr
= FIXED_ADDRESS1
;
200 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
214 test_allocateunalignedfixed() {
217 mach_vm_address_t addr
= FIXED_ADDRESS2
;
218 mach_vm_size_t size
= SUPERPAGE_SIZE
;
220 kr
= mach_vm_allocate(mach_task_self(), &addr
, size
, VM_FLAGS_SUPERPAGE_SIZE_2MB
);
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");
239 test_deallocatesubpage() {
242 mach_vm_address_t addr
= 0;
243 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
258 mach_vm_address_t addr
= 0, addr2
;
259 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
;
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
);
280 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
281 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
289 mach_vm_address_t addr
= 0;
290 mach_vm_size_t size
= SUPERPAGE_SIZE
;
292 kr
= mach_vm_allocate(mach_task_self(), &addr
, size
, VM_FLAGS_ANYWHERE
| VM_FLAGS_SUPERPAGE_SIZE_2MB
);
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
;
299 kr
= mach_vm_wire(mach_host_self(), mach_task_self(), addr
, size
, VM_PROT_WRITE
| VM_PROT_READ
);
301 if (kr
&& geteuid()) /* may fail as user */
304 if (!(ret
= check_kr(kr
, "mach_vm_wire"))) return ret
;
306 if (!(ret
= check_rw(addr
, size
))) return ret
;
308 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
309 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
318 mach_vm_address_t addr
= 0;
319 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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");
332 if (!(ret
= check_rw(addr
, size
))) return ret
;
334 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
335 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
344 mach_vm_address_t addr
= 0;
345 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
352 mach_vm_protect(mach_task_self(), addr
, size
, 0, VM_PROT_READ
);
353 if (!(ret
= check_kr(kr
, "mach_vm_protect"))) return ret
;
355 if (!(ret
= check_r(addr
, size
, NULL
))) return ret
;
356 if (!(ret
= check_nw(addr
, size
))) return ret
;
358 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
359 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
365 test_readonlysubpage() {
368 mach_vm_address_t addr
= 0;
369 // mach_vm_size_t size = SUPERPAGE_SIZE;
370 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
;
384 if (!(ret
= check_r(addr
, size
, NULL
))) return ret
;
385 if (!(ret
= check_nw(addr
, size
))) return ret
;
387 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
388 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
395 mach_vm_address_t addr
= 0;
396 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
408 if (!(ret
= check_rw(addr
, size
))) return ret
;
409 waitpid(pid
, &ret
, 0);
411 sprintf(error
, "child could access superpage");
415 /* for the child, the superpage should not be mapped */
416 if (!(ret
= check_nr(addr
, size
, NULL
))) exit(ret
);
420 kr
= mach_vm_deallocate(mach_task_self(), addr
, size
);
421 if (!(ret
= check_kr(kr
, "mach_vm_deallocate"))) return ret
;
425 #define FILENAME "/mach_kernel"
428 mach_vm_address_t addr1
= 0;
429 mach_vm_address_t addr2
= 0;
430 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
;
443 if ((fd
= open(FILENAME
, O_RDONLY
))<0) {
444 sprintf(error
, "couldn't open %s", FILENAME
);
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)");
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)");
462 if (memcmp((void*)(uintptr_t)addr1
, (void*)(uintptr_t)addr2
, bytes
)) {
463 sprintf(error
, "read data corrupt");
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
;
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
484 void *addr
= (void*)(1*1024*1024*1024 + 4096); /* 1 GB + base page (i.e. not superpage-aligned) */
487 addr
= mmap(addr
, size
, PROT_READ
, MAP_ANON
| MAP_PRIVATE
| MAP_SUPERPAGE
, -1, 0);
488 if (addr
== MAP_FAILED
) {
489 sprintf(error
, "mmap()");
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
;
496 kr
= munmap(addr
, size
);
497 if (!(ret
= check_kr(kr
, "mach_vm_deallocate (2)"))) return ret
;
504 test_alloc_dealloc() {
505 mach_vm_address_t addr
= 0;
506 mach_vm_size_t size
= SUPERPAGE_SIZE
;
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
;
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
},
532 { "mmap()", test_mmap
},
534 { "fork", test_fork
},
536 #define TESTS ((int)(sizeof(test)/sizeof(*test)))
543 printf ("Test #%d \"%s\"...", i
+1, test
[i
].description
);
550 printf (" (%s)\n", error
);
556 int main(int argc
, char **argv
) {
558 uint64_t time1
, time2
;
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");
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
);
580 if (mode
>0) /* one specific test */
583 if (mode
==0) { /* test all cases */
584 printf("Running %d tests:\n", TESTS
);
585 for (i
=0; i
<TESTS
; i
++) {
589 if (mode
==-1) { /* alloc/dealloc */
592 ret
= test_alloc_dealloc(TRUE
);
597 printf (" (%s)\n", error
);