]>
Commit | Line | Data |
---|---|---|
d9a64523 | 1 | /* |
cb323159 | 2 | * Copyright (c) 2019 Apple Inc. All rights reserved. |
d9a64523 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <tests/ktest.h> | |
30 | #include <tests/xnupost.h> | |
31 | #include <kern/assert.h> | |
32 | #include <sys/param.h> | |
33 | #include <sys/kernel.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/kdebug.h> | |
36 | #include <libkern/libkern.h> | |
37 | #include <kern/kalloc.h> | |
38 | #include <sys/cdefs.h> | |
39 | #include <libkern/version.h> | |
40 | #include <kern/clock.h> | |
41 | #include <kern/kern_cdata.h> | |
42 | #include <pexpert/pexpert.h> | |
43 | ||
44 | ||
45 | #if !(DEVELOPMENT || DEBUG) | |
46 | #error "Testing is not enabled on RELEASE configurations" | |
47 | #endif | |
48 | ||
49 | #ifdef __arm64__ | |
50 | extern kern_return_t arm64_lock_test(void); | |
51 | #endif | |
f427ee49 A |
52 | #if defined(__arm__) || defined(__arm64__) |
53 | extern kern_return_t pmap_test(void); | |
54 | #endif /* defined(__arm__) || defined(__arm64__) */ | |
d9a64523 A |
55 | kern_return_t kalloc_test(void); |
56 | kern_return_t ipi_test(void); | |
c6bf4f31 A |
57 | #if defined(KERNEL_INTEGRITY_CTRR) |
58 | extern kern_return_t ctrr_test(void); | |
59 | #endif | |
cb323159 A |
60 | #if __ARM_PAN_AVAILABLE__ |
61 | extern kern_return_t arm64_late_pan_test(void); | |
62 | #endif | |
63 | #if HAS_TWO_STAGE_SPR_LOCK | |
64 | extern kern_return_t arm64_spr_lock_test(void); | |
65 | #endif | |
66 | extern kern_return_t copyio_test(void); | |
d9a64523 A |
67 | |
68 | struct xnupost_test bsd_post_tests[] = { | |
69 | #ifdef __arm64__ | |
70 | XNUPOST_TEST_CONFIG_BASIC(arm64_lock_test), | |
cb323159 | 71 | #endif |
c3c9b80d | 72 | #if !KASAN // <rdar://71151361> |
f427ee49 A |
73 | #if defined(__arm__) || defined(__arm64__) |
74 | XNUPOST_TEST_CONFIG_BASIC(pmap_test), | |
75 | #endif /* defined(__arm__) || defined(__arm64__) */ | |
c6bf4f31 A |
76 | #if defined(KERNEL_INTEGRITY_CTRR) |
77 | XNUPOST_TEST_CONFIG_BASIC(ctrr_test), | |
78 | #endif | |
cb323159 A |
79 | #if __ARM_PAN_AVAILABLE__ |
80 | XNUPOST_TEST_CONFIG_BASIC(arm64_late_pan_test), | |
d9a64523 | 81 | #endif |
c3c9b80d | 82 | #endif /* !KASAN */ |
d9a64523 | 83 | XNUPOST_TEST_CONFIG_BASIC(kalloc_test), |
cb323159 A |
84 | XNUPOST_TEST_CONFIG_BASIC(ipi_test), |
85 | #if HAS_TWO_STAGE_SPR_LOCK | |
86 | XNUPOST_TEST_CONFIG_BASIC(arm64_spr_lock_test), | |
87 | #endif | |
c3c9b80d | 88 | #if !KASAN |
cb323159 | 89 | XNUPOST_TEST_CONFIG_BASIC(copyio_test), |
c3c9b80d | 90 | #endif /* KASAN */ |
d9a64523 A |
91 | }; |
92 | ||
93 | uint32_t bsd_post_tests_count = sizeof(bsd_post_tests) / sizeof(xnupost_test_data_t); | |
94 | ||
95 | extern uint64_t last_loaded_timestamp; /* updated by OSKext::load() */ | |
96 | extern uint64_t kernel_post_args; | |
97 | int | |
98 | bsd_list_tests() | |
99 | { | |
100 | if (kernel_post_args == 0) { | |
101 | return 0; | |
102 | } | |
103 | ||
104 | uint64_t prev_load_time = last_loaded_timestamp; | |
105 | int no_load_counter = 5; | |
106 | int absolute_break_counter = 15; | |
107 | int delay_duration_usecs = 300000; /* 0.3 second for kext loading to stabilize */ | |
108 | ||
109 | while (no_load_counter > 0) { | |
110 | printf("bsd_list_tests:INFO waiting for %d usecs\n", delay_duration_usecs); | |
111 | printf("bsd_list_tests: prev: %llu current: %llu\n", prev_load_time, last_loaded_timestamp); | |
112 | ||
113 | delay(delay_duration_usecs); | |
114 | absolute_break_counter -= 1; | |
115 | ||
116 | if (absolute_break_counter <= 0) { | |
117 | printf("bsd_list_tests: WARNING: Waiting beyond normal time for stabilizing kext loading\n"); | |
118 | break; | |
119 | } | |
120 | ||
121 | if (prev_load_time == last_loaded_timestamp) { | |
122 | no_load_counter -= 1; | |
123 | printf("bsd_list_tests: INFO: no new kexts loaded. remaining checks: %d\n", no_load_counter); | |
124 | } | |
125 | ||
126 | prev_load_time = last_loaded_timestamp; | |
127 | } | |
128 | ||
129 | return xnupost_list_tests(bsd_post_tests, bsd_post_tests_count); | |
130 | } | |
131 | ||
132 | int | |
133 | bsd_do_post() | |
134 | { | |
135 | return xnupost_run_tests(bsd_post_tests, bsd_post_tests_count); | |
136 | } | |
137 | ||
138 | kern_return_t | |
139 | kalloc_test() | |
140 | { | |
141 | uint64_t * data_ptr; | |
142 | size_t alloc_size; | |
143 | ||
144 | T_LOG("Running kalloc test.\n"); | |
145 | ||
146 | alloc_size = sizeof(uint64_t); | |
147 | data_ptr = kalloc(alloc_size); | |
148 | T_ASSERT_NOTNULL(data_ptr, "kalloc sizeof(uint64_t) return not null"); | |
149 | kfree(data_ptr, alloc_size); | |
150 | ||
151 | alloc_size = 3544; | |
152 | data_ptr = kalloc(alloc_size); | |
153 | T_ASSERT_NOTNULL(data_ptr, "kalloc 3544 return not null"); | |
154 | kfree(data_ptr, alloc_size); | |
155 | ||
156 | return KERN_SUCCESS; | |
157 | } | |
158 | ||
159 | /* kcdata type definition */ | |
160 | #define XNUPOST_TNAME_MAXLEN 132 | |
161 | ||
162 | struct kcdata_subtype_descriptor kc_xnupost_test_def[] = { | |
cb323159 A |
163 | {.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT16, .kcs_elem_offset = 0, .kcs_elem_size = sizeof(uint16_t), .kcs_name = "config"}, |
164 | {.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT16, .kcs_elem_offset = 1 * sizeof(uint16_t), .kcs_elem_size = sizeof(uint16_t), .kcs_name = "test_num"}, | |
165 | {.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_INT32, .kcs_elem_offset = 2 * sizeof(uint16_t), .kcs_elem_size = sizeof(int32_t), .kcs_name = "retval"}, | |
166 | {.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_INT32, .kcs_elem_offset = 2 * sizeof(uint16_t) + sizeof(int32_t), .kcs_elem_size = sizeof(int32_t), .kcs_name = "expected_retval"}, | |
167 | {.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT64, .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t)), .kcs_elem_size = sizeof(uint64_t), .kcs_name = "begin_time"}, | |
168 | {.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT64, .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t)) + sizeof(uint64_t), .kcs_elem_size = sizeof(uint64_t), .kcs_name = "end_time"}, | |
169 | {.kcs_flags = KCS_SUBTYPE_FLAGS_ARRAY, | |
170 | .kcs_elem_type = KC_ST_CHAR, | |
171 | .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t) + sizeof(uint64_t)), | |
172 | .kcs_elem_size = KCS_SUBTYPE_PACK_SIZE(XNUPOST_TNAME_MAXLEN * sizeof(char), sizeof(char)), | |
173 | .kcs_name = "test_name"} | |
0a7de745 | 174 | }; |
d9a64523 A |
175 | |
176 | const uint32_t kc_xnupost_test_def_count = sizeof(kc_xnupost_test_def) / sizeof(struct kcdata_subtype_descriptor); | |
177 | ||
178 | kern_return_t xnupost_copyout_test(xnupost_test_t t, mach_vm_address_t outaddr); | |
179 | ||
180 | int | |
181 | xnupost_copyout_test(xnupost_test_t t, mach_vm_address_t outaddr) | |
182 | { | |
183 | /* code to copyout test config */ | |
184 | int kret = 0; | |
f427ee49 | 185 | size_t namelen = 0; |
d9a64523 | 186 | |
f427ee49 | 187 | kret = copyout(&t->xt_config, (user_addr_t)outaddr, sizeof(uint16_t)); |
0a7de745 | 188 | if (kret) { |
d9a64523 | 189 | return kret; |
0a7de745 | 190 | } |
d9a64523 A |
191 | outaddr += sizeof(uint16_t); |
192 | ||
f427ee49 | 193 | kret = copyout(&t->xt_test_num, (user_addr_t)outaddr, sizeof(uint16_t)); |
0a7de745 | 194 | if (kret) { |
d9a64523 | 195 | return kret; |
0a7de745 | 196 | } |
d9a64523 A |
197 | outaddr += sizeof(uint16_t); |
198 | ||
f427ee49 | 199 | kret = copyout(&t->xt_retval, (user_addr_t)outaddr, sizeof(uint32_t)); |
0a7de745 | 200 | if (kret) { |
d9a64523 | 201 | return kret; |
0a7de745 | 202 | } |
d9a64523 A |
203 | outaddr += sizeof(uint32_t); |
204 | ||
f427ee49 | 205 | kret = copyout(&t->xt_expected_retval, (user_addr_t)outaddr, sizeof(uint32_t)); |
0a7de745 | 206 | if (kret) { |
d9a64523 | 207 | return kret; |
0a7de745 | 208 | } |
d9a64523 A |
209 | outaddr += sizeof(uint32_t); |
210 | ||
f427ee49 | 211 | kret = copyout(&t->xt_begin_time, (user_addr_t)outaddr, sizeof(uint64_t)); |
0a7de745 | 212 | if (kret) { |
d9a64523 | 213 | return kret; |
0a7de745 | 214 | } |
d9a64523 A |
215 | outaddr += sizeof(uint64_t); |
216 | ||
f427ee49 | 217 | kret = copyout(&t->xt_end_time, (user_addr_t)outaddr, sizeof(uint64_t)); |
0a7de745 | 218 | if (kret) { |
d9a64523 | 219 | return kret; |
0a7de745 | 220 | } |
d9a64523 A |
221 | outaddr += sizeof(uint64_t); |
222 | ||
223 | namelen = strnlen(t->xt_name, XNUPOST_TNAME_MAXLEN); | |
f427ee49 | 224 | kret = copyout(t->xt_name, (user_addr_t)outaddr, namelen); |
0a7de745 | 225 | if (kret) { |
d9a64523 | 226 | return kret; |
0a7de745 | 227 | } |
d9a64523 A |
228 | outaddr += namelen; |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | uint32_t | |
234 | xnupost_get_estimated_testdata_size(void) | |
235 | { | |
236 | uint32_t total_tests = bsd_post_tests_count + kernel_post_tests_count; | |
237 | uint32_t elem_size = kc_xnupost_test_def[kc_xnupost_test_def_count - 1].kcs_elem_offset + | |
0a7de745 | 238 | kcs_get_elem_size(&kc_xnupost_test_def[kc_xnupost_test_def_count - 1]); |
d9a64523 A |
239 | uint32_t retval = 1024; /* account for type definition and mach timebase */ |
240 | retval += 1024; /* kernel version and boot-args string data */ | |
241 | retval += (total_tests * elem_size); | |
242 | ||
243 | return retval; | |
244 | } | |
245 | ||
246 | int | |
f427ee49 | 247 | xnupost_export_testdata(void * outp, size_t size_in, uint32_t * lenp) |
d9a64523 A |
248 | { |
249 | struct kcdata_descriptor kcd; | |
250 | mach_vm_address_t user_addr = 0; | |
251 | mach_vm_address_t tmp_entry_addr = 0; | |
252 | kern_return_t kret = 0; | |
253 | uint32_t i = 0; | |
254 | char kctype_name[32] = "xnupost_test_config"; | |
255 | mach_timebase_info_data_t timebase = {0, 0}; | |
256 | uint32_t length_to_copy = 0; | |
f427ee49 A |
257 | unsigned int size = (unsigned int)size_in; |
258 | ||
259 | if (size_in > UINT_MAX) { | |
260 | return ENOSPC; | |
261 | } | |
d9a64523 A |
262 | |
263 | #define RET_IF_OP_FAIL \ | |
264 | do { \ | |
0a7de745 A |
265 | if (kret != KERN_SUCCESS) { \ |
266 | return (kret == KERN_NO_ACCESS) ? EACCES : ((kret == KERN_RESOURCE_SHORTAGE) ? ENOMEM : EINVAL); \ | |
267 | } \ | |
d9a64523 A |
268 | } while (0) |
269 | ||
270 | kret = kcdata_memory_static_init(&kcd, (mach_vm_address_t)outp, KCDATA_BUFFER_BEGIN_XNUPOST_CONFIG, size, KCFLAG_USE_COPYOUT); | |
271 | RET_IF_OP_FAIL; | |
272 | ||
273 | /* add mach timebase info */ | |
274 | clock_timebase_info(&timebase); | |
275 | kret = kcdata_get_memory_addr(&kcd, KCDATA_TYPE_TIMEBASE, sizeof(timebase), &user_addr); | |
276 | RET_IF_OP_FAIL; | |
f427ee49 | 277 | kret = copyout(&timebase, (user_addr_t)user_addr, sizeof(timebase)); |
d9a64523 A |
278 | RET_IF_OP_FAIL; |
279 | ||
280 | /* save boot-args and osversion string */ | |
281 | length_to_copy = MIN((uint32_t)(strlen(version) + 1), OSVERSIZE); | |
282 | kret = kcdata_get_memory_addr(&kcd, STACKSHOT_KCTYPE_OSVERSION, length_to_copy, &user_addr); | |
283 | RET_IF_OP_FAIL; | |
f427ee49 | 284 | kret = copyout(&version[0], (user_addr_t)user_addr, length_to_copy); |
d9a64523 A |
285 | RET_IF_OP_FAIL; |
286 | ||
4ba76501 | 287 | length_to_copy = MIN((uint32_t)(strlen(PE_boot_args()) + 1), BOOT_LINE_LENGTH); |
d9a64523 A |
288 | kret = kcdata_get_memory_addr(&kcd, STACKSHOT_KCTYPE_BOOTARGS, length_to_copy, &user_addr); |
289 | RET_IF_OP_FAIL; | |
f427ee49 | 290 | kret = copyout(PE_boot_args(), (user_addr_t)user_addr, length_to_copy); |
d9a64523 A |
291 | RET_IF_OP_FAIL; |
292 | ||
293 | /* add type definition to buffer */ | |
294 | kret = kcdata_add_type_definition(&kcd, XNUPOST_KCTYPE_TESTCONFIG, kctype_name, &kc_xnupost_test_def[0], | |
0a7de745 | 295 | kc_xnupost_test_def_count); |
d9a64523 A |
296 | RET_IF_OP_FAIL; |
297 | ||
298 | /* add the tests to buffer as array */ | |
299 | uint32_t total_tests = bsd_post_tests_count + kernel_post_tests_count; | |
300 | uint32_t elem_size = kc_xnupost_test_def[kc_xnupost_test_def_count - 1].kcs_elem_offset + | |
0a7de745 | 301 | kcs_get_elem_size(&kc_xnupost_test_def[kc_xnupost_test_def_count - 1]); |
d9a64523 A |
302 | |
303 | kret = kcdata_get_memory_addr_for_array(&kcd, XNUPOST_KCTYPE_TESTCONFIG, elem_size, total_tests, &user_addr); | |
304 | RET_IF_OP_FAIL; | |
305 | ||
306 | for (i = 0; i < bsd_post_tests_count; i++) { | |
307 | tmp_entry_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size)); | |
308 | kret = xnupost_copyout_test(&bsd_post_tests[i], tmp_entry_addr); | |
309 | RET_IF_OP_FAIL; | |
310 | } | |
311 | user_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size)); | |
312 | ||
313 | for (i = 0; i < kernel_post_tests_count; i++) { | |
314 | tmp_entry_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size)); | |
315 | kret = xnupost_copyout_test(&kernel_post_tests[i], tmp_entry_addr); | |
316 | RET_IF_OP_FAIL; | |
317 | } | |
318 | ||
0a7de745 | 319 | if (kret == KERN_SUCCESS && lenp != NULL) { |
d9a64523 | 320 | *lenp = (uint32_t)kcdata_memory_get_used_bytes(&kcd); |
0a7de745 | 321 | } |
d9a64523 A |
322 | RET_IF_OP_FAIL; |
323 | ||
324 | #undef RET_IF_OP_FAIL | |
325 | return kret; | |
326 | } | |
327 | ||
328 | int | |
329 | xnupost_reset_all_tests(void) | |
330 | { | |
331 | xnupost_reset_tests(&bsd_post_tests[0], bsd_post_tests_count); | |
332 | xnupost_reset_tests(&kernel_post_tests[0], kernel_post_tests_count); | |
333 | return 0; | |
334 | } |