1 | /* |
2 | * Copyright (c) 2023 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 | #if CONFIG_EXCLAVES |
30 | |
31 | #include <mach/kern_return.h> |
32 | #include <kern/assert.h> |
33 | #include <stdint.h> |
34 | #include <kern/startup.h> |
35 | #include <kern/locks.h> |
36 | #include <kern/kalloc.h> |
37 | #include <kern/task.h> |
38 | |
39 | #include <Tightbeam/tightbeam.h> |
40 | |
41 | #include "kern/exclaves.tightbeam.h" |
42 | |
43 | #include "exclaves_debug.h" |
44 | #include "exclaves_conclave.h" |
45 | #include "exclaves_resource.h" |
46 | |
47 | /* -------------------------------------------------------------------------- */ |
48 | #pragma mark Conclave Launcher |
49 | |
50 | kern_return_t |
51 | exclaves_conclave_launcher_init(uint64_t id, tb_client_connection_t *connection) |
52 | { |
53 | assert3p(connection, !=, NULL); |
54 | |
55 | tb_error_t tb_result = TB_ERROR_SUCCESS; |
56 | |
57 | conclave_launcher_conclavecontrol_s control = {}; |
58 | |
59 | tb_endpoint_t conclave_control_endpoint = |
60 | tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU, id, |
61 | TB_ENDPOINT_OPTIONS_NONE); |
62 | |
63 | tb_result = conclave_launcher_conclavecontrol__init(&control, |
64 | conclave_control_endpoint); |
65 | if (tb_result != TB_ERROR_SUCCESS) { |
66 | exclaves_debug_printf(show_errors, |
67 | "conclave init failure: %llu\n" , id); |
68 | return KERN_FAILURE; |
69 | } |
70 | |
71 | *connection = control.connection; |
72 | |
73 | return KERN_SUCCESS; |
74 | } |
75 | |
76 | kern_return_t |
77 | exclaves_conclave_launcher_launch(const tb_client_connection_t connection) |
78 | { |
79 | assert3p(connection, !=, NULL); |
80 | |
81 | tb_error_t tb_result = TB_ERROR_SUCCESS; |
82 | |
83 | const conclave_launcher_conclavecontrol_s control = { |
84 | .connection = connection, |
85 | }; |
86 | |
87 | __block bool success = false; |
88 | |
89 | /* BEGIN IGNORE CODESTYLE */ |
90 | tb_result = conclave_launcher_conclavecontrol_launch(&control, |
91 | ^(conclave_launcher_conclavecontrol_launch__result_s result) { |
92 | conclave_launcher_conclavestatus_s *status = NULL; |
93 | status = conclave_launcher_conclavecontrol_launch__result_get_success(&result); |
94 | if (status != NULL) { |
95 | success = true; |
96 | return; |
97 | } |
98 | |
99 | conclave_launcher_conclavelauncherfailure_s *failure = NULL; |
100 | failure = conclave_launcher_conclavecontrol_launch__result_get_failure(&result); |
101 | assert3p(failure, !=, NULL); |
102 | exclaves_debug_printf(show_errors, |
103 | "conclave launch failure: failure %u\n" , *failure); |
104 | }); |
105 | /* END IGNORE CODESTYLE */ |
106 | |
107 | if (tb_result != TB_ERROR_SUCCESS || !success) { |
108 | return KERN_FAILURE; |
109 | } |
110 | |
111 | return KERN_SUCCESS; |
112 | } |
113 | |
114 | kern_return_t |
115 | exclaves_conclave_launcher_stop(const tb_client_connection_t connection, |
116 | uint32_t stop_reason) |
117 | { |
118 | assert3p(connection, !=, NULL); |
119 | |
120 | tb_error_t tb_result = TB_ERROR_SUCCESS; |
121 | |
122 | const conclave_launcher_conclavecontrol_s control = { |
123 | .connection = connection, |
124 | }; |
125 | |
126 | __block bool success = false; |
127 | |
128 | /* BEGIN IGNORE CODESTYLE */ |
129 | tb_result = conclave_launcher_conclavecontrol_stop( |
130 | &control, stop_reason, true, |
131 | ^(conclave_launcher_conclavecontrol_stop__result_s result) { |
132 | conclave_launcher_conclavestatus_s *status = NULL; |
133 | status = conclave_launcher_conclavecontrol_stop__result_get_success(&result); |
134 | if (status != NULL) { |
135 | success = true; |
136 | return; |
137 | } |
138 | |
139 | conclave_launcher_conclavelauncherfailure_s *failure = NULL; |
140 | failure = conclave_launcher_conclavecontrol_stop__result_get_failure(&result); |
141 | assert3p(failure, !=, NULL); |
142 | |
143 | exclaves_debug_printf(show_errors, |
144 | "conclave stop failure: failure %u\n" , *failure); |
145 | }); |
146 | /* END IGNORE CODESTYLE */ |
147 | |
148 | if (tb_result != TB_ERROR_SUCCESS || !success) { |
149 | return KERN_FAILURE; |
150 | } |
151 | |
152 | return KERN_SUCCESS; |
153 | } |
154 | |
155 | |
156 | /* -------------------------------------------------------------------------- */ |
157 | #pragma mark Conclave Upcalls |
158 | |
159 | tb_error_t |
160 | exclaves_conclave_upcall_suspend(const uint32_t flags, |
161 | tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_suspend__result_s)) |
162 | { |
163 | xnuupcalls_conclavescidlist_s scid_list = {}; |
164 | |
165 | exclaves_debug_printf(show_lifecycle_upcalls, |
166 | "[lifecycle_upcalls] conclave_suspend flags %x\n" , flags); |
167 | |
168 | kern_return_t kret = task_suspend_conclave_upcall(scid_list.scid, |
169 | ARRAY_COUNT(scid_list.scid)); |
170 | |
171 | exclaves_debug_printf(show_lifecycle_upcalls, |
172 | "[lifecycle_upcalls] task_suspend_conclave_upcall returned %x\n" , kret); |
173 | |
174 | xnuupcalls_xnuupcalls_conclave_suspend__result_s result = {}; |
175 | |
176 | switch (kret) { |
177 | case KERN_SUCCESS: |
178 | xnuupcalls_xnuupcalls_conclave_suspend__result_init_success(&result, scid_list); |
179 | break; |
180 | |
181 | case KERN_INVALID_TASK: |
182 | case KERN_INVALID_ARGUMENT: |
183 | xnuupcalls_xnuupcalls_conclave_suspend__result_init_failure(&result, |
184 | XNUUPCALLS_LIFECYCLEERROR_INVALIDTASK); |
185 | break; |
186 | |
187 | default: |
188 | xnuupcalls_xnuupcalls_conclave_suspend__result_init_failure(&result, |
189 | XNUUPCALLS_LIFECYCLEERROR_FAILURE); |
190 | break; |
191 | } |
192 | |
193 | return completion(result); |
194 | } |
195 | |
196 | tb_error_t |
197 | exclaves_conclave_upcall_stop(const uint32_t flags, |
198 | tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_stop__result_s)) |
199 | { |
200 | exclaves_debug_printf(show_lifecycle_upcalls, |
201 | "[lifecycle_upcalls] conclave_stop flags %x\n" , flags); |
202 | |
203 | kern_return_t kret = task_stop_conclave_upcall(); |
204 | |
205 | exclaves_debug_printf(show_lifecycle_upcalls, |
206 | "[lifecycle_upcalls] task_stop_conclave_upcall returned %x\n" , kret); |
207 | |
208 | xnuupcalls_xnuupcalls_conclave_stop__result_s result = {}; |
209 | |
210 | switch (kret) { |
211 | case KERN_SUCCESS: |
212 | xnuupcalls_xnuupcalls_conclave_stop__result_init_success(&result); |
213 | break; |
214 | |
215 | case KERN_INVALID_TASK: |
216 | case KERN_INVALID_ARGUMENT: |
217 | xnuupcalls_xnuupcalls_conclave_stop__result_init_failure(&result, |
218 | XNUUPCALLS_LIFECYCLEERROR_INVALIDTASK); |
219 | break; |
220 | |
221 | default: |
222 | xnuupcalls_xnuupcalls_conclave_stop__result_init_failure(&result, |
223 | XNUUPCALLS_LIFECYCLEERROR_FAILURE); |
224 | break; |
225 | } |
226 | |
227 | return completion(result); |
228 | } |
229 | |
230 | tb_error_t |
231 | exclaves_conclave_upcall_crash_info(const xnuupcalls_conclavesharedbuffer_s *shared_buf, |
232 | const uint32_t length, |
233 | tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_crash_info__result_s )) |
234 | { |
235 | task_t task; |
236 | |
237 | /* Check if the thread was calling conclave stop on another task */ |
238 | task = current_thread()->conclave_stop_task; |
239 | if (task == TASK_NULL) { |
240 | task = current_task(); |
241 | } |
242 | |
243 | exclaves_debug_printf(show_lifecycle_upcalls, |
244 | "[lifecycle_upcalls] conclave_crash_info\n" ); |
245 | |
246 | kern_return_t kret = task_crash_info_conclave_upcall(task, shared_buf, length); |
247 | |
248 | exclaves_debug_printf(show_lifecycle_upcalls, |
249 | "[lifecycle_upcalls] task_crash_info_conclave_upcall returned 0x%x\n" , kret); |
250 | |
251 | xnuupcalls_xnuupcalls_conclave_crash_info__result_s result = {}; |
252 | |
253 | switch (kret) { |
254 | case KERN_SUCCESS: |
255 | xnuupcalls_xnuupcalls_conclave_crash_info__result_init_success(&result); |
256 | break; |
257 | |
258 | case KERN_INVALID_TASK: |
259 | case KERN_INVALID_ARGUMENT: |
260 | xnuupcalls_xnuupcalls_conclave_crash_info__result_init_failure(&result, |
261 | XNUUPCALLS_LIFECYCLEERROR_INVALIDTASK); |
262 | break; |
263 | |
264 | default: |
265 | xnuupcalls_xnuupcalls_conclave_crash_info__result_init_failure(&result, |
266 | XNUUPCALLS_LIFECYCLEERROR_FAILURE); |
267 | break; |
268 | } |
269 | |
270 | return completion(result); |
271 | } |
272 | |
273 | #if DEVELOPMENT || DEBUG |
274 | |
275 | /* -------------------------------------------------------------------------- */ |
276 | #pragma mark Testing |
277 | |
278 | typedef struct conclave_test_context { |
279 | tb_endpoint_t conclave_control_endpoint; |
280 | conclave_launcher_conclavecontrol_s conclave_control; |
281 | tb_endpoint_t conclave_debug_endpoint; |
282 | conclave_launcher_conclavedebug_s conclave_debug; |
283 | } conclave_test_context_t; |
284 | |
285 | extern lck_grp_t exclaves_lck_grp; |
286 | |
287 | LCK_MTX_DECLARE(exclaves_conclave_lock, &exclaves_lck_grp); |
288 | |
289 | static conclave_test_context_t *conclave_context = NULL; |
290 | |
291 | #define EXCLAVES_ID_CONCLAVECONTROL_EP \ |
292 | (exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \ |
293 | "com.apple.service.ConclaveLauncherControl")) |
294 | |
295 | #define EXCLAVES_ID_CONCLAVEDEBUG_EP \ |
296 | (exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \ |
297 | "com.apple.service.ConclaveLauncherDebug")) |
298 | |
299 | static TUNABLE(bool, enable_hello_conclaves, "enable_hello_conclaves" , false); |
300 | |
301 | static int |
302 | exclaves_hello_conclaves_test(int64_t in, int64_t *out) |
303 | { |
304 | tb_error_t tb_result = TB_ERROR_SUCCESS; |
305 | if (!enable_hello_conclaves) { |
306 | exclaves_debug_printf(show_test_output, |
307 | "%s: SKIPPED: enable_hello_conclaves not set\n" , __func__); |
308 | *out = -1; |
309 | return 0; |
310 | } |
311 | |
312 | if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) { |
313 | exclaves_debug_printf(show_test_output, |
314 | "%s: SKIPPED: Exclaves not available\n" , __func__); |
315 | *out = -1; |
316 | return 0; |
317 | } |
318 | lck_mtx_lock(&exclaves_conclave_lock); |
319 | switch (in) { |
320 | case 0: { /* init */ |
321 | if (conclave_context != NULL) { |
322 | break; |
323 | } |
324 | |
325 | conclave_context = kalloc_type(conclave_test_context_t, Z_WAITOK); |
326 | assert(conclave_context != NULL); |
327 | |
328 | /* BEGIN IGNORE CODESTYLE */ |
329 | conclave_context->conclave_control_endpoint = |
330 | tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU, |
331 | EXCLAVES_ID_CONCLAVECONTROL_EP, TB_ENDPOINT_OPTIONS_NONE); |
332 | tb_result = conclave_launcher_conclavecontrol__init( |
333 | &conclave_context->conclave_control, |
334 | conclave_context->conclave_control_endpoint); |
335 | assert(tb_result == TB_ERROR_SUCCESS); |
336 | |
337 | conclave_context->conclave_debug_endpoint = |
338 | tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU, |
339 | EXCLAVES_ID_CONCLAVEDEBUG_EP, TB_ENDPOINT_OPTIONS_NONE); |
340 | tb_result = conclave_launcher_conclavedebug__init( |
341 | &conclave_context->conclave_debug, |
342 | conclave_context->conclave_debug_endpoint); |
343 | assert(tb_result == TB_ERROR_SUCCESS); |
344 | /* END IGNORE CODESTYLE */ |
345 | |
346 | break; |
347 | } |
348 | case 1: { /* launch conclave */ |
349 | assert(conclave_context != NULL); |
350 | |
351 | /* BEGIN IGNORE CODESTYLE */ |
352 | tb_result = conclave_launcher_conclavecontrol_launch( |
353 | &conclave_context->conclave_control, |
354 | ^(conclave_launcher_conclavecontrol_launch__result_s result) { |
355 | conclave_launcher_conclavestatus_s *status = NULL; |
356 | status = conclave_launcher_conclavecontrol_launch__result_get_success(&result); |
357 | if (status != NULL) { |
358 | exclaves_debug_printf(show_test_output, |
359 | "%s:%d conclave launch success: status %u\n" , |
360 | __func__, __LINE__, *status); |
361 | return; |
362 | } |
363 | |
364 | conclave_launcher_conclavelauncherfailure_s *failure = NULL; |
365 | failure = conclave_launcher_conclavecontrol_launch__result_get_failure(&result); |
366 | assert3p(failure, !=, NULL); |
367 | exclaves_debug_printf(show_errors, |
368 | "%s:%d conclave launch failure: failure %u\n" , |
369 | __func__, __LINE__, *failure); |
370 | }); |
371 | /* END IGNORE CODESTYLE */ |
372 | |
373 | if (tb_result != TB_ERROR_SUCCESS) { |
374 | exclaves_debug_printf(show_errors, |
375 | "%s:%d conclave launch failure: tightbeam error %u\n" , |
376 | __func__, __LINE__, tb_result); |
377 | } |
378 | break; |
379 | } |
380 | case 2: { /* status */ |
381 | assert(conclave_context != NULL); |
382 | |
383 | /* BEGIN IGNORE CODESTYLE */ |
384 | tb_result = conclave_launcher_conclavecontrol_status( |
385 | &conclave_context->conclave_control, |
386 | ^(conclave_launcher_conclavecontrol_status__result_s result) { |
387 | conclave_launcher_conclavestatus_s *status = NULL; |
388 | status = conclave_launcher_conclavecontrol_status__result_get_success(&result); |
389 | if (status != NULL) { |
390 | exclaves_debug_printf(show_test_output, |
391 | "%s:%d conclave status success: status %u\n" , |
392 | __func__, __LINE__, *status); |
393 | return; |
394 | } |
395 | |
396 | conclave_launcher_conclavelauncherfailure_s *failure = NULL; |
397 | failure = conclave_launcher_conclavecontrol_status__result_get_failure(&result); |
398 | assert3p(failure, !=, NULL); |
399 | exclaves_debug_printf(show_errors, |
400 | "%s:%d conclave status failure: failure %u\n" , |
401 | __func__, __LINE__, *failure); |
402 | }); |
403 | /* END IGNORE CODESTYLE */ |
404 | |
405 | if (tb_result != TB_ERROR_SUCCESS) { |
406 | exclaves_debug_printf(show_errors, |
407 | "%s:%d conclave status failure: tightbeam error %u\n" , |
408 | __func__, __LINE__, tb_result); |
409 | } |
410 | break; |
411 | } |
412 | case 3: { /* stop conclave */ |
413 | conclave_launcher_conclavestopreason_s stop_reason = |
414 | CONCLAVE_LAUNCHER_CONCLAVESTOPREASON_EXIT; |
415 | assert(conclave_context != NULL); |
416 | |
417 | /* BEGIN IGNORE CODESTYLE */ |
418 | tb_result = conclave_launcher_conclavecontrol_stop( |
419 | &conclave_context->conclave_control, stop_reason, true, |
420 | ^(conclave_launcher_conclavecontrol_stop__result_s result) { |
421 | conclave_launcher_conclavestatus_s *status = NULL; |
422 | status = conclave_launcher_conclavecontrol_stop__result_get_success(&result); |
423 | if (status != NULL) { |
424 | exclaves_debug_printf(show_test_output, |
425 | "%s:%d conclave stop success: status %u\n" , |
426 | __func__, __LINE__, *status); |
427 | return; |
428 | } |
429 | |
430 | conclave_launcher_conclavelauncherfailure_s *failure = NULL; |
431 | failure = conclave_launcher_conclavecontrol_stop__result_get_failure(&result); |
432 | assert3p(failure, !=, NULL); |
433 | exclaves_debug_printf(show_errors, |
434 | "%s:%d conclave stop failure: failure %u\n" , |
435 | __func__, __LINE__, *failure); |
436 | }); |
437 | /* END IGNORE CODESTYLE */ |
438 | |
439 | if (tb_result != TB_ERROR_SUCCESS) { |
440 | exclaves_debug_printf(show_errors, |
441 | "%s:%d conclave stop failure: tightbeam error %u\n" , |
442 | __func__, __LINE__, tb_result); |
443 | } |
444 | break; |
445 | } |
446 | case 4: { /* debug info */ |
447 | assert(conclave_context != NULL); |
448 | |
449 | /* BEGIN IGNORE CODESTYLE */ |
450 | tb_result = conclave_launcher_conclavedebug_debuginfo( |
451 | &conclave_context->conclave_debug, |
452 | ^(conclave_launcher_conclavedebug_debuginfo__result_s result) { |
453 | conclave_launcher_conclavedebuginfo_s *debuginfo = NULL; |
454 | debuginfo = conclave_launcher_conclavedebug_debuginfo__result_get_success(&result); |
455 | if (debuginfo != NULL) { |
456 | uuid_string_t uuid_string; |
457 | uuid_unparse(debuginfo->conclaveuuid, uuid_string); |
458 | exclaves_debug_printf(show_test_output, |
459 | "%s:%d conclave debuginfo success: result %s\n" , |
460 | __func__, __LINE__, uuid_string); |
461 | return; |
462 | } |
463 | |
464 | conclave_launcher_conclavelauncherfailure_s *failure = NULL; |
465 | failure = conclave_launcher_conclavedebug_debuginfo__result_get_failure(&result); |
466 | assert3p(failure, !=, NULL); |
467 | exclaves_debug_printf(show_errors, |
468 | "%s:%d conclave debuginfo failure: failure %u\n" , |
469 | __func__, __LINE__, *failure); |
470 | }); |
471 | /* END IGNORE CODESTYLE */ |
472 | |
473 | if (tb_result != TB_ERROR_SUCCESS) { |
474 | exclaves_debug_printf(show_errors, |
475 | "%s:%d conclave debug info failure: tightbeam error %u\n" , |
476 | __func__, __LINE__, tb_result); |
477 | } |
478 | break; |
479 | } |
480 | } |
481 | lck_mtx_unlock(&exclaves_conclave_lock); |
482 | *out = tb_result == TB_ERROR_SUCCESS ? 1 : 0; |
483 | return tb_result == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE; |
484 | } |
485 | |
486 | SYSCTL_TEST_REGISTER(exclaves_hello_conclaves_test, |
487 | exclaves_hello_conclaves_test); |
488 | |
489 | #endif /* DEVELOPMENT || DEBUG */ |
490 | |
491 | #endif /* CONFIG_EXCLAVES */ |
492 | |