This is xnu-11215.1.10. See this file in:
/*
* Copyright (c) 2023 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#if CONFIG_EXCLAVES
#include <mach/kern_return.h>
#include <kern/assert.h>
#include <stdint.h>
#include <kern/startup.h>
#include <kern/locks.h>
#include <kern/kalloc.h>
#include <kern/task.h>
#include <Tightbeam/tightbeam.h>
#include "kern/exclaves.tightbeam.h"
#include "exclaves_debug.h"
#include "exclaves_conclave.h"
#include "exclaves_resource.h"
#include "exclaves_internal.h"
/* -------------------------------------------------------------------------- */
#pragma mark Conclave Launcher
kern_return_t
exclaves_conclave_launcher_init(uint64_t id, tb_client_connection_t *connection)
{
assert3p(connection, !=, NULL);
tb_error_t tb_result = TB_ERROR_SUCCESS;
conclave_launcher_conclavecontrol_s control = {};
tb_endpoint_t conclave_control_endpoint =
tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU, id,
TB_ENDPOINT_OPTIONS_NONE);
tb_result = conclave_launcher_conclavecontrol__init(&control,
conclave_control_endpoint);
if (tb_result != TB_ERROR_SUCCESS) {
exclaves_debug_printf(show_errors,
"conclave init failure: %llu\n", id);
return KERN_FAILURE;
}
*connection = control.connection;
return KERN_SUCCESS;
}
kern_return_t
exclaves_conclave_launcher_launch(const tb_client_connection_t connection)
{
assert3p(connection, !=, NULL);
tb_error_t tb_result = TB_ERROR_SUCCESS;
const conclave_launcher_conclavecontrol_s control = {
.connection = connection,
};
__block bool success = false;
/* BEGIN IGNORE CODESTYLE */
tb_result = conclave_launcher_conclavecontrol_launch(&control,
^(conclave_launcher_conclavecontrol_launch__result_s result) {
conclave_launcher_conclavestatus_s *status = NULL;
status = conclave_launcher_conclavecontrol_launch__result_get_success(&result);
if (status != NULL) {
success = true;
return;
}
conclave_launcher_conclavelauncherfailure_s *failure = NULL;
failure = conclave_launcher_conclavecontrol_launch__result_get_failure(&result);
assert3p(failure, !=, NULL);
exclaves_debug_printf(show_errors,
"conclave launch failure: failure %u\n", *failure);
});
/* END IGNORE CODESTYLE */
if (tb_result != TB_ERROR_SUCCESS || !success) {
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
kern_return_t
exclaves_conclave_launcher_stop(const tb_client_connection_t connection,
uint32_t stop_reason)
{
assert3p(connection, !=, NULL);
tb_error_t tb_result = TB_ERROR_SUCCESS;
const conclave_launcher_conclavecontrol_s control = {
.connection = connection,
};
__block bool success = false;
/* BEGIN IGNORE CODESTYLE */
tb_result = conclave_launcher_conclavecontrol_stop(
&control, stop_reason, true,
^(conclave_launcher_conclavecontrol_stop__result_s result) {
conclave_launcher_conclavestatus_s *status = NULL;
status = conclave_launcher_conclavecontrol_stop__result_get_success(&result);
if (status != NULL) {
success = true;
return;
}
conclave_launcher_conclavelauncherfailure_s *failure = NULL;
failure = conclave_launcher_conclavecontrol_stop__result_get_failure(&result);
assert3p(failure, !=, NULL);
exclaves_debug_printf(show_errors,
"conclave stop failure: failure %u\n", *failure);
});
/* END IGNORE CODESTYLE */
if (tb_result != TB_ERROR_SUCCESS || !success) {
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
/* -------------------------------------------------------------------------- */
#pragma mark Conclave Upcalls
tb_error_t
exclaves_conclave_upcall_suspend(const uint32_t flags,
tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_suspend__result_s))
{
xnuupcalls_conclavescidlist_s scid_list = {};
exclaves_debug_printf(show_lifecycle_upcalls,
"[lifecycle_upcalls] conclave_suspend flags %x\n", flags);
kern_return_t kret = task_suspend_conclave_upcall(scid_list.scid,
ARRAY_COUNT(scid_list.scid));
exclaves_debug_printf(show_lifecycle_upcalls,
"[lifecycle_upcalls] task_suspend_conclave_upcall returned %x\n", kret);
xnuupcalls_xnuupcalls_conclave_suspend__result_s result = {};
switch (kret) {
case KERN_SUCCESS:
xnuupcalls_xnuupcalls_conclave_suspend__result_init_success(&result, scid_list);
break;
case KERN_INVALID_TASK:
case KERN_INVALID_ARGUMENT:
xnuupcalls_xnuupcalls_conclave_suspend__result_init_failure(&result,
XNUUPCALLS_LIFECYCLEERROR_INVALIDTASK);
break;
default:
xnuupcalls_xnuupcalls_conclave_suspend__result_init_failure(&result,
XNUUPCALLS_LIFECYCLEERROR_FAILURE);
break;
}
return completion(result);
}
tb_error_t
exclaves_conclave_upcall_stop(const uint32_t flags,
tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_stop__result_s))
{
exclaves_debug_printf(show_lifecycle_upcalls,
"[lifecycle_upcalls] conclave_stop flags %x\n", flags);
kern_return_t kret = task_stop_conclave_upcall();
exclaves_debug_printf(show_lifecycle_upcalls,
"[lifecycle_upcalls] task_stop_conclave_upcall returned %x\n", kret);
xnuupcalls_xnuupcalls_conclave_stop__result_s result = {};
switch (kret) {
case KERN_SUCCESS:
xnuupcalls_xnuupcalls_conclave_stop__result_init_success(&result);
break;
case KERN_INVALID_TASK:
case KERN_INVALID_ARGUMENT:
xnuupcalls_xnuupcalls_conclave_stop__result_init_failure(&result,
XNUUPCALLS_LIFECYCLEERROR_INVALIDTASK);
break;
default:
xnuupcalls_xnuupcalls_conclave_stop__result_init_failure(&result,
XNUUPCALLS_LIFECYCLEERROR_FAILURE);
break;
}
return completion(result);
}
tb_error_t
exclaves_conclave_upcall_crash_info(const xnuupcalls_conclavesharedbuffer_s *shared_buf,
const uint32_t length,
tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_crash_info__result_s ))
{
task_t task;
/* Check if the thread was calling conclave stop on another task */
task = current_thread()->conclave_stop_task;
if (task == TASK_NULL) {
task = current_task();
}
exclaves_debug_printf(show_lifecycle_upcalls,
"[lifecycle_upcalls] conclave_crash_info\n");
kern_return_t kret = task_crash_info_conclave_upcall(task, shared_buf, length);
exclaves_debug_printf(show_lifecycle_upcalls,
"[lifecycle_upcalls] task_crash_info_conclave_upcall returned 0x%x\n", kret);
xnuupcalls_xnuupcalls_conclave_crash_info__result_s result = {};
switch (kret) {
case KERN_SUCCESS:
xnuupcalls_xnuupcalls_conclave_crash_info__result_init_success(&result);
break;
case KERN_INVALID_TASK:
case KERN_INVALID_ARGUMENT:
xnuupcalls_xnuupcalls_conclave_crash_info__result_init_failure(&result,
XNUUPCALLS_LIFECYCLEERROR_INVALIDTASK);
break;
default:
xnuupcalls_xnuupcalls_conclave_crash_info__result_init_failure(&result,
XNUUPCALLS_LIFECYCLEERROR_FAILURE);
break;
}
return completion(result);
}
#if DEVELOPMENT || DEBUG
/* -------------------------------------------------------------------------- */
#pragma mark Testing
typedef struct conclave_test_context {
tb_endpoint_t conclave_control_endpoint;
conclave_launcher_conclavecontrol_s conclave_control;
tb_endpoint_t conclave_debug_endpoint;
conclave_launcher_conclavedebug_s conclave_debug;
} conclave_test_context_t;
LCK_MTX_DECLARE(exclaves_conclave_lock, &exclaves_lck_grp);
static conclave_test_context_t *conclave_context = NULL;
#define EXCLAVES_ID_CONCLAVECONTROL_EP \
(exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \
"com.apple.service.ConclaveLauncherControl"))
#define EXCLAVES_ID_CONCLAVEDEBUG_EP \
(exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \
"com.apple.service.ConclaveLauncherDebug"))
static TUNABLE(bool, enable_hello_conclaves, "enable_hello_conclaves", false);
static int
exclaves_hello_conclaves_test(int64_t in, int64_t *out)
{
tb_error_t tb_result = TB_ERROR_SUCCESS;
if (!enable_hello_conclaves) {
exclaves_debug_printf(show_test_output,
"%s: SKIPPED: enable_hello_conclaves not set\n", __func__);
*out = -1;
return 0;
}
if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) {
exclaves_debug_printf(show_test_output,
"%s: SKIPPED: Exclaves not available\n", __func__);
*out = -1;
return 0;
}
lck_mtx_lock(&exclaves_conclave_lock);
switch (in) {
case 0: { /* init */
if (conclave_context != NULL) {
break;
}
conclave_context = kalloc_type(conclave_test_context_t, Z_WAITOK);
assert(conclave_context != NULL);
/* BEGIN IGNORE CODESTYLE */
conclave_context->conclave_control_endpoint =
tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU,
EXCLAVES_ID_CONCLAVECONTROL_EP, TB_ENDPOINT_OPTIONS_NONE);
tb_result = conclave_launcher_conclavecontrol__init(
&conclave_context->conclave_control,
conclave_context->conclave_control_endpoint);
assert(tb_result == TB_ERROR_SUCCESS);
conclave_context->conclave_debug_endpoint =
tb_endpoint_create_with_value(TB_TRANSPORT_TYPE_XNU,
EXCLAVES_ID_CONCLAVEDEBUG_EP, TB_ENDPOINT_OPTIONS_NONE);
tb_result = conclave_launcher_conclavedebug__init(
&conclave_context->conclave_debug,
conclave_context->conclave_debug_endpoint);
assert(tb_result == TB_ERROR_SUCCESS);
/* END IGNORE CODESTYLE */
break;
}
case 1: { /* launch conclave */
assert(conclave_context != NULL);
/* BEGIN IGNORE CODESTYLE */
tb_result = conclave_launcher_conclavecontrol_launch(
&conclave_context->conclave_control,
^(conclave_launcher_conclavecontrol_launch__result_s result) {
conclave_launcher_conclavestatus_s *status = NULL;
status = conclave_launcher_conclavecontrol_launch__result_get_success(&result);
if (status != NULL) {
exclaves_debug_printf(show_test_output,
"%s:%d conclave launch success: status %u\n",
__func__, __LINE__, *status);
return;
}
conclave_launcher_conclavelauncherfailure_s *failure = NULL;
failure = conclave_launcher_conclavecontrol_launch__result_get_failure(&result);
assert3p(failure, !=, NULL);
exclaves_debug_printf(show_errors,
"%s:%d conclave launch failure: failure %u\n",
__func__, __LINE__, *failure);
});
/* END IGNORE CODESTYLE */
if (tb_result != TB_ERROR_SUCCESS) {
exclaves_debug_printf(show_errors,
"%s:%d conclave launch failure: tightbeam error %u\n",
__func__, __LINE__, tb_result);
}
break;
}
case 2: { /* status */
assert(conclave_context != NULL);
/* BEGIN IGNORE CODESTYLE */
tb_result = conclave_launcher_conclavecontrol_status(
&conclave_context->conclave_control,
^(conclave_launcher_conclavecontrol_status__result_s result) {
conclave_launcher_conclavestatus_s *status = NULL;
status = conclave_launcher_conclavecontrol_status__result_get_success(&result);
if (status != NULL) {
exclaves_debug_printf(show_test_output,
"%s:%d conclave status success: status %u\n",
__func__, __LINE__, *status);
return;
}
conclave_launcher_conclavelauncherfailure_s *failure = NULL;
failure = conclave_launcher_conclavecontrol_status__result_get_failure(&result);
assert3p(failure, !=, NULL);
exclaves_debug_printf(show_errors,
"%s:%d conclave status failure: failure %u\n",
__func__, __LINE__, *failure);
});
/* END IGNORE CODESTYLE */
if (tb_result != TB_ERROR_SUCCESS) {
exclaves_debug_printf(show_errors,
"%s:%d conclave status failure: tightbeam error %u\n",
__func__, __LINE__, tb_result);
}
break;
}
case 3: { /* stop conclave */
conclave_launcher_conclavestopreason_s stop_reason =
CONCLAVE_LAUNCHER_CONCLAVESTOPREASON_EXIT;
assert(conclave_context != NULL);
/* BEGIN IGNORE CODESTYLE */
tb_result = conclave_launcher_conclavecontrol_stop(
&conclave_context->conclave_control, stop_reason, true,
^(conclave_launcher_conclavecontrol_stop__result_s result) {
conclave_launcher_conclavestatus_s *status = NULL;
status = conclave_launcher_conclavecontrol_stop__result_get_success(&result);
if (status != NULL) {
exclaves_debug_printf(show_test_output,
"%s:%d conclave stop success: status %u\n",
__func__, __LINE__, *status);
return;
}
conclave_launcher_conclavelauncherfailure_s *failure = NULL;
failure = conclave_launcher_conclavecontrol_stop__result_get_failure(&result);
assert3p(failure, !=, NULL);
exclaves_debug_printf(show_errors,
"%s:%d conclave stop failure: failure %u\n",
__func__, __LINE__, *failure);
});
/* END IGNORE CODESTYLE */
if (tb_result != TB_ERROR_SUCCESS) {
exclaves_debug_printf(show_errors,
"%s:%d conclave stop failure: tightbeam error %u\n",
__func__, __LINE__, tb_result);
}
break;
}
case 4: { /* debug info */
assert(conclave_context != NULL);
/* BEGIN IGNORE CODESTYLE */
tb_result = conclave_launcher_conclavedebug_debuginfo(
&conclave_context->conclave_debug,
^(conclave_launcher_conclavedebug_debuginfo__result_s result) {
conclave_launcher_conclavedebuginfo_s *debuginfo = NULL;
debuginfo = conclave_launcher_conclavedebug_debuginfo__result_get_success(&result);
if (debuginfo != NULL) {
uuid_string_t uuid_string;
uuid_unparse(debuginfo->conclaveuuid, uuid_string);
exclaves_debug_printf(show_test_output,
"%s:%d conclave debuginfo success: result %s\n",
__func__, __LINE__, uuid_string);
return;
}
conclave_launcher_conclavelauncherfailure_s *failure = NULL;
failure = conclave_launcher_conclavedebug_debuginfo__result_get_failure(&result);
assert3p(failure, !=, NULL);
exclaves_debug_printf(show_errors,
"%s:%d conclave debuginfo failure: failure %u\n",
__func__, __LINE__, *failure);
});
/* END IGNORE CODESTYLE */
if (tb_result != TB_ERROR_SUCCESS) {
exclaves_debug_printf(show_errors,
"%s:%d conclave debug info failure: tightbeam error %u\n",
__func__, __LINE__, tb_result);
}
break;
}
}
lck_mtx_unlock(&exclaves_conclave_lock);
*out = tb_result == TB_ERROR_SUCCESS ? 1 : 0;
return tb_result == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
}
SYSCTL_TEST_REGISTER(exclaves_hello_conclaves_test,
exclaves_hello_conclaves_test);
#endif /* DEVELOPMENT || DEBUG */
#endif /* CONFIG_EXCLAVES */