1 /*
2 * Copyright (c) 2019-2021 Apple Inc. All rights reserved.
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 #if defined(__arm64__)
50 extern kern_return_t arm64_lock_test(void);
51 extern kern_return_t arm_cpu_capabilities_legacy_test(void);
52 extern kern_return_t pmap_test(void);
53 #endif /* defined(__arm64__) */
54 kern_return_t ipi_test(void);
55 #if defined(KERNEL_INTEGRITY_CTRR)
56 extern kern_return_t ctrr_test(void);
57 #endif
58 #if __ARM_PAN_AVAILABLE__
59 extern kern_return_t arm64_late_pan_test(void);
60 #endif
61 extern kern_return_t copyio_test(void);
62 extern kern_return_t parse_boot_arg_test(void);
63
64 struct xnupost_test bsd_post_tests[] = {
65 #ifdef __arm64__
66 XNUPOST_TEST_CONFIG_BASIC(arm64_lock_test),
67 #endif
68 #if defined(__arm64__)
69 XNUPOST_TEST_CONFIG_BASIC(arm_cpu_capabilities_legacy_test),
70 XNUPOST_TEST_CONFIG_BASIC(pmap_test),
71 #endif /* defined(__arm64__) */
72 #if CONFIG_SPTM
73 /**
74 * SPTM TODO: The CTRR test is currently not functional in SPTM systems.
75 * This will be addressed in a future change.
76 */
77 #else
78 #if defined(KERNEL_INTEGRITY_CTRR)
79 XNUPOST_TEST_CONFIG_BASIC(ctrr_test),
80 #endif
81 #endif
82 #if __ARM_PAN_AVAILABLE__
83 XNUPOST_TEST_CONFIG_BASIC(arm64_late_pan_test),
84 #endif
85 XNUPOST_TEST_CONFIG_BASIC(ipi_test),
86 XNUPOST_TEST_CONFIG_BASIC(copyio_test),
87 XNUPOST_TEST_CONFIG_BASIC(parse_boot_arg_test),
88 };
89
90 uint32_t bsd_post_tests_count = sizeof(bsd_post_tests) / sizeof(xnupost_test_data_t);
91
92 extern uint64_t last_loaded_timestamp; /* updated by OSKext::load() */
93 extern uint64_t kernel_post_args;
94 int
bsd_list_tests()95 bsd_list_tests()
96 {
97 if (kernel_post_args == 0) {
98 return 0;
99 }
100
101 uint64_t prev_load_time = last_loaded_timestamp;
102 int no_load_counter = 5;
103 int absolute_break_counter = 15;
104 int delay_duration_usecs = 300000; /* 0.3 second for kext loading to stabilize */
105
106 while (no_load_counter > 0) {
107 printf("bsd_list_tests:INFO waiting for %d usecs\n", delay_duration_usecs);
108 printf("bsd_list_tests: prev: %llu current: %llu\n", prev_load_time, last_loaded_timestamp);
109
110 delay(delay_duration_usecs);
111 absolute_break_counter -= 1;
112
113 if (absolute_break_counter <= 0) {
114 printf("bsd_list_tests: WARNING: Waiting beyond normal time for stabilizing kext loading\n");
115 break;
116 }
117
118 if (prev_load_time == last_loaded_timestamp) {
119 no_load_counter -= 1;
120 printf("bsd_list_tests: INFO: no new kexts loaded. remaining checks: %d\n", no_load_counter);
121 }
122
123 prev_load_time = last_loaded_timestamp;
124 }
125
126 return xnupost_list_tests(bsd_post_tests, bsd_post_tests_count);
127 }
128
129 int
bsd_do_post(void)130 bsd_do_post(void)
131 {
132 return xnupost_run_tests(bsd_post_tests, bsd_post_tests_count);
133 }
134
135 /* kcdata type definition */
136 #define XNUPOST_TNAME_MAXLEN 132
137
138 struct kcdata_subtype_descriptor kc_xnupost_test_def[] = {
139 {.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"},
140 {.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"},
141 {.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"},
142 {.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"},
143 {.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"},
144 {.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"},
145 {.kcs_flags = KCS_SUBTYPE_FLAGS_ARRAY,
146 .kcs_elem_type = KC_ST_CHAR,
147 .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t) + sizeof(uint64_t)),
148 .kcs_elem_size = KCS_SUBTYPE_PACK_SIZE(XNUPOST_TNAME_MAXLEN * sizeof(char), sizeof(char)),
149 .kcs_name = "test_name"}
150 };
151
152 const uint32_t kc_xnupost_test_def_count = sizeof(kc_xnupost_test_def) / sizeof(struct kcdata_subtype_descriptor);
153
154 kern_return_t xnupost_copyout_test(xnupost_test_t t, mach_vm_address_t outaddr);
155
156 int
xnupost_copyout_test(xnupost_test_t t,mach_vm_address_t outaddr)157 xnupost_copyout_test(xnupost_test_t t, mach_vm_address_t outaddr)
158 {
159 /* code to copyout test config */
160 int kret = 0;
161 size_t namelen = 0;
162
163 kret = copyout(&t->xt_config, (user_addr_t)outaddr, sizeof(uint16_t));
164 if (kret) {
165 return kret;
166 }
167 outaddr += sizeof(uint16_t);
168
169 kret = copyout(&t->xt_test_num, (user_addr_t)outaddr, sizeof(uint16_t));
170 if (kret) {
171 return kret;
172 }
173 outaddr += sizeof(uint16_t);
174
175 kret = copyout(&t->xt_retval, (user_addr_t)outaddr, sizeof(uint32_t));
176 if (kret) {
177 return kret;
178 }
179 outaddr += sizeof(uint32_t);
180
181 kret = copyout(&t->xt_expected_retval, (user_addr_t)outaddr, sizeof(uint32_t));
182 if (kret) {
183 return kret;
184 }
185 outaddr += sizeof(uint32_t);
186
187 kret = copyout(&t->xt_begin_time, (user_addr_t)outaddr, sizeof(uint64_t));
188 if (kret) {
189 return kret;
190 }
191 outaddr += sizeof(uint64_t);
192
193 kret = copyout(&t->xt_end_time, (user_addr_t)outaddr, sizeof(uint64_t));
194 if (kret) {
195 return kret;
196 }
197 outaddr += sizeof(uint64_t);
198
199 namelen = strnlen(t->xt_name, XNUPOST_TNAME_MAXLEN - 1) + 1;
200 kret = copyout(t->xt_name, (user_addr_t)outaddr, namelen);
201 if (kret) {
202 return kret;
203 }
204 outaddr += namelen;
205
206 return 0;
207 }
208
209 uint32_t
xnupost_get_estimated_testdata_size(void)210 xnupost_get_estimated_testdata_size(void)
211 {
212 uint32_t total_tests = bsd_post_tests_count + kernel_post_tests_count;
213 uint32_t elem_size = kc_xnupost_test_def[kc_xnupost_test_def_count - 1].kcs_elem_offset +
214 kcs_get_elem_size(&kc_xnupost_test_def[kc_xnupost_test_def_count - 1]);
215 uint32_t retval = 1024; /* account for type definition and mach timebase */
216 retval += 1024; /* kernel version and boot-args string data */
217 retval += (total_tests * elem_size);
218
219 return retval;
220 }
221
222 int
xnupost_export_testdata(void * outp,size_t size_in,uint32_t * lenp)223 xnupost_export_testdata(void * outp, size_t size_in, uint32_t * lenp)
224 {
225 struct kcdata_descriptor kcd;
226 mach_vm_address_t user_addr = 0;
227 mach_vm_address_t tmp_entry_addr = 0;
228 kern_return_t kret = 0;
229 uint32_t i = 0;
230 char kctype_name[32] = "xnupost_test_config";
231 mach_timebase_info_data_t timebase = {0, 0};
232 uint32_t length_to_copy = 0;
233 unsigned int size = (unsigned int)size_in;
234
235 if (size_in > UINT_MAX) {
236 return ENOSPC;
237 }
238
239 #define RET_IF_OP_FAIL \
240 do { \
241 if (kret != KERN_SUCCESS) { \
242 return (kret == KERN_NO_ACCESS) ? EACCES : ((kret == KERN_RESOURCE_SHORTAGE) ? ENOMEM : EINVAL); \
243 } \
244 } while (0)
245
246 kret = kcdata_memory_static_init(&kcd, (mach_vm_address_t)outp, KCDATA_BUFFER_BEGIN_XNUPOST_CONFIG, size, KCFLAG_USE_COPYOUT);
247 RET_IF_OP_FAIL;
248
249 /* add mach timebase info */
250 clock_timebase_info(&timebase);
251 kret = kcdata_get_memory_addr(&kcd, KCDATA_TYPE_TIMEBASE, sizeof(timebase), &user_addr);
252 RET_IF_OP_FAIL;
253 kret = copyout(&timebase, (user_addr_t)user_addr, sizeof(timebase));
254 RET_IF_OP_FAIL;
255
256 /* save boot-args and osversion string */
257 length_to_copy = MIN((uint32_t)(strlen(version) + 1), OSVERSIZE);
258 kret = kcdata_get_memory_addr(&kcd, STACKSHOT_KCTYPE_OSVERSION, length_to_copy, &user_addr);
259 RET_IF_OP_FAIL;
260 kret = copyout(&version[0], (user_addr_t)user_addr, length_to_copy);
261 RET_IF_OP_FAIL;
262
263 length_to_copy = MIN((uint32_t)(strlen(PE_boot_args()) + 1), BOOT_LINE_LENGTH);
264 kret = kcdata_get_memory_addr(&kcd, STACKSHOT_KCTYPE_BOOTARGS, length_to_copy, &user_addr);
265 RET_IF_OP_FAIL;
266 kret = copyout(PE_boot_args(), (user_addr_t)user_addr, length_to_copy);
267 RET_IF_OP_FAIL;
268
269 /* add type definition to buffer */
270 kret = kcdata_add_type_definition(&kcd, XNUPOST_KCTYPE_TESTCONFIG, kctype_name, &kc_xnupost_test_def[0],
271 kc_xnupost_test_def_count);
272 RET_IF_OP_FAIL;
273
274 /* add the tests to buffer as array */
275 uint32_t total_tests = bsd_post_tests_count + kernel_post_tests_count;
276 uint32_t elem_size = kc_xnupost_test_def[kc_xnupost_test_def_count - 1].kcs_elem_offset +
277 kcs_get_elem_size(&kc_xnupost_test_def[kc_xnupost_test_def_count - 1]);
278
279 kret = kcdata_get_memory_addr_for_array(&kcd, XNUPOST_KCTYPE_TESTCONFIG, elem_size, total_tests, &user_addr);
280 RET_IF_OP_FAIL;
281
282 for (i = 0; i < bsd_post_tests_count; i++) {
283 tmp_entry_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size));
284 kret = xnupost_copyout_test(&bsd_post_tests[i], tmp_entry_addr);
285 RET_IF_OP_FAIL;
286 }
287 user_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size));
288
289 for (i = 0; i < kernel_post_tests_count; i++) {
290 tmp_entry_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size));
291 kret = xnupost_copyout_test(&kernel_post_tests[i], tmp_entry_addr);
292 RET_IF_OP_FAIL;
293 }
294
295 if (kret == KERN_SUCCESS && lenp != NULL) {
296 *lenp = (uint32_t)kcdata_memory_get_used_bytes(&kcd);
297 }
298 RET_IF_OP_FAIL;
299
300 #undef RET_IF_OP_FAIL
301 return kret;
302 }
303
304 int
xnupost_reset_all_tests(void)305 xnupost_reset_all_tests(void)
306 {
307 xnupost_reset_tests(&bsd_post_tests[0], bsd_post_tests_count);
308 xnupost_reset_tests(&kernel_post_tests[0], kernel_post_tests_count);
309 return 0;
310 }
311