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
50kern_return_t
51exclaves_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
76kern_return_t
77exclaves_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
114kern_return_t
115exclaves_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
159tb_error_t
160exclaves_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
196tb_error_t
197exclaves_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
230tb_error_t
231exclaves_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
278typedef 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
285extern lck_grp_t exclaves_lck_grp;
286
287LCK_MTX_DECLARE(exclaves_conclave_lock, &exclaves_lck_grp);
288
289static 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
299static TUNABLE(bool, enable_hello_conclaves, "enable_hello_conclaves", false);
300
301static int
302exclaves_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
486SYSCTL_TEST_REGISTER(exclaves_hello_conclaves_test,
487 exclaves_hello_conclaves_test);
488
489#endif /* DEVELOPMENT || DEBUG */
490
491#endif /* CONFIG_EXCLAVES */
492