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
37static inline void
38regs_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(uu: uuid, out: uuid_str);
50#undef countof
51}
52
53static inline bool
54hvc_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
83static inline bool
84hvc_2(uint64_t *x0, uint64_t *x1)
85{
86 uint64_t x = 0;
87 return hvc_5(x0, x1, x2: &x, x3: &x, x4: &x);
88}
89
90static inline bool
91hvc_1(uint64_t *x0)
92{
93 uint64_t x = 0;
94 return hvc_5(x0, x1: &x, x2: &x, x3: &x, x4: &x);
95}
96
97static inline bool
98hvc32_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
123static inline bool
124hvc32_2(uint32_t *w0, uint32_t *w1)
125{
126 uint32_t w = 0;
127 return hvc32_4(w0, w1, w2: &w, w3: &w);
128}
129
130/* Unique identification */
131static bool
132hvg_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(w0: &reg[0], w1: &reg[1], w2: &reg[2], w3: &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(regs: &reg, uuid_str: uuid);
148 return true;
149}
150
151/* Revision information */
152static bool
153hvg_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(w0: major, w1: 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 */
168static bool
169hvg_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(w0: (uint32_t *)features, w1: &fid);
175}
176
177bool
178hvg_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: &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, uuid: uuids) ||
219 strcmp(s1: 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: &major, minor: &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, fid: fastcall, features: &features);
234}
235
236hvg_hcall_return_t
237hvg_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(x0: &fn, x1: &reg[0], x2: &reg[1], x3: &reg[2], x4: &reg[3])) {
243 return HVG_HCALL_UNSUPPORTED;
244 }
245
246 regs_to_uuid(regs: &(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
254hvg_hcall_return_t
255hvg_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(x0: &fn, x1: &x1)) {
261 return HVG_HCALL_UNSUPPORTED;
262 }
263 *mabs_offset = x1;
264
265 return HVG_HCALL_SUCCESS;
266}
267
268
269__attribute__((noinline))
270void
271hvg_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: &x0, x1: &x1);
280 assert(success);
281}
282
283__attribute__((noinline))
284void
285hvg_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: &x0, x1: &x1);
290 assert(success);
291}
292
293#else /* APPLEVIRTUALPLATFORM */
294
295bool
296hvg_is_hcall_available(__unused hvg_hcall_code_t hcall)
297{
298 return false;
299}
300
301hvg_hcall_return_t
302hvg_hcall_get_mabs_offset(__attribute__((unused)) uint64_t *mabs_offset)
303{
304 return HVG_HCALL_UNSUPPORTED;
305}
306
307hvg_hcall_return_t
308hvg_hcall_get_bootsessionuuid(__attribute__((unused)) uuid_string_t uuid)
309{
310 return HVG_HCALL_UNSUPPORTED;
311}
312
313#endif /* APPLEVIRTUALPLATFORM */
314
315hvg_hcall_return_t
316hvg_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. */
323void
324hvg_hcall_set_coredump_data(void)
325{
326}
327