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 <stdint.h>
#include <mach/kern_return.h>
#include <kern/assert.h>
#include <kern/misc_protos.h>

#include "exclaves_debug.h"
#include "exclaves_shared_memory.h"
#include "kern/exclaves.tightbeam.h"

kern_return_t
exclaves_shared_memory_init(const uint64_t endpoint,
    sharedmemorybase_segxnuaccess_s *sm_client)
{
	assert3p(sm_client, !=, NULL);

	tb_endpoint_t ep = tb_endpoint_create_with_value(
		TB_TRANSPORT_TYPE_XNU, endpoint, TB_ENDPOINT_OPTIONS_NONE);
	tb_error_t ret = sharedmemorybase_segxnuaccess__init(sm_client, ep);

	return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
}

static kern_return_t
exclaves_shared_memory_access_check(
	const sharedmemorybase_segxnuaccess_s *sm_client,
	const sharedmemorybase_perms_s perm, const uint64_t endpage,
	bool *access_allowed)
{
	assert3p(sm_client, !=, NULL);
	assert3p(access_allowed, !=, NULL);

	tb_error_t ret = TB_ERROR_SUCCESS;

	__block bool allowed = true;
	ret = sharedmemorybase_segxnuaccess_xnuaccessstatus(sm_client,
	    ^(sharedmemorybase_accessstatus_s result) {
		/*
		 * Check permissions.
		 * For the moment just check for writable
		 * access (if relevant).
		 */
		if (perm == SHAREDMEMORYBASE_PERMS_READWRITE) {
		        allowed = allowed && perm == result.permissions;
		}

		/* Check that it's xnu mappable. */
		allowed = allowed && result.xnu;

		/* Check that there are enough pages. */
		allowed = allowed && endpage <= result.segmentstatus.npages;
	});

	if (ret != TB_ERROR_SUCCESS) {
		return KERN_FAILURE;
	}

	*access_allowed = allowed;
	return KERN_SUCCESS;
}

kern_return_t
exclaves_shared_memory_setup(const sharedmemorybase_segxnuaccess_s *sm_client,
    const sharedmemorybase_perms_s perm, const uint64_t startpage,
    const uint64_t endpage, sharedmemorybase_mapping_s *mapping)
{
	assert3p(sm_client, !=, NULL);
	assert3p(mapping, !=, NULL);
	assert3u(startpage, <, endpage);
	assert(perm == SHAREDMEMORYBASE_PERMS_READWRITE ||
	    perm == SHAREDMEMORYBASE_PERMS_READONLY);

	tb_error_t ret = TB_ERROR_SUCCESS;

	/* Do a quick sanity check that this access is allowed. */
	bool allowed = false;
	kern_return_t kret = exclaves_shared_memory_access_check(sm_client, perm, endpage, &allowed);
	if (kret != KERN_SUCCESS) {
		return kret;
	}

	sharedmemorybase_pagerange__opt_s opt_range = {};

	sharedmemorybase_pagerange_s range = {
		.startpage = startpage,
		.endpage = endpage,
	};
	sharedmemorybase_pagerange__opt_init(&opt_range, &range);

	*mapping = 0;

	/* BEGIN IGNORE CODESTYLE */
	ret = sharedmemorybase_segxnuaccess_createxnumapping(sm_client, perm,
	    &opt_range,
	    ^(sharedmemorybase_segxnuaccess_createxnumapping__result_s result) {
		sharedmemorybase_accesserror_s *error = NULL;
		error = sharedmemorybase_segxnuaccess_createxnumapping__result_get_failure(&result);
		if (error != NULL) {
			exclaves_debug_printf(show_errors,
			    "%s: failed to create mapping: %u", __func__, *error);
			return;
		}

		sharedmemorybase_mappingresult_s *sm_result = NULL;
		sm_result = sharedmemorybase_segxnuaccess_createxnumapping__result_get_success(&result);
		assert3p(sm_result, !=, NULL);

		*mapping = sm_result->mappinginfo.mapping;
		assert3u(*mapping, !=, 0);
	});
	/* END IGNORE CODESTYLE */

	if (ret != TB_ERROR_SUCCESS || *mapping == 0) {
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}

/*
 * Currently unused as the setup process can provide an initial mapping.
 */
kern_return_t
exclaves_shared_memory_teardown(const sharedmemorybase_segxnuaccess_s *sm_client,
    const sharedmemorybase_mapping_s *mapping)
{
	assert3p(sm_client, !=, NULL);
	assert3p(mapping, !=, NULL);

	tb_error_t ret = TB_ERROR_SUCCESS;
	__block bool success = false;

	/* BEGIN IGNORE CODESTYLE */
	ret = sharedmemorybase_segxnuaccess_mappingdestroy(sm_client, *mapping,
	    ^(sharedmemorybase_segaccessbase_mappingdestroy__result_s result) {
		sharedmemorybase_accesserror_s *error;
		error = sharedmemorybase_segaccessbase_mappingdestroy__result_get_failure(&result);
		if (error != NULL) {
			exclaves_debug_printf(show_errors,
			    "%s: failed to destroy mapping: %u\n", __func__, *error);
			return;
		}

		assert(sharedmemorybase_segaccessbase_mappingdestroy__result_get_success(&result));
		success = true;
	});
	/* END IGNORE CODESTYLE */

	if (ret != TB_ERROR_SUCCESS || !success) {
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}

/*
 * Currently unused as the teardown process unmaps.
 */
kern_return_t
exclaves_shared_memory_map(const sharedmemorybase_segxnuaccess_s *sm_client,
    const sharedmemorybase_mapping_s *mapping, const uint64_t startpage,
    const uint64_t endpage)
{
	assert3p(sm_client, !=, NULL);
	assert3p(mapping, !=, NULL);
	assert3u(startpage, <, endpage);

	tb_error_t ret = TB_ERROR_SUCCESS;
	__block bool success = false;

	const sharedmemorybase_pagerange_s range = {
		.startpage = startpage,
		.endpage = endpage,
	};

	/* BEGIN IGNORE CODESTYLE */
	ret = sharedmemorybase_segxnuaccess_mappingmap(sm_client, *mapping,
	    &range, ^(sharedmemorybase_segaccessbase_mappingmap__result_s result) {
		sharedmemorybase_accesserror_s *error;
		error = sharedmemorybase_segaccessbase_mappingmap__result_get_failure(&result);
		if (error != NULL) {
			exclaves_debug_printf(show_errors,
			    "%s: failed to map: %u\n", __func__, *error);
			return;
		}

		assert(sharedmemorybase_segaccessbase_mappingmap__result_get_success(&result));
		success = true;
	});
	/* END IGNORE CODESTYLE */

	if (ret != TB_ERROR_SUCCESS || !success) {
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}


kern_return_t
exclaves_shared_memory_unmap(const sharedmemorybase_segxnuaccess_s *sm_client,
    const sharedmemorybase_mapping_s *mapping, const uint64_t startpage,
    const uint64_t endpage)
{
	assert3p(sm_client, !=, NULL);
	assert3p(mapping, !=, NULL);
	assert3u(startpage, <, endpage);

	tb_error_t ret = TB_ERROR_SUCCESS;
	__block bool success = false;

	const sharedmemorybase_pagerange_s range = {
		.startpage = startpage,
		.endpage = endpage,
	};

	/* BEGIN IGNORE CODESTYLE */
	ret = sharedmemorybase_segxnuaccess_mappingunmap(sm_client, *mapping,
	    &range, ^(sharedmemorybase_segaccessbase_mappingunmap__result_s result) {
		sharedmemorybase_accesserror_s *error;
		error = sharedmemorybase_segaccessbase_mappingunmap__result_get_failure(&result);
		if (error != NULL) {
			exclaves_debug_printf(show_errors, "%s: failed to unmap: %u\n",
			    __func__, *error);
			return;
		}

		assert(sharedmemorybase_segaccessbase_mappingunmap__result_get_success(&result));
		success = true;
	});
	/* END IGNORE CODESTYLE */

	if (ret != TB_ERROR_SUCCESS || !success) {
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}

kern_return_t
exclaves_shared_memory_iterate(const sharedmemorybase_segxnuaccess_s *sm_client,
    const sharedmemorybase_mapping_s *mapping, uint64_t startpage, uint64_t endpage,
    void (^cb)(uint64_t))
{
	assert3p(sm_client, !=, NULL);
	assert3p(mapping, !=, NULL);
	assert3u(startpage, <, endpage);

	tb_error_t ret = TB_ERROR_SUCCESS;
	__block bool success = false;

	sharedmemorybase_pagerange_s full_range = {
		.startpage = startpage,
		.endpage = endpage,
	};

	/* BEGIN IGNORE CODESTYLE */
	ret = sharedmemorybase_segxnuaccess_mappinggetphysicaladdresses(sm_client,
	    *mapping, &full_range,
	    ^(sharedmemorybase_segaccessbase_mappinggetphysicaladdresses__result_s result) {
		sharedmemorybase_accesserror_s *error = NULL;
		error = sharedmemorybase_segaccessbase_mappinggetphysicaladdresses__result_get_failure(&result);
		if (error != NULL) {
			exclaves_debug_printf(show_errors,
			    "%s: failed to get physical address: %u",
			    __func__, *error);
			return;
		}

		physicaladdress_v_s *phys_addr = NULL;
		phys_addr = sharedmemorybase_segaccessbase_mappinggetphysicaladdresses__result_get_success(&result);
		assert3p(phys_addr, !=, NULL);

		physicaladdress__v_visit(phys_addr,
		^(__unused size_t i, const sharedmemorybase_physicaladdress_s item) {
			cb(item);
		});

		success = true;
	});
	/* END IGNORE CODESTYLE */

	if (ret != TB_ERROR_SUCCESS || !success) {
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}

#endif /* CONFIG_EXCLAVES */