This is xnu-11215.1.10. See this file in:
/*
 * Copyright (c) 2020 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@
 */

#include <kern/assert.h>
#include <kern/hvg_hypercall.h>
#include <i386/x86_hypercall.h>
#include <kdp/ml/i386/kdp_x86_common.h>
#include <i386/cpuid.h>
#include <os/log.h>
#include <vm/pmap.h>
#include <vm/vm_map_xnu.h>
#include <x86_64/lowglobals.h>

bool
hvg_is_hcall_available(hvg_hcall_code_t hcall)
{
	assert3u(hcall, <, HVG_HCALL_COUNT);

	const uint64_t features = cpuid_vmm_get_applepv_features();
	const uint64_t hcall_feature[HVG_HCALL_COUNT] = {
		[HVG_HCALL_TRIGGER_DUMP]      = CPUID_LEAF_FEATURE_COREDUMP,
		[HVG_HCALL_SET_COREDUMP_DATA] = CPUID_LEAF_FEATURE_XNU_DEBUG,
		[HVG_HCALL_GET_MABS_OFFSET]   = CPUID_LEAF_FEATURE_MABS_OFFSET,
		[HVG_HCALL_GET_BOOTSESSIONUUID] = CPUID_LEAF_FEATURE_BOOTSESSIONUUID,
	};
	return cpuid_vmm_present() && (features & hcall_feature[hcall]) != 0;
}

/*
 * This routine issues an Apple hypercall that notifies the hypervisor to
 * take a guest kernel coredump. If the vmcore argument is not NULL, the
 * name tag of the vmcore file is copied into the caller's vmcore tag array.
 * Otherwise the name tag is ignored.
 */

hvg_hcall_return_t
hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t *vmcore,
    const hvg_hcall_dump_option_t dump_option)
{
	assert(hvg_is_hcall_available(HVG_HCALL_TRIGGER_DUMP));
	assert3u(dump_option, ==, HVG_HCALL_DUMP_OPTION_REGULAR); /* Only known option for now. */

	hvg_hcall_output_regs_t output = {};
	const hvg_hcall_return_t ret = hvg_hypercall1(HVG_HCALL_TRIGGER_DUMP,
	    dump_option,
	    &output);

	if (ret != HVG_HCALL_SUCCESS) {
		return ret;
	}

	if (vmcore) {
		/* Caller requested vmcore tag to be returned */
		static_assert(sizeof(vmcore->tag) > sizeof(output), "not enough room for tag");
		static_assert(sizeof(vmcore->tag[0] * sizeof(uint64_t)) == sizeof(output.rax), "mis-match of tag and output sizes");

		const size_t reg_size = sizeof(uint64_t);

		memcpy(&vmcore->tag[reg_size * 0], &output.rax, reg_size);
		memcpy(&vmcore->tag[reg_size * 1], &output.rdi, reg_size);
		memcpy(&vmcore->tag[reg_size * 2], &output.rsi, reg_size);
		memcpy(&vmcore->tag[reg_size * 3], &output.rdx, reg_size);
		memcpy(&vmcore->tag[reg_size * 4], &output.rcx, reg_size);
		memcpy(&vmcore->tag[reg_size * 5], &output.r8, reg_size);
		memcpy(&vmcore->tag[reg_size * 6], &output.r9, reg_size);
		vmcore->tag[reg_size * 7] = '\0';
	}

	return HVG_HCALL_SUCCESS;
}

extern vm_offset_t c_buffers;
extern vm_size_t   c_buffers_size;

/*
 * Inform the hypervisor of the kernel physical address of
 * the low globals data and kernel CR3 value.
 */
void
hvg_hcall_set_coredump_data(void)
{
	assert(hvg_is_hcall_available(HVG_HCALL_SET_COREDUMP_DATA));

	hvg_hcall_output_regs_t output = {};

	/* Hypercall to set up necessary information for reliable coredump */
	const hvg_hcall_return_t ret = hvg_hypercall6(HVG_HCALL_SET_COREDUMP_DATA,
	    lowGlo.lgStext,              /* args[0]: KVA of kernel text */
	    kernel_map->min_offset,      /* args[1]: KVA of kernel_map_start */
	    kernel_map->max_offset,      /* args[2]: KVA of kernel_map_end */
	    kernel_pmap->pm_cr3,         /* args[3]: Kernel CR3 */
	    c_buffers,                   /* args[4]: KVA of compressor buffers */
	    c_buffers_size,              /* args[5]: Size of compressor buffers */
	    &output);

	if (ret != HVG_HCALL_SUCCESS) {
		os_log_error(OS_LOG_DEFAULT, "%s: hcall failed, ret %d\n",
		    __func__, ret);
	}
}

hvg_hcall_return_t
hvg_hcall_get_mabs_offset(uint64_t *mabs_offset)
{
	assert(hvg_is_hcall_available(HVG_HCALL_GET_MABS_OFFSET));

	hvg_hcall_output_regs_t output = {};

	const hvg_hcall_return_t ret = hvg_hypercall2(HVG_HCALL_GET_MABS_OFFSET,
	    pal_rtc_nanotime_info.tsc_base, pal_rtc_nanotime_info.ns_base,
	    &output);
	if (ret != HVG_HCALL_SUCCESS) {
		return ret;
	}

	*mabs_offset = output.rdi;
	return HVG_HCALL_SUCCESS;
}

hvg_hcall_return_t
hvg_hcall_get_bootsessionuuid(uuid_string_t uuid)
{
	assert(hvg_is_hcall_available(HVG_HCALL_GET_BOOTSESSIONUUID));

	hvg_hcall_output_regs_t output = {};

	const hvg_hcall_return_t ret = hvg_hypercall0(
		HVG_HCALL_GET_BOOTSESSIONUUID, &output);
	if (ret != HVG_HCALL_SUCCESS) {
		return ret;
	}

	static_assert(sizeof(uuid_string_t) == 37,
	    "unexpected uuid string length");

	memset(uuid, 0, sizeof(uuid_string_t));

	memcpy(&uuid[0], &output.rax, 8);
	memcpy(&uuid[8], &output.rdi, 8);
	memcpy(&uuid[16], &output.rsi, 8);
	memcpy(&uuid[24], &output.rdx, 8);
	memcpy(&uuid[32], &output.rcx, 4);

	return HVG_HCALL_SUCCESS;
}