xref: /xnu-11417.121.6/osfmk/i386/x86_hypercall.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1 /*
2  * Copyright (c) 2020 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 #include <kern/assert.h>
30 #include <kern/hvg_hypercall.h>
31 #include <i386/x86_hypercall.h>
32 #include <kdp/ml/i386/kdp_x86_common.h>
33 #include <i386/cpuid.h>
34 #include <os/log.h>
35 #include <vm/pmap.h>
36 #include <vm/vm_map_xnu.h>
37 #include <x86_64/lowglobals.h>
38 
39 bool
hvg_is_hcall_available(hvg_hcall_code_t hcall)40 hvg_is_hcall_available(hvg_hcall_code_t hcall)
41 {
42 	assert3u(hcall, <, HVG_HCALL_COUNT);
43 
44 	const uint64_t features = cpuid_vmm_get_applepv_features();
45 	const uint64_t hcall_feature[HVG_HCALL_COUNT] = {
46 		[HVG_HCALL_TRIGGER_DUMP]      = CPUID_LEAF_FEATURE_COREDUMP,
47 		[HVG_HCALL_SET_COREDUMP_DATA] = CPUID_LEAF_FEATURE_XNU_DEBUG,
48 		[HVG_HCALL_GET_MABS_OFFSET]   = CPUID_LEAF_FEATURE_MABS_OFFSET,
49 		[HVG_HCALL_GET_BOOTSESSIONUUID] = CPUID_LEAF_FEATURE_BOOTSESSIONUUID,
50 	};
51 	return cpuid_vmm_present() && (features & hcall_feature[hcall]) != 0;
52 }
53 
54 /*
55  * This routine issues an Apple hypercall that notifies the hypervisor to
56  * take a guest kernel coredump. If the vmcore argument is not NULL, the
57  * name tag of the vmcore file is copied into the caller's vmcore tag array.
58  * Otherwise the name tag is ignored.
59  */
60 
61 hvg_hcall_return_t
hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t * vmcore,const hvg_hcall_dump_option_t dump_option)62 hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t *vmcore,
63     const hvg_hcall_dump_option_t dump_option)
64 {
65 	assert(hvg_is_hcall_available(HVG_HCALL_TRIGGER_DUMP));
66 	assert3u(dump_option, ==, HVG_HCALL_DUMP_OPTION_REGULAR); /* Only known option for now. */
67 
68 	hvg_hcall_output_regs_t output = {};
69 	const hvg_hcall_return_t ret = hvg_hypercall1(HVG_HCALL_TRIGGER_DUMP,
70 	    dump_option,
71 	    &output);
72 
73 	if (ret != HVG_HCALL_SUCCESS) {
74 		return ret;
75 	}
76 
77 	if (vmcore) {
78 		/* Caller requested vmcore tag to be returned */
79 		static_assert(sizeof(vmcore->tag) > sizeof(output), "not enough room for tag");
80 		static_assert(sizeof(vmcore->tag[0] * sizeof(uint64_t)) == sizeof(output.rax), "mis-match of tag and output sizes");
81 
82 		const size_t reg_size = sizeof(uint64_t);
83 
84 		memcpy(&vmcore->tag[reg_size * 0], &output.rax, reg_size);
85 		memcpy(&vmcore->tag[reg_size * 1], &output.rdi, reg_size);
86 		memcpy(&vmcore->tag[reg_size * 2], &output.rsi, reg_size);
87 		memcpy(&vmcore->tag[reg_size * 3], &output.rdx, reg_size);
88 		memcpy(&vmcore->tag[reg_size * 4], &output.rcx, reg_size);
89 		memcpy(&vmcore->tag[reg_size * 5], &output.r8, reg_size);
90 		memcpy(&vmcore->tag[reg_size * 6], &output.r9, reg_size);
91 		vmcore->tag[reg_size * 7] = '\0';
92 	}
93 
94 	return HVG_HCALL_SUCCESS;
95 }
96 
97 extern vm_offset_t c_buffers;
98 extern vm_size_t   c_buffers_size;
99 
100 /*
101  * Inform the hypervisor of the kernel physical address of
102  * the low globals data and kernel CR3 value.
103  */
104 void
hvg_hcall_set_coredump_data(void)105 hvg_hcall_set_coredump_data(void)
106 {
107 	assert(hvg_is_hcall_available(HVG_HCALL_SET_COREDUMP_DATA));
108 
109 	hvg_hcall_output_regs_t output = {};
110 
111 	/* Hypercall to set up necessary information for reliable coredump */
112 	const hvg_hcall_return_t ret = hvg_hypercall6(HVG_HCALL_SET_COREDUMP_DATA,
113 	    lowGlo.lgStext,              /* args[0]: KVA of kernel text */
114 	    kernel_map->min_offset,      /* args[1]: KVA of kernel_map_start */
115 	    kernel_map->max_offset,      /* args[2]: KVA of kernel_map_end */
116 	    kernel_pmap->pm_cr3,         /* args[3]: Kernel CR3 */
117 	    c_buffers,                   /* args[4]: KVA of compressor buffers */
118 	    c_buffers_size,              /* args[5]: Size of compressor buffers */
119 	    &output);
120 
121 	if (ret != HVG_HCALL_SUCCESS) {
122 		os_log_error(OS_LOG_DEFAULT, "%s: hcall failed, ret %d\n",
123 		    __func__, ret);
124 	}
125 }
126 
127 hvg_hcall_return_t
hvg_hcall_get_mabs_offset(uint64_t * mabs_offset)128 hvg_hcall_get_mabs_offset(uint64_t *mabs_offset)
129 {
130 	assert(hvg_is_hcall_available(HVG_HCALL_GET_MABS_OFFSET));
131 
132 	hvg_hcall_output_regs_t output = {};
133 
134 	const hvg_hcall_return_t ret = hvg_hypercall2(HVG_HCALL_GET_MABS_OFFSET,
135 	    pal_rtc_nanotime_info.tsc_base, pal_rtc_nanotime_info.ns_base,
136 	    &output);
137 	if (ret != HVG_HCALL_SUCCESS) {
138 		return ret;
139 	}
140 
141 	*mabs_offset = output.rdi;
142 	return HVG_HCALL_SUCCESS;
143 }
144 
145 hvg_hcall_return_t
hvg_hcall_get_bootsessionuuid(uuid_string_t uuid)146 hvg_hcall_get_bootsessionuuid(uuid_string_t uuid)
147 {
148 	assert(hvg_is_hcall_available(HVG_HCALL_GET_BOOTSESSIONUUID));
149 
150 	hvg_hcall_output_regs_t output = {};
151 
152 	const hvg_hcall_return_t ret = hvg_hypercall0(
153 		HVG_HCALL_GET_BOOTSESSIONUUID, &output);
154 	if (ret != HVG_HCALL_SUCCESS) {
155 		return ret;
156 	}
157 
158 	static_assert(sizeof(uuid_string_t) == 37,
159 	    "unexpected uuid string length");
160 
161 	memset(uuid, 0, sizeof(uuid_string_t));
162 
163 	memcpy(&uuid[0], &output.rax, 8);
164 	memcpy(&uuid[8], &output.rdi, 8);
165 	memcpy(&uuid[16], &output.rsi, 8);
166 	memcpy(&uuid[24], &output.rdx, 8);
167 	memcpy(&uuid[32], &output.rcx, 4);
168 
169 	return HVG_HCALL_SUCCESS;
170 }
171