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 |
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(uu: uuid, out: uuid_str); |
50 | #undef countof |
51 | } |
52 | |
53 | static inline bool |
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 |
84 | hvc_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 | |
90 | static inline bool |
91 | hvc_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 | |
97 | static inline bool |
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 |
124 | hvc32_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 */ |
131 | static bool |
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(w0: ®[0], w1: ®[1], w2: ®[2], w3: ®[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: ®, uuid_str: uuid); |
148 | return true; |
149 | } |
150 | |
151 | /* Revision information */ |
152 | static bool |
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(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 | */ |
168 | static bool |
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(w0: (uint32_t *)features, w1: &fid); |
175 | } |
176 | |
177 | bool |
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: &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 | |
236 | hvg_hcall_return_t |
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(x0: &fn, x1: ®[0], x2: ®[1], x3: ®[2], x4: ®[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 | |
254 | hvg_hcall_return_t |
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(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)) |
270 | void |
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: &x0, x1: &x1); |
280 | assert(success); |
281 | } |
282 | |
283 | __attribute__((noinline)) |
284 | void |
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: &x0, x1: &x1); |
290 | assert(success); |
291 | } |
292 | |
293 | #else /* APPLEVIRTUALPLATFORM */ |
294 | |
295 | bool |
296 | hvg_is_hcall_available(__unused hvg_hcall_code_t hcall) |
297 | { |
298 | return false; |
299 | } |
300 | |
301 | hvg_hcall_return_t |
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 |
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 |
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 |
324 | hvg_hcall_set_coredump_data(void) |
325 | { |
326 | } |
327 | |