xref: /xnu-11417.140.69/osfmk/arm64/arm64_hypercall.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
1 /*
2  * Copyright (c) 2021 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/hvg_hypercall.h>
30 #include <pexpert/pexpert.h>
31 #include <arm/machine_routines.h>
32 
33 #include "hv_hvc.h"
34 
35 #if APPLEVIRTUALPLATFORM
36 
37 static inline void
regs_to_uuid(const uint32_t (* const regs)[4],uuid_string_t uuid_str)38 regs_to_uuid(const uint32_t (*const regs)[4], uuid_string_t uuid_str)
39 {
40 #define countof(array)  (sizeof (array) / sizeof ((array)[0]))
41 
42 	uuid_t uuid = {};
43 
44 	for (int i = 0; i < countof(*regs); i++) {
45 		for (int j = 0; j < 4; j++) {
46 			uuid[15 - (i * 4) - j] = ((*regs)[i] >> (j * 8)) & 0xff;
47 		}
48 	}
49 	uuid_unparse(uuid, uuid_str);
50 #undef countof
51 }
52 
53 static inline bool
hvc_5(uint64_t * x0,uint64_t * x1,uint64_t * x2,uint64_t * x3,uint64_t * x4)54 hvc_5(uint64_t *x0, uint64_t *x1, uint64_t *x2, uint64_t *x3, uint64_t *x4)
55 {
56 	asm volatile (
57                 "mov   x0, %[i0]"    "\n"
58                 "mov   x1, %[i1]"    "\n"
59                 "mov   x2, %[i2]"    "\n"
60                 "mov   x3, %[i3]"    "\n"
61                 "mov   x4, %[i4]"    "\n"
62                 "hvc   #0"           "\n"
63                 "str   x0, %[o0]"    "\n"
64                 "str   x1, %[o1]"    "\n"
65                 "str   x2, %[o2]"    "\n"
66                 "str   x3, %[o3]"    "\n"
67                 "str   x4, %[o4]"    "\n"
68                 : [o0]  "=m" (*x0),
69                   [o1]  "=m" (*x1),
70                   [o2]  "=m" (*x2),
71                   [o3]  "=m" (*x3),
72                   [o4]  "=m" (*x4)
73                 : [i0]   "r" (*x0),
74                   [i1]   "r" (*x1),
75                   [i2]   "r" (*x2),
76                   [i3]   "r" (*x3),
77                   [i4]   "r" (*x4)
78                 : "x0", "x1", "x2", "x3", "x4"
79         );
80 	return *(int64_t *)x0 >= 0;
81 }
82 
83 static inline bool
hvc_2(uint64_t * x0,uint64_t * x1)84 hvc_2(uint64_t *x0, uint64_t *x1)
85 {
86 	uint64_t x = 0;
87 	return hvc_5(x0, x1, &x, &x, &x);
88 }
89 
90 static inline bool
hvc_1(uint64_t * x0)91 hvc_1(uint64_t *x0)
92 {
93 	uint64_t x = 0;
94 	return hvc_5(x0, &x, &x, &x, &x);
95 }
96 
97 static inline bool
hvc32_4(uint32_t * w0,uint32_t * w1,uint32_t * w2,uint32_t * w3)98 hvc32_4(uint32_t *w0, uint32_t *w1, uint32_t *w2, uint32_t *w3)
99 {
100 	asm volatile (
101                 "mov   w0, %w[i0]"   "\n"
102                 "mov   w1, %w[i1]"   "\n"
103                 "mov   w2, %w[i2]"   "\n"
104                 "mov   w3, %w[i3]"   "\n"
105                 "hvc   #0"           "\n"
106                 "str   w0, %[o0]"    "\n"
107                 "str   w1, %[o1]"    "\n"
108                 "str   w2, %[o2]"    "\n"
109                 "str   w3, %[o3]"    "\n"
110                 : [o0]  "=m" (*w0),
111                   [o1]  "=m" (*w1),
112                   [o2]  "=m" (*w2),
113                   [o3]  "=m" (*w3)
114                 : [i0]   "r" (*w0),
115                   [i1]   "r" (*w1),
116                   [i2]   "r" (*w2),
117                   [i3]   "r" (*w3)
118                 : "w0", "w1", "w2", "w3"
119         );
120 	return *(int32_t *)w0 >= 0;
121 }
122 
123 static inline bool
hvc32_2(uint32_t * w0,uint32_t * w1)124 hvc32_2(uint32_t *w0, uint32_t *w1)
125 {
126 	uint32_t w = 0;
127 	return hvc32_4(w0, w1, &w, &w);
128 }
129 
130 /* Unique identification */
131 static bool
hvg_get_uid(uint32_t range,uuid_string_t uuid)132 hvg_get_uid(uint32_t range, uuid_string_t uuid)
133 {
134 	assert(range == HVC_FID_CPU || range == HVC_FID_OEM);
135 
136 	uint32_t reg[4] = {
137 		[0] = HVC32_FI(range, HVC_FID_UID)
138 	};
139 	(void) hvc32_4(&reg[0], &reg[1], &reg[2], &reg[3]);
140 	if (reg[0] == 0xffffffffu) {
141 		/*
142 		 * The only illegal %x0 value for a UID is 0xffffffff,
143 		 * thus cannot rely on "%x0 < 0 => failure" here.
144 		 */
145 		return false;
146 	}
147 	regs_to_uuid(&reg, uuid);
148 	return true;
149 }
150 
151 /* Revision information */
152 static bool
hvg_get_revision(uint32_t range,uint32_t * major,uint32_t * minor)153 hvg_get_revision(uint32_t range, uint32_t *major, uint32_t *minor)
154 {
155 	assert(range == HVC_FID_CPU || range == HVC_FID_OEM);
156 
157 	*major = HVC32_FI(range, HVC_FID_REVISION);
158 	return hvc32_2(major, minor);
159 }
160 
161 /*
162  * Hypercall feature information
163  *
164  * Similar semantics to SMCCC_ARCH_FEATURES i.e. return < 0 if not
165  * supported, with the negative value potentially providing more info.
166  * Returns >= 0 if supported, with the value indicating feature flags.
167  */
168 static bool
hvg_get_features(uint32_t range,uint32_t fid,int32_t * features)169 hvg_get_features(uint32_t range, uint32_t fid, int32_t *features)
170 {
171 	assert(range == HVC_FID_CPU || range == HVC_FID_OEM);
172 
173 	*features = HVC32_FI(range, HVC_FID_FEATURES);
174 	return hvc32_2((uint32_t *)features, &fid);
175 }
176 
177 bool
hvg_is_hcall_available(hvg_hcall_code_t hcall)178 hvg_is_hcall_available(hvg_hcall_code_t hcall)
179 {
180 	int32_t features = 0;
181 	uint32_t major = 0, minor = 0;
182 	uuid_string_t uuids = {};
183 
184 	/*
185 	 * This is a workaround for older hosts which exited when unknown
186 	 * hypercalls (including those querying the UID and revision)
187 	 * were issued.
188 	 * The PAC_NOP call was added in Sydro along with the OEM UID,
189 	 * REVISION and FEATURES calls; before then, this hypercall was
190 	 * accepted by Hypervisor framework code, but (despite being a
191 	 * 64-bit hypercall) returned 0xffffffff for unknown values.
192 	 */
193 	uint64_t x0 = VMAPPLE_PAC_NOP;
194 	(void) hvc_1(&x0);
195 
196 	if (x0 == 0xffffffff || *(int64_t *)&x0 < 0) {
197 		return false; // Pre-Sydro host or unknown hypercall
198 	}
199 
200 	static const uint32_t hcall_oem_table[HVG_HCALL_COUNT] = {
201 		[HVG_HCALL_GET_MABS_OFFSET]     = VMAPPLE_GET_MABS_OFFSET,
202 		[HVG_HCALL_GET_BOOTSESSIONUUID] = VMAPPLE_GET_BOOTSESSIONUUID,
203 		[HVG_HCALL_VCPU_WFK]            = VMAPPLE_VCPU_WFK,
204 		[HVG_HCALL_VCPU_KICK]           = VMAPPLE_VCPU_KICK,
205 	};
206 
207 	uint32_t fastcall = 0;
208 	if (hcall >= HVG_HCALL_COUNT ||
209 	    (fastcall = hcall_oem_table[hcall]) == 0) {
210 		return false;
211 	}
212 	assert(fastcall & HVC_OEM_SERVICE);
213 
214 	/*
215 	 * Verify that the host OEM hypercalls are implemented as
216 	 * specified by Apple.
217 	 */
218 	if (!hvg_get_uid(HVC_FID_OEM, uuids) ||
219 	    strcmp(uuids, VMAPPLE_HVC_UID) != 0) {
220 		return false;
221 	}
222 
223 	/*
224 	 * Verify that the host implements the OEM "features" hypercall
225 	 */
226 	if (!hvg_get_revision(HVC_FID_OEM, &major, &minor) && major == 1) {
227 		return false;
228 	}
229 
230 	/*
231 	 * Does the host support this OEM hypercall?
232 	 */
233 	return hvg_get_features(HVC_FID_OEM, fastcall, &features);
234 }
235 
236 hvg_hcall_return_t
hvg_hcall_get_bootsessionuuid(uuid_string_t uuid_str)237 hvg_hcall_get_bootsessionuuid(uuid_string_t uuid_str)
238 {
239 	uint64_t fn = VMAPPLE_GET_BOOTSESSIONUUID;
240 	uint64_t reg[5] = {};
241 
242 	if (!hvc_5(&fn, &reg[0], &reg[1], &reg[2], &reg[3])) {
243 		return HVG_HCALL_UNSUPPORTED;
244 	}
245 
246 	regs_to_uuid(&(uint32_t[4])
247 	    {(uint32_t)reg[0], (uint32_t)reg[1],
248 	     (uint32_t)reg[2], (uint32_t)reg[3]},
249 	    uuid_str);
250 
251 	return HVG_HCALL_SUCCESS;
252 }
253 
254 hvg_hcall_return_t
hvg_hcall_get_mabs_offset(uint64_t * mabs_offset)255 hvg_hcall_get_mabs_offset(uint64_t *mabs_offset)
256 {
257 	uint64_t fn = VMAPPLE_GET_MABS_OFFSET;
258 	uint64_t x1 = ml_get_abstime_offset();
259 
260 	if (!hvc_2(&fn, &x1)) {
261 		return HVG_HCALL_UNSUPPORTED;
262 	}
263 	*mabs_offset = x1;
264 
265 	return HVG_HCALL_SUCCESS;
266 }
267 
268 
269 __attribute__((noinline))
270 void
hvg_hc_kick_cpu(unsigned int cpu_id)271 hvg_hc_kick_cpu(unsigned int cpu_id)
272 {
273 	const ml_topology_info_t *tip = ml_get_topology_info();
274 	assert(cpu_id < tip->num_cpus);
275 
276 	const uint32_t phys_id = tip->cpus[cpu_id].phys_id;
277 	uint64_t x0 = VMAPPLE_VCPU_KICK;
278 	uint64_t x1 = phys_id;
279 	__assert_only const bool success = hvc_2(&x0, &x1);
280 	assert(success);
281 }
282 
283 __attribute__((noinline))
284 void
hvg_hc_wait_for_kick(unsigned int ien)285 hvg_hc_wait_for_kick(unsigned int ien)
286 {
287 	uint64_t x0 = VMAPPLE_VCPU_WFK;
288 	uint64_t x1 = ien;
289 	__assert_only const bool success = hvc_2(&x0, &x1);
290 	assert(success);
291 }
292 
293 #else /* APPLEVIRTUALPLATFORM */
294 
295 bool
hvg_is_hcall_available(__unused hvg_hcall_code_t hcall)296 hvg_is_hcall_available(__unused hvg_hcall_code_t hcall)
297 {
298 	return false;
299 }
300 
301 hvg_hcall_return_t
hvg_hcall_get_mabs_offset(uint64_t * mabs_offset)302 hvg_hcall_get_mabs_offset(__attribute__((unused)) uint64_t *mabs_offset)
303 {
304 	return HVG_HCALL_UNSUPPORTED;
305 }
306 
307 hvg_hcall_return_t
hvg_hcall_get_bootsessionuuid(uuid_string_t uuid)308 hvg_hcall_get_bootsessionuuid(__attribute__((unused)) uuid_string_t uuid)
309 {
310 	return HVG_HCALL_UNSUPPORTED;
311 }
312 
313 #endif /* APPLEVIRTUALPLATFORM */
314 
315 hvg_hcall_return_t
hvg_hcall_trigger_dump(__unused hvg_hcall_vmcore_file_t * vmcore,__unused const hvg_hcall_dump_option_t dump_option)316 hvg_hcall_trigger_dump(__unused hvg_hcall_vmcore_file_t *vmcore,
317     __unused const hvg_hcall_dump_option_t dump_option)
318 {
319 	return HVG_HCALL_UNSUPPORTED;
320 }
321 
322 /* Unsupported. */
323 void
hvg_hcall_set_coredump_data(void)324 hvg_hcall_set_coredump_data(void)
325 {
326 }
327