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(®[0], ®[1], ®[2], ®[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(®, 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, ®[0], ®[1], ®[2], ®[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