]>
Commit | Line | Data |
---|---|---|
6d2010ae A |
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 | */ | |
b0d623f7 A |
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 | ||
b0d623f7 A |
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 | ||
30 | char error[100]; | |
31 | ||
32 | jmp_buf resume; | |
33 | void test_signal_handler(int signo) | |
34 | { | |
35 | longjmp(resume, signo); | |
36 | } | |
37 | ||
38 | char *signame[32] = { | |
39 | [SIGBUS] "SIGBUS", | |
40 | [SIGSEGV] "SIGSEGV" | |
41 | }; | |
42 | ||
43 | typedef struct { | |
44 | char *description; | |
45 | boolean_t (*fn)(); | |
46 | } test_t; | |
47 | ||
48 | boolean_t | |
49 | check_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 | ||
57 | boolean_t | |
58 | check_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 | ||
66 | boolean_t | |
67 | check_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 | ||
75 | boolean_t | |
76 | check_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 | ||
84 | boolean_t | |
85 | check_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 */ | |
104 | boolean_t | |
105 | check_nr(mach_vm_address_t addr, mach_vm_size_t size, int *res) { | |
106 | int i; | |
107 | boolean_t ret; | |
b0d623f7 A |
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 | ||
117 | boolean_t | |
118 | check_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 | ||
133 | boolean_t | |
134 | check_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 | ||
147 | boolean_t | |
148 | check_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; | |
b0d623f7 A |
153 | if ((size==SUPERPAGE_SIZE) && (res!=0xfff00000)) { |
154 | sprintf(error, "checksum error"); | |
155 | return FALSE; | |
156 | } | |
157 | ||
158 | return TRUE; | |
159 | } | |
160 | ||
161 | mach_vm_address_t global_addr = 0; | |
162 | mach_vm_size_t global_size = 0; | |
163 | ||
6d2010ae A |
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 | */ | |
b0d623f7 A |
171 | boolean_t |
172 | test_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); | |
b0d623f7 A |
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 | ||
6d2010ae A |
187 | /* |
188 | * If we deallocate a superpage, | |
189 | * - the call should succeed | |
190 | * - make the memory inaccessible | |
191 | */ | |
b0d623f7 A |
192 | boolean_t |
193 | test_deallocate() { | |
b0d623f7 A |
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; | |
6d2010ae | 203 | if (!(ret = check_nr(global_addr, size, NULL))) return ret; |
b0d623f7 A |
204 | return TRUE; |
205 | } | |
206 | ||
6d2010ae A |
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 | */ | |
216 | boolean_t | |
217 | test_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 | } | |
b0d623f7 | 232 | |
6d2010ae A |
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 | */ | |
b0d623f7 A |
242 | boolean_t |
243 | test_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; | |
b0d623f7 | 251 | if (!(ret = check_addr(addr, FIXED_ADDRESS1, "mach_vm_allocate"))) return ret; |
b0d623f7 A |
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; | |
6d2010ae | 255 | if (!(ret = check_nr(addr, size, NULL))) return ret; |
b0d623f7 A |
256 | return TRUE; |
257 | } | |
258 | ||
6d2010ae A |
259 | /* |
260 | * If we allocate a 2 MB superpage read-write at an unaligned address, | |
261 | * - the call should fail | |
262 | */ | |
b0d623f7 A |
263 | boolean_t |
264 | test_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); | |
6d2010ae A |
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 | */ | |
283 | boolean_t | |
284 | test_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 */ | |
b0d623f7 A |
292 | if ((ret = check_kr(kr, "mach_vm_allocate"))) { |
293 | sprintf(error, "mach_vm_allocate() should have failed"); | |
294 | return FALSE; | |
295 | } | |
b0d623f7 A |
296 | return TRUE; |
297 | } | |
298 | ||
6d2010ae A |
299 | /* |
300 | * If we deallocate a sub-page of a superpage, | |
301 | * - the call should succeed | |
302 | * - make the complete memory inaccessible | |
303 | */ | |
b0d623f7 A |
304 | boolean_t |
305 | test_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; | |
b0d623f7 A |
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 | ||
6d2010ae A |
319 | /* |
320 | * If we try to allocate memory occupied by superpages as normal pages | |
321 | * - the call should fail | |
322 | */ | |
b0d623f7 A |
323 | boolean_t |
324 | test_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; | |
b0d623f7 A |
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 | ||
6d2010ae A |
349 | /* |
350 | * If we try to wire superpages | |
351 | * - the call should succeed | |
352 | * - the memory should remain readable and writable | |
353 | */ | |
b0d623f7 A |
354 | boolean_t |
355 | test_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); | |
b0d623f7 | 362 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) return ret; |
b0d623f7 A |
363 | |
364 | kr = mach_vm_wire(mach_host_self(), mach_task_self(), addr, size, VM_PROT_WRITE | VM_PROT_READ); | |
365 | ||
6d2010ae A |
366 | if (!geteuid()) /* may fail as user */ |
367 | if (!(ret = check_kr(kr, "mach_vm_wire"))) return ret; | |
b0d623f7 A |
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 | ||
6d2010ae A |
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 | */ | |
b0d623f7 A |
383 | boolean_t |
384 | test_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; | |
b0d623f7 A |
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 | ||
6d2010ae A |
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 | */ | |
b0d623f7 A |
413 | boolean_t |
414 | test_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; | |
b0d623f7 A |
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 | ||
6d2010ae A |
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 | */ | |
b0d623f7 A |
441 | boolean_t |
442 | test_readonlysubpage() { | |
443 | int kr; | |
444 | int ret; | |
445 | mach_vm_address_t addr = 0; | |
b0d623f7 A |
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; | |
b0d623f7 | 450 | |
b0d623f7 | 451 | mach_vm_protect(mach_task_self(), addr+PAGE_SIZE, PAGE_SIZE, 0, VM_PROT_READ); |
b0d623f7 A |
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 | ||
6d2010ae A |
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 | */ | |
b0d623f7 A |
468 | boolean_t |
469 | test_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; | |
b0d623f7 A |
477 | |
478 | fflush(stdout); | |
6d2010ae | 479 | if ((pid=fork())) { /* parent */ |
b0d623f7 A |
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 | } | |
6d2010ae | 486 | } else { /* child */ |
b0d623f7 A |
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 | ||
6d2010ae A |
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 | */ | |
b0d623f7 A |
501 | #define FILENAME "/mach_kernel" |
502 | boolean_t | |
503 | test_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 | ||
b0d623f7 | 550 | /* |
6d2010ae | 551 | * The mmap() interface should work just as well! |
b0d623f7 A |
552 | */ |
553 | boolean_t | |
554 | test_mmap() { | |
555 | int kr, ret; | |
6d2010ae A |
556 | uintptr_t addr = 0; |
557 | int size = SUPERPAGE_SIZE; | |
b0d623f7 | 558 | |
6d2010ae A |
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) { | |
b0d623f7 A |
561 | sprintf(error, "mmap()"); |
562 | return FALSE; | |
563 | } | |
6d2010ae A |
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; | |
b0d623f7 A |
571 | |
572 | return TRUE; | |
573 | } | |
b0d623f7 | 574 | |
6d2010ae A |
575 | /* |
576 | * Tests one allocation/deallocaton cycle; used in a loop this tests for leaks | |
577 | */ | |
b0d623f7 A |
578 | boolean_t |
579 | test_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 | ||
594 | test_t test[] = { | |
595 | { "allocate one page anywhere", test_allocate }, | |
596 | { "deallocate a page", test_deallocate }, | |
6d2010ae | 597 | { "allocate a SIZE_ANY page anywhere", test_allocate_size_any }, |
b0d623f7 A |
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 }, | |
b0d623f7 | 607 | { "mmap()", test_mmap }, |
b0d623f7 A |
608 | { "fork", test_fork }, |
609 | }; | |
610 | #define TESTS ((int)(sizeof(test)/sizeof(*test))) | |
611 | ||
612 | boolean_t | |
613 | testit(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 | ||
630 | int 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 | } |