]>
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; | |
0a7de745 A |
33 | void |
34 | test_signal_handler(int signo) | |
35 | { | |
b0d623f7 A |
36 | longjmp(resume, signo); |
37 | } | |
38 | ||
39 | char *signame[32] = { | |
40 | [SIGBUS] "SIGBUS", | |
41 | [SIGSEGV] "SIGSEGV" | |
42 | }; | |
43 | ||
44 | typedef struct { | |
45 | char *description; | |
46 | boolean_t (*fn)(); | |
47 | } test_t; | |
48 | ||
49 | boolean_t | |
0a7de745 A |
50 | check_kr(int kr, char *fn) |
51 | { | |
b0d623f7 A |
52 | if (kr) { |
53 | sprintf(error, "%s() returned %d", fn, kr); | |
54 | return FALSE; | |
55 | } | |
56 | return TRUE; | |
57 | } | |
58 | ||
59 | boolean_t | |
0a7de745 A |
60 | check_addr0(mach_vm_address_t addr, char *fn) |
61 | { | |
b0d623f7 A |
62 | if (!addr) { |
63 | sprintf(error, "%s() returned address 0", fn); | |
64 | return FALSE; | |
65 | } | |
66 | return TRUE; | |
67 | } | |
68 | ||
69 | boolean_t | |
0a7de745 A |
70 | check_addr(mach_vm_address_t addr1, mach_vm_address_t addr2, char *fn) |
71 | { | |
b0d623f7 A |
72 | if (addr1 != addr2) { |
73 | sprintf(error, "%s() returned address %llx instead of %llx", fn, addr1, addr2); | |
74 | return FALSE; | |
75 | } | |
76 | return TRUE; | |
77 | } | |
78 | ||
79 | boolean_t | |
0a7de745 A |
80 | check_align(mach_vm_address_t addr) |
81 | { | |
b0d623f7 A |
82 | if (addr & !SUPERPAGE_MASK) { |
83 | sprintf(error, "address not aligned properly: 0x%llx", addr); | |
84 | return FALSE; | |
85 | } | |
86 | return TRUE; | |
87 | } | |
88 | ||
89 | boolean_t | |
0a7de745 A |
90 | check_r(mach_vm_address_t addr, mach_vm_size_t size, int *res) |
91 | { | |
b0d623f7 A |
92 | volatile char *data = (char*)(uintptr_t)addr; |
93 | int i, sig, test; | |
0a7de745 | 94 | |
b0d623f7 A |
95 | if ((sig = setjmp(resume)) != 0) { |
96 | sprintf(error, "%s when reading", signame[sig]); | |
97 | return FALSE; | |
98 | } | |
99 | test = 0; | |
0a7de745 | 100 | for (i = 0; i < size; i++) { |
b0d623f7 | 101 | test += (data)[i]; |
0a7de745 | 102 | } |
b0d623f7 | 103 | |
0a7de745 | 104 | if (res) { |
b0d623f7 | 105 | *res = test; |
0a7de745 A |
106 | } |
107 | ||
b0d623f7 A |
108 | return TRUE; |
109 | } | |
110 | ||
111 | /* check that no subpage of the superpage is readable */ | |
112 | boolean_t | |
0a7de745 A |
113 | check_nr(mach_vm_address_t addr, mach_vm_size_t size, int *res) |
114 | { | |
b0d623f7 A |
115 | int i; |
116 | boolean_t ret; | |
0a7de745 A |
117 | for (i = 0; i < size / PAGE_SIZE; i++) { |
118 | if ((ret = check_r(addr + i * PAGE_SIZE, PAGE_SIZE, res))) { | |
b0d623f7 A |
119 | sprintf(error, "page still readable"); |
120 | return FALSE; | |
121 | } | |
122 | } | |
123 | return TRUE; | |
124 | } | |
125 | ||
126 | boolean_t | |
0a7de745 A |
127 | check_w(mach_vm_address_t addr, mach_vm_size_t size) |
128 | { | |
b0d623f7 A |
129 | char *data = (char*)(uintptr_t)addr; |
130 | int i, sig; | |
131 | ||
132 | if ((sig = setjmp(resume)) != 0) { | |
133 | sprintf(error, "%s when writing", signame[sig]); | |
134 | return FALSE; | |
135 | } | |
136 | ||
0a7de745 | 137 | for (i = 0; i < size; i++) { |
b0d623f7 | 138 | (data)[i] = i & 0xFF; |
0a7de745 | 139 | } |
b0d623f7 A |
140 | |
141 | return TRUE; | |
142 | } | |
143 | ||
144 | boolean_t | |
0a7de745 A |
145 | check_nw(mach_vm_address_t addr, mach_vm_size_t size) |
146 | { | |
b0d623f7 A |
147 | int i; |
148 | boolean_t ret; | |
149 | ||
0a7de745 A |
150 | for (i = 0; i < size / PAGE_SIZE; i++) { |
151 | if ((ret = check_w(addr + i * PAGE_SIZE, PAGE_SIZE))) { | |
b0d623f7 A |
152 | sprintf(error, "page still writable"); |
153 | return FALSE; | |
154 | } | |
155 | } | |
156 | return TRUE; | |
157 | } | |
158 | ||
159 | boolean_t | |
0a7de745 A |
160 | check_rw(mach_vm_address_t addr, mach_vm_size_t size) |
161 | { | |
b0d623f7 A |
162 | int ret; |
163 | int res; | |
0a7de745 A |
164 | if (!(ret = check_w(addr, size))) { |
165 | return ret; | |
166 | } | |
167 | if (!(ret = check_r(addr, size, &res))) { | |
168 | return ret; | |
169 | } | |
170 | if ((size == SUPERPAGE_SIZE) && (res != 0xfff00000)) { | |
b0d623f7 A |
171 | sprintf(error, "checksum error"); |
172 | return FALSE; | |
173 | } | |
174 | ||
175 | return TRUE; | |
176 | } | |
177 | ||
178 | mach_vm_address_t global_addr = 0; | |
0a7de745 | 179 | mach_vm_size_t global_size = 0; |
b0d623f7 | 180 | |
6d2010ae A |
181 | /* |
182 | * If we allocate a 2 MB superpage read-write without specifying an address, | |
183 | * - the call should succeed | |
184 | * - not return 0 | |
185 | * - return a 2 MB aligned address | |
186 | * - the memory should be readable and writable | |
187 | */ | |
b0d623f7 | 188 | boolean_t |
0a7de745 A |
189 | test_allocate() |
190 | { | |
b0d623f7 A |
191 | int kr, ret; |
192 | ||
193 | global_addr = 0; | |
194 | global_size = SUPERPAGE_SIZE; | |
0a7de745 | 195 | |
b0d623f7 | 196 | kr = mach_vm_allocate(mach_task_self(), &global_addr, global_size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); |
0a7de745 A |
197 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
198 | return ret; | |
199 | } | |
200 | if (!(ret = check_addr0(global_addr, "mach_vm_allocate"))) { | |
201 | return ret; | |
202 | } | |
203 | if (!(ret = check_align(global_addr))) { | |
204 | return ret; | |
205 | } | |
206 | if (!(ret = check_rw(global_addr, global_size))) { | |
207 | return ret; | |
208 | } | |
b0d623f7 A |
209 | |
210 | return TRUE; | |
211 | } | |
212 | ||
6d2010ae A |
213 | /* |
214 | * If we deallocate a superpage, | |
215 | * - the call should succeed | |
216 | * - make the memory inaccessible | |
217 | */ | |
b0d623f7 | 218 | boolean_t |
0a7de745 A |
219 | test_deallocate() |
220 | { | |
221 | mach_vm_size_t size = SUPERPAGE_SIZE; | |
b0d623f7 A |
222 | int kr, ret; |
223 | ||
224 | if (!global_addr) { | |
225 | sprintf(error, "skipped deallocation"); | |
226 | return FALSE; | |
227 | } | |
228 | kr = mach_vm_deallocate(mach_task_self(), global_addr, global_size); | |
0a7de745 A |
229 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
230 | return ret; | |
231 | } | |
232 | if (!(ret = check_nr(global_addr, size, NULL))) { | |
233 | return ret; | |
234 | } | |
b0d623f7 A |
235 | return TRUE; |
236 | } | |
237 | ||
6d2010ae A |
238 | /* |
239 | * If we allocate a superpage of any size read-write without specifying an address | |
240 | * - the call should succeed | |
241 | * - not return 0 | |
242 | * - the memory should be readable and writable | |
243 | * If we deallocate it, | |
244 | * - the call should succeed | |
245 | * - make the memory inaccessible | |
246 | */ | |
247 | boolean_t | |
0a7de745 A |
248 | test_allocate_size_any() |
249 | { | |
6d2010ae A |
250 | int kr; |
251 | int ret; | |
252 | mach_vm_address_t addr = 0; | |
0a7de745 | 253 | mach_vm_size_t size = 2 * PAGE_SIZE; /* will be rounded up to some superpage size */ |
6d2010ae A |
254 | |
255 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_ANY); | |
0a7de745 A |
256 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
257 | return ret; | |
258 | } | |
259 | if (!(ret = check_addr0(addr, "mach_vm_allocate"))) { | |
260 | return ret; | |
261 | } | |
262 | if (!(ret = check_rw(addr, size))) { | |
263 | return ret; | |
264 | } | |
6d2010ae | 265 | kr = mach_vm_deallocate(mach_task_self(), addr, size); |
0a7de745 A |
266 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
267 | return ret; | |
268 | } | |
269 | if (!(ret = check_nr(addr, size, NULL))) { | |
270 | return ret; | |
271 | } | |
6d2010ae A |
272 | return TRUE; |
273 | } | |
b0d623f7 | 274 | |
6d2010ae A |
275 | /* |
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 | |
283 | */ | |
b0d623f7 | 284 | boolean_t |
0a7de745 A |
285 | test_allocatefixed() |
286 | { | |
b0d623f7 A |
287 | int kr; |
288 | int ret; | |
289 | mach_vm_address_t addr = FIXED_ADDRESS1; | |
0a7de745 | 290 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
291 | |
292 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
293 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
294 | return ret; | |
295 | } | |
296 | if (!(ret = check_addr(addr, FIXED_ADDRESS1, "mach_vm_allocate"))) { | |
297 | return ret; | |
298 | } | |
299 | if (!(ret = check_rw(addr, size))) { | |
300 | return ret; | |
301 | } | |
b0d623f7 | 302 | kr = mach_vm_deallocate(mach_task_self(), addr, size); |
0a7de745 A |
303 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
304 | return ret; | |
305 | } | |
306 | if (!(ret = check_nr(addr, size, NULL))) { | |
307 | return ret; | |
308 | } | |
b0d623f7 A |
309 | return TRUE; |
310 | } | |
311 | ||
6d2010ae A |
312 | /* |
313 | * If we allocate a 2 MB superpage read-write at an unaligned address, | |
314 | * - the call should fail | |
315 | */ | |
b0d623f7 | 316 | boolean_t |
0a7de745 A |
317 | test_allocateunalignedfixed() |
318 | { | |
b0d623f7 A |
319 | int kr; |
320 | int ret; | |
321 | mach_vm_address_t addr = FIXED_ADDRESS2; | |
0a7de745 A |
322 | mach_vm_size_t size = SUPERPAGE_SIZE; |
323 | ||
b0d623f7 | 324 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB); |
6d2010ae A |
325 | /* is supposed to fail */ |
326 | if ((ret = check_kr(kr, "mach_vm_allocate"))) { | |
327 | sprintf(error, "mach_vm_allocate() should have failed"); | |
328 | return FALSE; | |
329 | } | |
330 | return TRUE; | |
331 | } | |
332 | ||
333 | /* | |
334 | * If we allocate an amount of memory not divisible by 2 MB as a 2 MB superpage | |
335 | * - the call should fail | |
336 | */ | |
337 | boolean_t | |
0a7de745 A |
338 | test_allocateoddsize() |
339 | { | |
6d2010ae A |
340 | int kr; |
341 | int ret; | |
342 | mach_vm_address_t addr = FIXED_ADDRESS1; | |
0a7de745 | 343 | mach_vm_size_t size = PAGE_SIZE; /* != 2 MB */ |
6d2010ae A |
344 | |
345 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
346 | /* is supposed to fail */ | |
b0d623f7 A |
347 | if ((ret = check_kr(kr, "mach_vm_allocate"))) { |
348 | sprintf(error, "mach_vm_allocate() should have failed"); | |
349 | return FALSE; | |
350 | } | |
b0d623f7 A |
351 | return TRUE; |
352 | } | |
353 | ||
6d2010ae A |
354 | /* |
355 | * If we deallocate a sub-page of a superpage, | |
356 | * - the call should succeed | |
357 | * - make the complete memory inaccessible | |
358 | */ | |
b0d623f7 | 359 | boolean_t |
0a7de745 A |
360 | test_deallocatesubpage() |
361 | { | |
b0d623f7 A |
362 | int kr; |
363 | int ret; | |
364 | mach_vm_address_t addr = 0; | |
0a7de745 A |
365 | mach_vm_size_t size = SUPERPAGE_SIZE; |
366 | ||
b0d623f7 | 367 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); |
0a7de745 A |
368 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
369 | return ret; | |
370 | } | |
b0d623f7 | 371 | kr = mach_vm_deallocate(mach_task_self(), addr + PAGE_SIZE, size); |
0a7de745 A |
372 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
373 | return ret; | |
374 | } | |
375 | if (!(ret = check_nr(addr, size, NULL))) { | |
376 | return ret; | |
377 | } | |
b0d623f7 A |
378 | return TRUE; |
379 | } | |
380 | ||
6d2010ae A |
381 | /* |
382 | * If we try to allocate memory occupied by superpages as normal pages | |
383 | * - the call should fail | |
384 | */ | |
b0d623f7 | 385 | boolean_t |
0a7de745 A |
386 | test_reallocate() |
387 | { | |
b0d623f7 | 388 | mach_vm_address_t addr = 0, addr2; |
0a7de745 | 389 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
390 | int kr, ret; |
391 | int i; | |
392 | ||
393 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
394 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
395 | return ret; | |
396 | } | |
b0d623f7 A |
397 | |
398 | /* attempt to allocate every sub-page of superpage */ | |
0a7de745 A |
399 | for (i = 0; i < SUPERPAGE_SIZE / PAGE_SIZE; i++) { |
400 | addr2 = addr + i * PAGE_SIZE; | |
b0d623f7 A |
401 | size = 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); | |
406 | return FALSE; | |
407 | } | |
408 | } | |
409 | kr = mach_vm_deallocate(mach_task_self(), addr, size); | |
0a7de745 A |
410 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
411 | return ret; | |
412 | } | |
b0d623f7 A |
413 | return TRUE; |
414 | } | |
415 | ||
6d2010ae A |
416 | /* |
417 | * If we try to wire superpages | |
418 | * - the call should succeed | |
419 | * - the memory should remain readable and writable | |
420 | */ | |
b0d623f7 | 421 | boolean_t |
0a7de745 A |
422 | test_wire() |
423 | { | |
b0d623f7 A |
424 | int kr; |
425 | int ret; | |
426 | mach_vm_address_t addr = 0; | |
0a7de745 | 427 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
428 | |
429 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
430 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
431 | return ret; | |
432 | } | |
b0d623f7 A |
433 | |
434 | kr = mach_vm_wire(mach_host_self(), mach_task_self(), addr, size, VM_PROT_WRITE | VM_PROT_READ); | |
435 | ||
0a7de745 A |
436 | if (!geteuid()) { /* may fail as user */ |
437 | if (!(ret = check_kr(kr, "mach_vm_wire"))) { | |
438 | return ret; | |
439 | } | |
440 | } | |
b0d623f7 | 441 | |
0a7de745 A |
442 | if (!(ret = check_rw(addr, size))) { |
443 | return ret; | |
444 | } | |
b0d623f7 A |
445 | |
446 | kr = mach_vm_deallocate(mach_task_self(), addr, size); | |
0a7de745 A |
447 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
448 | return ret; | |
449 | } | |
b0d623f7 A |
450 | |
451 | return TRUE; | |
452 | } | |
453 | ||
6d2010ae A |
454 | /* |
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. | |
459 | */ | |
b0d623f7 | 460 | boolean_t |
0a7de745 A |
461 | test_unwire() |
462 | { | |
b0d623f7 A |
463 | int kr; |
464 | int ret; | |
465 | mach_vm_address_t addr = 0; | |
0a7de745 | 466 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
467 | |
468 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
469 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
470 | return ret; | |
471 | } | |
b0d623f7 A |
472 | |
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"); | |
476 | return FALSE; | |
477 | } | |
478 | ||
0a7de745 A |
479 | if (!(ret = check_rw(addr, size))) { |
480 | return ret; | |
481 | } | |
b0d623f7 A |
482 | |
483 | kr = mach_vm_deallocate(mach_task_self(), addr, size); | |
0a7de745 A |
484 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
485 | return ret; | |
486 | } | |
b0d623f7 A |
487 | |
488 | return TRUE; | |
489 | } | |
490 | ||
6d2010ae A |
491 | /* |
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 | |
496 | */ | |
b0d623f7 | 497 | boolean_t |
0a7de745 A |
498 | test_readonly() |
499 | { | |
b0d623f7 A |
500 | int kr; |
501 | int ret; | |
502 | mach_vm_address_t addr = 0; | |
0a7de745 | 503 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
504 | |
505 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
506 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
507 | return ret; | |
508 | } | |
b0d623f7 A |
509 | |
510 | mach_vm_protect(mach_task_self(), addr, size, 0, VM_PROT_READ); | |
0a7de745 A |
511 | if (!(ret = check_kr(kr, "mach_vm_protect"))) { |
512 | return ret; | |
513 | } | |
b0d623f7 | 514 | |
0a7de745 A |
515 | if (!(ret = check_r(addr, size, NULL))) { |
516 | return ret; | |
517 | } | |
518 | if (!(ret = check_nw(addr, size))) { | |
519 | return ret; | |
520 | } | |
b0d623f7 A |
521 | |
522 | kr = mach_vm_deallocate(mach_task_self(), addr, size); | |
0a7de745 A |
523 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
524 | return ret; | |
525 | } | |
b0d623f7 A |
526 | |
527 | return TRUE; | |
528 | } | |
529 | ||
6d2010ae A |
530 | /* |
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 | |
535 | */ | |
b0d623f7 | 536 | boolean_t |
0a7de745 A |
537 | test_readonlysubpage() |
538 | { | |
b0d623f7 A |
539 | int kr; |
540 | int ret; | |
541 | mach_vm_address_t addr = 0; | |
0a7de745 | 542 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
543 | |
544 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
545 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
546 | return ret; | |
547 | } | |
b0d623f7 | 548 | |
0a7de745 A |
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"))) { | |
551 | return ret; | |
552 | } | |
b0d623f7 | 553 | |
0a7de745 A |
554 | if (!(ret = check_r(addr, size, NULL))) { |
555 | return ret; | |
556 | } | |
557 | if (!(ret = check_nw(addr, size))) { | |
558 | return ret; | |
559 | } | |
b0d623f7 A |
560 | |
561 | kr = mach_vm_deallocate(mach_task_self(), addr, size); | |
0a7de745 A |
562 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
563 | return ret; | |
564 | } | |
b0d623f7 A |
565 | |
566 | return TRUE; | |
567 | } | |
568 | ||
6d2010ae A |
569 | /* |
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 | |
573 | */ | |
b0d623f7 | 574 | boolean_t |
0a7de745 A |
575 | test_fork() |
576 | { | |
b0d623f7 | 577 | mach_vm_address_t addr = 0; |
0a7de745 | 578 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
579 | int kr, ret; |
580 | pid_t pid; | |
0a7de745 | 581 | |
b0d623f7 | 582 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); |
0a7de745 A |
583 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
584 | return ret; | |
585 | } | |
b0d623f7 A |
586 | |
587 | fflush(stdout); | |
0a7de745 A |
588 | if ((pid = fork())) { /* parent */ |
589 | if (!(ret = check_rw(addr, size))) { | |
590 | return ret; | |
591 | } | |
b0d623f7 A |
592 | waitpid(pid, &ret, 0); |
593 | if (!ret) { | |
594 | sprintf(error, "child could access superpage"); | |
595 | return ret; | |
596 | } | |
6d2010ae | 597 | } else { /* child */ |
0a7de745 A |
598 | if (!(ret = check_nr(addr, size, NULL))) { |
599 | exit(ret); | |
600 | } | |
b0d623f7 A |
601 | exit(TRUE); |
602 | } | |
0a7de745 | 603 | |
b0d623f7 | 604 | kr = mach_vm_deallocate(mach_task_self(), addr, size); |
0a7de745 A |
605 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
606 | return ret; | |
607 | } | |
b0d623f7 A |
608 | return TRUE; |
609 | } | |
610 | ||
6d2010ae A |
611 | /* |
612 | * Doing file I/O with superpages | |
613 | * - should succeed | |
614 | * - should behave the same as with base pages (i.e. no bad data) | |
615 | */ | |
fe8ab488 | 616 | #define FILENAME "/System/Library/Kernels/kernel" |
b0d623f7 | 617 | boolean_t |
0a7de745 A |
618 | test_fileio() |
619 | { | |
b0d623f7 A |
620 | mach_vm_address_t addr1 = 0; |
621 | mach_vm_address_t addr2 = 0; | |
0a7de745 | 622 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 A |
623 | int kr, ret; |
624 | int fd; | |
625 | unsigned int bytes; | |
0a7de745 | 626 | |
b0d623f7 A |
627 | /* allocate one superpage */ |
628 | kr = mach_vm_allocate(mach_task_self(), &addr1, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); | |
0a7de745 A |
629 | if (!(ret = check_kr(kr, "mach_vm_allocate (1)"))) { |
630 | return ret; | |
631 | } | |
b0d623f7 A |
632 | |
633 | /* allocate base pages (superpage-sized) */ | |
634 | kr = mach_vm_allocate(mach_task_self(), &addr2, size, VM_FLAGS_ANYWHERE); | |
0a7de745 A |
635 | if (!(ret = check_kr(kr, "mach_vm_allocate (2)"))) { |
636 | return ret; | |
637 | } | |
b0d623f7 | 638 | |
0a7de745 | 639 | if ((fd = open(FILENAME, O_RDONLY)) < 0) { |
b0d623f7 A |
640 | sprintf(error, "couldn't open %s", FILENAME); |
641 | return FALSE; | |
642 | } | |
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)"); | |
647 | return FALSE; | |
648 | } | |
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)"); | |
653 | return FALSE; | |
654 | } | |
655 | close(fd); | |
0a7de745 | 656 | |
b0d623f7 A |
657 | /* compare */ |
658 | if (memcmp((void*)(uintptr_t)addr1, (void*)(uintptr_t)addr2, bytes)) { | |
659 | sprintf(error, "read data corrupt"); | |
660 | return FALSE; | |
661 | } | |
662 | ||
663 | kr = mach_vm_deallocate(mach_task_self(), addr1, size); | |
0a7de745 A |
664 | if (!(ret = check_kr(kr, "mach_vm_deallocate (1)"))) { |
665 | return ret; | |
666 | } | |
b0d623f7 | 667 | kr = mach_vm_deallocate(mach_task_self(), addr2, size); |
0a7de745 A |
668 | if (!(ret = check_kr(kr, "mach_vm_deallocate (2)"))) { |
669 | return ret; | |
670 | } | |
b0d623f7 A |
671 | return TRUE; |
672 | } | |
673 | ||
b0d623f7 | 674 | /* |
6d2010ae | 675 | * The mmap() interface should work just as well! |
b0d623f7 A |
676 | */ |
677 | boolean_t | |
0a7de745 A |
678 | test_mmap() |
679 | { | |
b0d623f7 | 680 | int kr, ret; |
6d2010ae A |
681 | uintptr_t addr = 0; |
682 | int size = SUPERPAGE_SIZE; | |
0a7de745 | 683 | |
6d2010ae A |
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) { | |
b0d623f7 A |
686 | sprintf(error, "mmap()"); |
687 | return FALSE; | |
688 | } | |
0a7de745 A |
689 | if (!(ret = check_addr0(addr, "mach_vm_allocate"))) { |
690 | return ret; | |
691 | } | |
692 | if (!(ret = check_align(addr))) { | |
693 | return ret; | |
694 | } | |
695 | if (!(ret = check_r(addr, SUPERPAGE_SIZE, NULL))) { | |
696 | return ret; | |
697 | } | |
698 | if (!(ret = check_nw(addr, SUPERPAGE_SIZE))) { | |
699 | return ret; | |
700 | } | |
6d2010ae | 701 | kr = munmap((void*)addr, size); |
0a7de745 A |
702 | if (!(ret = check_kr(kr, "munmap"))) { |
703 | return ret; | |
704 | } | |
705 | if (!(ret = check_nr(addr, size, NULL))) { | |
706 | return ret; | |
707 | } | |
b0d623f7 A |
708 | |
709 | return TRUE; | |
710 | } | |
b0d623f7 | 711 | |
6d2010ae A |
712 | /* |
713 | * Tests one allocation/deallocaton cycle; used in a loop this tests for leaks | |
714 | */ | |
b0d623f7 | 715 | boolean_t |
0a7de745 A |
716 | test_alloc_dealloc() |
717 | { | |
b0d623f7 | 718 | mach_vm_address_t addr = 0; |
0a7de745 | 719 | mach_vm_size_t size = SUPERPAGE_SIZE; |
b0d623f7 | 720 | int kr, ret; |
0a7de745 | 721 | |
b0d623f7 | 722 | kr = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE | VM_FLAGS_SUPERPAGE_SIZE_2MB); |
0a7de745 A |
723 | if (!(ret = check_kr(kr, "mach_vm_allocate"))) { |
724 | return ret; | |
725 | } | |
726 | if (!(ret = check_addr0(addr, "mach_vm_allocate"))) { | |
727 | return ret; | |
728 | } | |
729 | if (!(ret = check_align(addr))) { | |
730 | return ret; | |
731 | } | |
732 | if (!(ret = check_rw(addr, size))) { | |
733 | return ret; | |
734 | } | |
b0d623f7 | 735 | kr = mach_vm_deallocate(mach_task_self(), addr, size); |
0a7de745 A |
736 | if (!(ret = check_kr(kr, "mach_vm_deallocate"))) { |
737 | return ret; | |
738 | } | |
b0d623f7 A |
739 | return TRUE; |
740 | } | |
741 | ||
742 | test_t test[] = { | |
743 | { "allocate one page anywhere", test_allocate }, | |
744 | { "deallocate a page", test_deallocate }, | |
6d2010ae | 745 | { "allocate a SIZE_ANY page anywhere", test_allocate_size_any }, |
b0d623f7 A |
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 }, | |
b0d623f7 | 755 | { "mmap()", test_mmap }, |
b0d623f7 A |
756 | { "fork", test_fork }, |
757 | }; | |
758 | #define TESTS ((int)(sizeof(test)/sizeof(*test))) | |
759 | ||
760 | boolean_t | |
0a7de745 A |
761 | testit(int i) |
762 | { | |
b0d623f7 A |
763 | boolean_t ret; |
764 | ||
765 | error[0] = 0; | |
0a7de745 | 766 | printf("Test #%d \"%s\"...", i + 1, test[i].description); |
b0d623f7 | 767 | ret = test[i].fn(); |
0a7de745 A |
768 | if (ret) { |
769 | printf("OK\n"); | |
770 | } else { | |
771 | printf("FAILED!"); | |
772 | if (error[0]) { | |
773 | printf(" (%s)\n", error); | |
774 | } else { | |
775 | printf("\n"); | |
776 | } | |
b0d623f7 A |
777 | } |
778 | } | |
779 | ||
0a7de745 A |
780 | int |
781 | main(int argc, char **argv) | |
782 | { | |
b0d623f7 A |
783 | int i; |
784 | uint64_t time1, time2; | |
785 | ||
786 | int mode = 0; | |
0a7de745 | 787 | if (argc > 1) { |
b0d623f7 A |
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"); | |
793 | exit(0); | |
794 | } | |
0a7de745 | 795 | mode = atoi(argv[1]); |
b0d623f7 | 796 | } |
0a7de745 | 797 | |
b0d623f7 A |
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 ); | |
b0d623f7 | 805 | |
0a7de745 A |
806 | if (mode > 0) { /* one specific test */ |
807 | testit(mode - 1); | |
808 | } | |
809 | ||
810 | if (mode == 0) { /* test all cases */ | |
b0d623f7 | 811 | printf("Running %d tests:\n", TESTS); |
0a7de745 | 812 | for (i = 0; i < TESTS; i++) { |
b0d623f7 A |
813 | testit(i); |
814 | } | |
815 | } | |
0a7de745 A |
816 | if (mode == -1) { /* alloc/dealloc */ |
817 | boolean_t ret; | |
b0d623f7 A |
818 | do { |
819 | ret = test_alloc_dealloc(TRUE); | |
820 | printf("."); | |
821 | fflush(stdout); | |
822 | } while (ret); | |
0a7de745 A |
823 | if (error[0]) { |
824 | printf(" (%s)\n", error); | |
825 | } | |
b0d623f7 A |
826 | } |
827 | return 0; | |
828 | } |