master
c 1,002 lines 36.1 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * QEMU Windows Hypervisor Platform accelerator (WHPX)
4 *
5 * Copyright (c) 2025 Mohamed Mediouni
6 *
7 */
8
9 #include "qemu/osdep.h"
10 #include "cpu.h"
11 #include "system/address-spaces.h"
12 #include "system/ioport.h"
13 #include "gdbstub/helpers.h"
14 #include "qemu/accel.h"
15 #include "accel/accel-ops.h"
16 #include "system/whpx.h"
17 #include "system/cpus.h"
18 #include "system/runstate.h"
19 #include "qemu/main-loop.h"
20 #include "hw/core/boards.h"
21 #include "qemu/error-report.h"
22 #include "qapi/error.h"
23 #include "qapi/qapi-types-common.h"
24 #include "qapi/qapi-visit-common.h"
25 #include "migration/blocker.h"
26 #include "accel/accel-cpu-target.h"
27 #include <winerror.h>
28
29 #include "syndrome.h"
30 #include "target/arm/cpregs.h"
31 #include "internals.h"
32
33 #include "system/whpx-internal.h"
34 #include "system/whpx-accel-ops.h"
35 #include "system/whpx-all.h"
36 #include "system/whpx-common.h"
37 #include "whpx_arm.h"
38 #include "hw/arm/bsa.h"
39 #include "arm-powerctl.h"
40
41 #include <winhvplatform.h>
42 #include <winhvplatformdefs.h>
43 #include <winreg.h>
44
45 typedef struct ARMHostCPUFeatures {
46 ARMISARegisters isar;
47 uint64_t features;
48 uint64_t midr;
49 uint32_t reset_sctlr;
50 const char *dtb_compatible;
51 } ARMHostCPUFeatures;
52
53 static ARMHostCPUFeatures arm_host_cpu_features;
54
55 typedef struct WHPXRegMatch {
56 WHV_REGISTER_NAME reg;
57 uint64_t offset;
58 } WHPXRegMatch;
59
60 static const WHPXRegMatch whpx_reg_match[] = {
61 { WHvArm64RegisterX0, offsetof(CPUARMState, xregs[0]) },
62 { WHvArm64RegisterX1, offsetof(CPUARMState, xregs[1]) },
63 { WHvArm64RegisterX2, offsetof(CPUARMState, xregs[2]) },
64 { WHvArm64RegisterX3, offsetof(CPUARMState, xregs[3]) },
65 { WHvArm64RegisterX4, offsetof(CPUARMState, xregs[4]) },
66 { WHvArm64RegisterX5, offsetof(CPUARMState, xregs[5]) },
67 { WHvArm64RegisterX6, offsetof(CPUARMState, xregs[6]) },
68 { WHvArm64RegisterX7, offsetof(CPUARMState, xregs[7]) },
69 { WHvArm64RegisterX8, offsetof(CPUARMState, xregs[8]) },
70 { WHvArm64RegisterX9, offsetof(CPUARMState, xregs[9]) },
71 { WHvArm64RegisterX10, offsetof(CPUARMState, xregs[10]) },
72 { WHvArm64RegisterX11, offsetof(CPUARMState, xregs[11]) },
73 { WHvArm64RegisterX12, offsetof(CPUARMState, xregs[12]) },
74 { WHvArm64RegisterX13, offsetof(CPUARMState, xregs[13]) },
75 { WHvArm64RegisterX14, offsetof(CPUARMState, xregs[14]) },
76 { WHvArm64RegisterX15, offsetof(CPUARMState, xregs[15]) },
77 { WHvArm64RegisterX16, offsetof(CPUARMState, xregs[16]) },
78 { WHvArm64RegisterX17, offsetof(CPUARMState, xregs[17]) },
79 { WHvArm64RegisterX18, offsetof(CPUARMState, xregs[18]) },
80 { WHvArm64RegisterX19, offsetof(CPUARMState, xregs[19]) },
81 { WHvArm64RegisterX20, offsetof(CPUARMState, xregs[20]) },
82 { WHvArm64RegisterX21, offsetof(CPUARMState, xregs[21]) },
83 { WHvArm64RegisterX22, offsetof(CPUARMState, xregs[22]) },
84 { WHvArm64RegisterX23, offsetof(CPUARMState, xregs[23]) },
85 { WHvArm64RegisterX24, offsetof(CPUARMState, xregs[24]) },
86 { WHvArm64RegisterX25, offsetof(CPUARMState, xregs[25]) },
87 { WHvArm64RegisterX26, offsetof(CPUARMState, xregs[26]) },
88 { WHvArm64RegisterX27, offsetof(CPUARMState, xregs[27]) },
89 { WHvArm64RegisterX28, offsetof(CPUARMState, xregs[28]) },
90 { WHvArm64RegisterFp, offsetof(CPUARMState, xregs[29]) },
91 { WHvArm64RegisterLr, offsetof(CPUARMState, xregs[30]) },
92 { WHvArm64RegisterPc, offsetof(CPUARMState, pc) },
93 };
94
95 static const WHPXRegMatch whpx_fpreg_match[] = {
96 { WHvArm64RegisterQ0, offsetof(CPUARMState, vfp.zregs[0]) },
97 { WHvArm64RegisterQ1, offsetof(CPUARMState, vfp.zregs[1]) },
98 { WHvArm64RegisterQ2, offsetof(CPUARMState, vfp.zregs[2]) },
99 { WHvArm64RegisterQ3, offsetof(CPUARMState, vfp.zregs[3]) },
100 { WHvArm64RegisterQ4, offsetof(CPUARMState, vfp.zregs[4]) },
101 { WHvArm64RegisterQ5, offsetof(CPUARMState, vfp.zregs[5]) },
102 { WHvArm64RegisterQ6, offsetof(CPUARMState, vfp.zregs[6]) },
103 { WHvArm64RegisterQ7, offsetof(CPUARMState, vfp.zregs[7]) },
104 { WHvArm64RegisterQ8, offsetof(CPUARMState, vfp.zregs[8]) },
105 { WHvArm64RegisterQ9, offsetof(CPUARMState, vfp.zregs[9]) },
106 { WHvArm64RegisterQ10, offsetof(CPUARMState, vfp.zregs[10]) },
107 { WHvArm64RegisterQ11, offsetof(CPUARMState, vfp.zregs[11]) },
108 { WHvArm64RegisterQ12, offsetof(CPUARMState, vfp.zregs[12]) },
109 { WHvArm64RegisterQ13, offsetof(CPUARMState, vfp.zregs[13]) },
110 { WHvArm64RegisterQ14, offsetof(CPUARMState, vfp.zregs[14]) },
111 { WHvArm64RegisterQ15, offsetof(CPUARMState, vfp.zregs[15]) },
112 { WHvArm64RegisterQ16, offsetof(CPUARMState, vfp.zregs[16]) },
113 { WHvArm64RegisterQ17, offsetof(CPUARMState, vfp.zregs[17]) },
114 { WHvArm64RegisterQ18, offsetof(CPUARMState, vfp.zregs[18]) },
115 { WHvArm64RegisterQ19, offsetof(CPUARMState, vfp.zregs[19]) },
116 { WHvArm64RegisterQ20, offsetof(CPUARMState, vfp.zregs[20]) },
117 { WHvArm64RegisterQ21, offsetof(CPUARMState, vfp.zregs[21]) },
118 { WHvArm64RegisterQ22, offsetof(CPUARMState, vfp.zregs[22]) },
119 { WHvArm64RegisterQ23, offsetof(CPUARMState, vfp.zregs[23]) },
120 { WHvArm64RegisterQ24, offsetof(CPUARMState, vfp.zregs[24]) },
121 { WHvArm64RegisterQ25, offsetof(CPUARMState, vfp.zregs[25]) },
122 { WHvArm64RegisterQ26, offsetof(CPUARMState, vfp.zregs[26]) },
123 { WHvArm64RegisterQ27, offsetof(CPUARMState, vfp.zregs[27]) },
124 { WHvArm64RegisterQ28, offsetof(CPUARMState, vfp.zregs[28]) },
125 { WHvArm64RegisterQ29, offsetof(CPUARMState, vfp.zregs[29]) },
126 { WHvArm64RegisterQ30, offsetof(CPUARMState, vfp.zregs[30]) },
127 { WHvArm64RegisterQ31, offsetof(CPUARMState, vfp.zregs[31]) },
128 };
129
130 struct whpx_sreg_match {
131 WHV_REGISTER_NAME reg;
132 uint32_t key;
133 bool global;
134 uint32_t cp_idx;
135 };
136
137 static struct whpx_sreg_match whpx_sreg_match[] = {
138 { WHvArm64RegisterDbgbvr0El1, ENCODE_AA64_CP_REG(0, 0, 2, 0, 4) },
139 { WHvArm64RegisterDbgbcr0El1, ENCODE_AA64_CP_REG(0, 0, 2, 0, 5) },
140 { WHvArm64RegisterDbgwvr0El1, ENCODE_AA64_CP_REG(0, 0, 2, 0, 6) },
141 { WHvArm64RegisterDbgwcr0El1, ENCODE_AA64_CP_REG(0, 0, 2, 0, 7) },
142
143 { WHvArm64RegisterDbgbvr0El1, ENCODE_AA64_CP_REG(0, 1, 2, 0, 4) },
144 { WHvArm64RegisterDbgbcr0El1, ENCODE_AA64_CP_REG(0, 1, 2, 0, 5) },
145 { WHvArm64RegisterDbgwvr0El1, ENCODE_AA64_CP_REG(0, 1, 2, 0, 6) },
146 { WHvArm64RegisterDbgwcr0El1, ENCODE_AA64_CP_REG(0, 1, 2, 0, 7) },
147
148 { WHvArm64RegisterDbgbvr2El1, ENCODE_AA64_CP_REG(0, 2, 2, 0, 4) },
149 { WHvArm64RegisterDbgbcr2El1, ENCODE_AA64_CP_REG(0, 2, 2, 0, 5) },
150 { WHvArm64RegisterDbgwvr2El1, ENCODE_AA64_CP_REG(0, 2, 2, 0, 6) },
151 { WHvArm64RegisterDbgwcr2El1, ENCODE_AA64_CP_REG(0, 2, 2, 0, 7) },
152
153 { WHvArm64RegisterDbgbvr3El1, ENCODE_AA64_CP_REG(0, 3, 2, 0, 4) },
154 { WHvArm64RegisterDbgbcr3El1, ENCODE_AA64_CP_REG(0, 3, 2, 0, 5) },
155 { WHvArm64RegisterDbgwvr3El1, ENCODE_AA64_CP_REG(0, 3, 2, 0, 6) },
156 { WHvArm64RegisterDbgwcr3El1, ENCODE_AA64_CP_REG(0, 3, 2, 0, 7) },
157
158 { WHvArm64RegisterDbgbvr4El1, ENCODE_AA64_CP_REG(0, 4, 2, 0, 4) },
159 { WHvArm64RegisterDbgbcr4El1, ENCODE_AA64_CP_REG(0, 4, 2, 0, 5) },
160 { WHvArm64RegisterDbgwvr4El1, ENCODE_AA64_CP_REG(0, 4, 2, 0, 6) },
161 { WHvArm64RegisterDbgwcr4El1, ENCODE_AA64_CP_REG(0, 4, 2, 0, 7) },
162
163 { WHvArm64RegisterDbgbvr5El1, ENCODE_AA64_CP_REG(0, 5, 2, 0, 4) },
164 { WHvArm64RegisterDbgbcr5El1, ENCODE_AA64_CP_REG(0, 5, 2, 0, 5) },
165 { WHvArm64RegisterDbgwvr5El1, ENCODE_AA64_CP_REG(0, 5, 2, 0, 6) },
166 { WHvArm64RegisterDbgwcr5El1, ENCODE_AA64_CP_REG(0, 5, 2, 0, 7) },
167
168 { WHvArm64RegisterDbgbvr6El1, ENCODE_AA64_CP_REG(0, 6, 2, 0, 4) },
169 { WHvArm64RegisterDbgbcr6El1, ENCODE_AA64_CP_REG(0, 6, 2, 0, 5) },
170 { WHvArm64RegisterDbgwvr6El1, ENCODE_AA64_CP_REG(0, 6, 2, 0, 6) },
171 { WHvArm64RegisterDbgwcr6El1, ENCODE_AA64_CP_REG(0, 6, 2, 0, 7) },
172
173 { WHvArm64RegisterDbgbvr7El1, ENCODE_AA64_CP_REG(0, 7, 2, 0, 4) },
174 { WHvArm64RegisterDbgbcr7El1, ENCODE_AA64_CP_REG(0, 7, 2, 0, 5) },
175 { WHvArm64RegisterDbgwvr7El1, ENCODE_AA64_CP_REG(0, 7, 2, 0, 6) },
176 { WHvArm64RegisterDbgwcr7El1, ENCODE_AA64_CP_REG(0, 7, 2, 0, 7) },
177
178 { WHvArm64RegisterDbgbvr8El1, ENCODE_AA64_CP_REG(0, 8, 2, 0, 4) },
179 { WHvArm64RegisterDbgbcr8El1, ENCODE_AA64_CP_REG(0, 8, 2, 0, 5) },
180 { WHvArm64RegisterDbgwvr8El1, ENCODE_AA64_CP_REG(0, 8, 2, 0, 6) },
181 { WHvArm64RegisterDbgwcr8El1, ENCODE_AA64_CP_REG(0, 8, 2, 0, 7) },
182
183 { WHvArm64RegisterDbgbvr9El1, ENCODE_AA64_CP_REG(0, 9, 2, 0, 4) },
184 { WHvArm64RegisterDbgbcr9El1, ENCODE_AA64_CP_REG(0, 9, 2, 0, 5) },
185 { WHvArm64RegisterDbgwvr9El1, ENCODE_AA64_CP_REG(0, 9, 2, 0, 6) },
186 { WHvArm64RegisterDbgwcr9El1, ENCODE_AA64_CP_REG(0, 9, 2, 0, 7) },
187
188 { WHvArm64RegisterDbgbvr10El1, ENCODE_AA64_CP_REG(0, 10, 2, 0, 4) },
189 { WHvArm64RegisterDbgbcr10El1, ENCODE_AA64_CP_REG(0, 10, 2, 0, 5) },
190 { WHvArm64RegisterDbgwvr10El1, ENCODE_AA64_CP_REG(0, 10, 2, 0, 6) },
191 { WHvArm64RegisterDbgwcr10El1, ENCODE_AA64_CP_REG(0, 10, 2, 0, 7) },
192
193 { WHvArm64RegisterDbgbvr11El1, ENCODE_AA64_CP_REG(0, 11, 2, 0, 4) },
194 { WHvArm64RegisterDbgbcr11El1, ENCODE_AA64_CP_REG(0, 11, 2, 0, 5) },
195 { WHvArm64RegisterDbgwvr11El1, ENCODE_AA64_CP_REG(0, 11, 2, 0, 6) },
196 { WHvArm64RegisterDbgwcr11El1, ENCODE_AA64_CP_REG(0, 11, 2, 0, 7) },
197
198 { WHvArm64RegisterDbgbvr12El1, ENCODE_AA64_CP_REG(0, 12, 2, 0, 4) },
199 { WHvArm64RegisterDbgbcr12El1, ENCODE_AA64_CP_REG(0, 12, 2, 0, 5) },
200 { WHvArm64RegisterDbgwvr12El1, ENCODE_AA64_CP_REG(0, 12, 2, 0, 6) },
201 { WHvArm64RegisterDbgwcr12El1, ENCODE_AA64_CP_REG(0, 12, 2, 0, 7) },
202
203 { WHvArm64RegisterDbgbvr13El1, ENCODE_AA64_CP_REG(0, 13, 2, 0, 4) },
204 { WHvArm64RegisterDbgbcr13El1, ENCODE_AA64_CP_REG(0, 13, 2, 0, 5) },
205 { WHvArm64RegisterDbgwvr13El1, ENCODE_AA64_CP_REG(0, 13, 2, 0, 6) },
206 { WHvArm64RegisterDbgwcr13El1, ENCODE_AA64_CP_REG(0, 13, 2, 0, 7) },
207
208 { WHvArm64RegisterDbgbvr14El1, ENCODE_AA64_CP_REG(0, 14, 2, 0, 4) },
209 { WHvArm64RegisterDbgbcr14El1, ENCODE_AA64_CP_REG(0, 14, 2, 0, 5) },
210 { WHvArm64RegisterDbgwvr14El1, ENCODE_AA64_CP_REG(0, 14, 2, 0, 6) },
211 { WHvArm64RegisterDbgwcr14El1, ENCODE_AA64_CP_REG(0, 14, 2, 0, 7) },
212
213 { WHvArm64RegisterDbgbvr15El1, ENCODE_AA64_CP_REG(0, 15, 2, 0, 4) },
214 { WHvArm64RegisterDbgbcr15El1, ENCODE_AA64_CP_REG(0, 15, 2, 0, 5) },
215 { WHvArm64RegisterDbgwvr15El1, ENCODE_AA64_CP_REG(0, 15, 2, 0, 6) },
216 { WHvArm64RegisterDbgwcr15El1, ENCODE_AA64_CP_REG(0, 15, 2, 0, 7) },
217 #ifdef SYNC_NO_RAW_REGS
218 /*
219 * The registers below are manually synced on init because they are
220 * marked as NO_RAW. We still list them to make number space sync easier.
221 */
222 { WHvArm64RegisterMidrEl1, ENCODE_AA64_CP_REG(0, 0, 3, 0, 0) },
223 { WHvArm64RegisterMpidrEl1, ENCODE_AA64_CP_REG(0, 0, 3, 0, 5) },
224 { WHvArm64RegisterIdPfr0El1, ENCODE_AA64_CP_REG(0, 4, 3, 0, 0) },
225 #endif
226 { WHvArm64RegisterIdAa64Pfr1El1, ENCODE_AA64_CP_REG(0, 4, 3, 0, 1), true },
227 { WHvArm64RegisterIdAa64Dfr0El1, ENCODE_AA64_CP_REG(0, 5, 3, 0, 0), true },
228 { WHvArm64RegisterIdAa64Dfr1El1, ENCODE_AA64_CP_REG(0, 5, 3, 0, 1), true },
229 { WHvArm64RegisterIdAa64Isar0El1, ENCODE_AA64_CP_REG(0, 6, 3, 0, 0), true },
230 { WHvArm64RegisterIdAa64Isar1El1, ENCODE_AA64_CP_REG(0, 6, 3, 0, 1), true },
231 #ifdef SYNC_NO_MMFR0
232 /* We keep the hardware MMFR0 around. HW limits are there anyway */
233 { WHvArm64RegisterIdAa64Mmfr0El1, ENCODE_AA64_CP_REG(0, 7, 3, 0, 0) },
234 #endif
235 { WHvArm64RegisterIdAa64Mmfr1El1, ENCODE_AA64_CP_REG(0, 7, 3, 0, 1), true },
236 { WHvArm64RegisterIdAa64Mmfr2El1, ENCODE_AA64_CP_REG(0, 7, 3, 0, 2), true },
237 { WHvArm64RegisterIdAa64Mmfr3El1, ENCODE_AA64_CP_REG(0, 7, 3, 0, 3), true },
238
239 { WHvArm64RegisterMdscrEl1, ENCODE_AA64_CP_REG(0, 2, 2, 0, 2) },
240 { WHvArm64RegisterSctlrEl1, ENCODE_AA64_CP_REG(1, 0, 3, 0, 0) },
241 { WHvArm64RegisterCpacrEl1, ENCODE_AA64_CP_REG(1, 0, 3, 0, 2) },
242 { WHvArm64RegisterTtbr0El1, ENCODE_AA64_CP_REG(2, 0, 3, 0, 0) },
243 { WHvArm64RegisterTtbr1El1, ENCODE_AA64_CP_REG(2, 0, 3, 0, 1) },
244 { WHvArm64RegisterTcrEl1, ENCODE_AA64_CP_REG(2, 0, 3, 0, 2) },
245
246 { WHvArm64RegisterApiAKeyLoEl1, ENCODE_AA64_CP_REG(2, 1, 3, 0, 0) },
247 { WHvArm64RegisterApiAKeyHiEl1, ENCODE_AA64_CP_REG(2, 1, 3, 0, 1) },
248 { WHvArm64RegisterApiBKeyLoEl1, ENCODE_AA64_CP_REG(2, 1, 3, 0, 2) },
249 { WHvArm64RegisterApiBKeyHiEl1, ENCODE_AA64_CP_REG(2, 1, 3, 0, 3) },
250 { WHvArm64RegisterApdAKeyLoEl1, ENCODE_AA64_CP_REG(2, 2, 3, 0, 0) },
251 { WHvArm64RegisterApdAKeyHiEl1, ENCODE_AA64_CP_REG(2, 2, 3, 0, 1) },
252 { WHvArm64RegisterApdBKeyLoEl1, ENCODE_AA64_CP_REG(2, 2, 3, 0, 2) },
253 { WHvArm64RegisterApdBKeyHiEl1, ENCODE_AA64_CP_REG(2, 2, 3, 0, 3) },
254 { WHvArm64RegisterApgAKeyLoEl1, ENCODE_AA64_CP_REG(2, 3, 3, 0, 0) },
255 { WHvArm64RegisterApgAKeyHiEl1, ENCODE_AA64_CP_REG(2, 3, 3, 0, 1) },
256
257 { WHvArm64RegisterSpsrEl1, ENCODE_AA64_CP_REG(4, 0, 3, 0, 0) },
258 { WHvArm64RegisterElrEl1, ENCODE_AA64_CP_REG(4, 0, 3, 0, 1) },
259 { WHvArm64RegisterSpEl1, ENCODE_AA64_CP_REG(4, 1, 3, 0, 0) },
260 { WHvArm64RegisterEsrEl1, ENCODE_AA64_CP_REG(5, 2, 3, 0, 0) },
261 { WHvArm64RegisterFarEl1, ENCODE_AA64_CP_REG(6, 0, 3, 0, 0) },
262 { WHvArm64RegisterParEl1, ENCODE_AA64_CP_REG(7, 4, 3, 0, 0) },
263 { WHvArm64RegisterMairEl1, ENCODE_AA64_CP_REG(10, 2, 3, 0, 0) },
264 { WHvArm64RegisterVbarEl1, ENCODE_AA64_CP_REG(12, 0, 3, 0, 0) },
265 { WHvArm64RegisterContextidrEl1, ENCODE_AA64_CP_REG(13, 0, 3, 0, 1) },
266 { WHvArm64RegisterTpidrEl1, ENCODE_AA64_CP_REG(13, 0, 3, 0, 4) },
267 { WHvArm64RegisterCntkctlEl1, ENCODE_AA64_CP_REG(14, 1, 3, 0, 0) },
268 { WHvArm64RegisterCsselrEl1, ENCODE_AA64_CP_REG(0, 0, 3, 2, 0) },
269 { WHvArm64RegisterTpidrEl0, ENCODE_AA64_CP_REG(13, 0, 3, 3, 2) },
270 { WHvArm64RegisterTpidrroEl0, ENCODE_AA64_CP_REG(13, 0, 3, 3, 3) },
271 { WHvArm64RegisterCntvCtlEl0, ENCODE_AA64_CP_REG(14, 3, 3, 3, 1) },
272 { WHvArm64RegisterCntvCvalEl0, ENCODE_AA64_CP_REG(14, 3, 3, 3, 2) },
273 { WHvArm64RegisterSpEl1, ENCODE_AA64_CP_REG(4, 1, 3, 4, 0) },
274 };
275
276 HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
277 {
278 if (exceptions != 0) {
279 return E_NOTIMPL;
280 }
281 return ERROR_SUCCESS;
282 }
283 void whpx_apply_breakpoints(
284 struct whpx_breakpoint_collection *breakpoints,
285 CPUState *cpu,
286 bool resuming)
287 {
288 /* Breakpoints aren’t supported on this platform */
289 }
290 void whpx_translate_cpu_breakpoints(
291 struct whpx_breakpoints *breakpoints,
292 CPUState *cpu,
293 int cpu_breakpoint_count)
294 {
295 /* Breakpoints aren’t supported on this platform */
296 }
297
298 void whpx_arch_destroy_vcpu(CPUState *cpu)
299 {
300 /* currently empty on Arm */
301 }
302
303 static void whpx_get_global_reg(WHV_REGISTER_NAME reg, WHV_REGISTER_VALUE *val)
304 {
305 struct whpx_state *whpx = &whpx_global;
306 HRESULT hr;
307
308 hr = whp_dispatch.WHvGetVirtualProcessorRegisters(whpx->partition, WHV_ANY_VP,
309 &reg, 1, val);
310
311 if (FAILED(hr)) {
312 error_report("WHPX: Failed to get register %08x, hr=%08lx", reg, hr);
313 }
314 }
315
316 static void whpx_set_global_reg(WHV_REGISTER_NAME reg, WHV_REGISTER_VALUE val)
317 {
318 struct whpx_state *whpx = &whpx_global;
319 HRESULT hr;
320 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(whpx->partition, WHV_ANY_VP,
321 &reg, 1, &val);
322
323 if (FAILED(hr)) {
324 error_report("WHPX: Failed to set register %08x, hr=%08lx", reg, hr);
325 }
326 }
327
328 static uint64_t whpx_get_gp_reg(CPUState *cpu, int rt)
329 {
330 assert(rt <= 31);
331 if (rt == 31) {
332 return 0;
333 }
334 WHV_REGISTER_NAME reg = WHvArm64RegisterX0 + rt;
335 WHV_REGISTER_VALUE val;
336 whpx_get_reg(cpu, reg, &val);
337
338 return val.Reg64;
339 }
340
341 static void whpx_set_gp_reg(CPUState *cpu, int rt, uint64_t val)
342 {
343 assert(rt < 31);
344 WHV_REGISTER_NAME reg = WHvArm64RegisterX0 + rt;
345 WHV_REGISTER_VALUE reg_val = {.Reg64 = val};
346
347 whpx_set_reg(cpu, reg, reg_val);
348 }
349
350 static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
351 {
352 uint64_t syndrome = ctx->Syndrome;
353
354 bool isv = FIELD_EX32(syndrome, DABORT_ISS, ISV);
355 bool iswrite = FIELD_EX32(syndrome, DABORT_ISS, WNR);
356 bool sse = FIELD_EX32(syndrome, DABORT_ISS, SSE);
357 uint32_t sas = FIELD_EX32(syndrome, DABORT_ISS, SAS);
358 uint32_t len = 1 << sas;
359 uint32_t srt = FIELD_EX32(syndrome, DABORT_ISS, SRT);
360 uint32_t cm = FIELD_EX32(syndrome, DABORT_ISS, CM);
361 uint64_t val = 0;
362
363 assert(syn_get_ec(syndrome) == EC_DATAABORT);
364 assert(!cm);
365 assert(isv);
366
367 if (iswrite) {
368 val = whpx_get_gp_reg(cpu, srt);
369 address_space_write(&address_space_memory,
370 ctx->Gpa,
371 MEMTXATTRS_UNSPECIFIED, &val, len);
372 } else {
373 address_space_read(&address_space_memory,
374 ctx->Gpa,
375 MEMTXATTRS_UNSPECIFIED, &val, len);
376 if (sse) {
377 val = sextract64(val, 0, len * 8);
378 }
379 whpx_set_gp_reg(cpu, srt, val);
380 }
381
382 return 0;
383 }
384
385 static void whpx_psci_cpu_off(ARMCPU *arm_cpu)
386 {
387 int32_t ret = arm_set_cpu_off(arm_cpu_mp_affinity(arm_cpu));
388 assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS);
389 }
390
391 int whpx_vcpu_run(CPUState *cpu)
392 {
393 HRESULT hr;
394 struct whpx_state *whpx = &whpx_global;
395 ARMCPU *arm_cpu = ARM_CPU(cpu);
396 AccelCPUState *vcpu = cpu->accel;
397 int ret;
398
399
400 g_assert(bql_locked());
401
402 if (whpx->running_cpus++ == 0) {
403 ret = whpx_first_vcpu_starting(cpu);
404 if (ret != 0) {
405 return ret;
406 }
407 }
408
409 bql_unlock();
410
411
412 cpu_exec_start(cpu);
413 do {
414 bool advance_pc = false;
415 if (cpu->vcpu_dirty) {
416 whpx_set_registers(cpu, WHPX_LEVEL_RUNTIME_STATE);
417 cpu->vcpu_dirty = false;
418 }
419
420 if (qatomic_read(&cpu->exit_request)) {
421 whpx_vcpu_kick(cpu);
422 }
423
424 hr = whp_dispatch.WHvRunVirtualProcessor(
425 whpx->partition, cpu->cpu_index,
426 &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
427
428 if (FAILED(hr)) {
429 error_report("WHPX: Failed to exec a virtual processor,"
430 " hr=%08lx", hr);
431 ret = -1;
432 break;
433 }
434
435 switch (vcpu->exit_ctx.ExitReason) {
436 case WHvRunVpExitReasonGpaIntercept:
437 case WHvRunVpExitReasonUnmappedGpa:
438 assert(syn_get_ec(vcpu->exit_ctx.MemoryAccess.Syndrome) == EC_DATAABORT);
439 advance_pc = true;
440
441 if (vcpu->exit_ctx.MemoryAccess.Syndrome & BIT(8)) {
442 error_report("WHPX: cached access to unmapped memory"
443 "Pc = 0x%llx Gva = 0x%llx Gpa = 0x%llx",
444 vcpu->exit_ctx.MemoryAccess.Header.Pc,
445 vcpu->exit_ctx.MemoryAccess.Gpa,
446 vcpu->exit_ctx.MemoryAccess.Gva);
447 break;
448 }
449
450 ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
451 break;
452 case WHvRunVpExitReasonCanceled:
453 cpu->exception_index = EXCP_INTERRUPT;
454 ret = 1;
455 break;
456 case WHvRunVpExitReasonArm64Reset:
457 switch (vcpu->exit_ctx.Arm64Reset.ResetType) {
458 case WHvArm64ResetTypePowerOff:
459 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
460 break;
461 case WHvArm64ResetTypeReboot:
462 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
463 break;
464 default:
465 g_assert_not_reached();
466 }
467 bql_lock();
468 if (arm_cpu->power_state != PSCI_OFF) {
469 whpx_psci_cpu_off(arm_cpu);
470 }
471 /* Partition-wide reset, to reset state for reboots to succeed. */
472 whp_dispatch.WHvResetPartition(whpx->partition);
473 bql_unlock();
474 break;
475 case WHvRunVpExitReasonNone:
476 case WHvRunVpExitReasonUnrecoverableException:
477 case WHvRunVpExitReasonInvalidVpRegisterValue:
478 case WHvRunVpExitReasonUnsupportedFeature:
479 default:
480 error_report("WHPX: Unexpected VP exit code 0x%08x",
481 vcpu->exit_ctx.ExitReason);
482 whpx_get_registers(cpu, WHPX_LEVEL_FULL_STATE);
483 bql_lock();
484 qemu_system_guest_panicked(cpu_get_crash_info(cpu));
485 bql_unlock();
486 break;
487 }
488 if (advance_pc) {
489 WHV_REGISTER_VALUE pc;
490
491 whpx_flush_cpu_state(cpu);
492 pc.Reg64 = vcpu->exit_ctx.MemoryAccess.Header.Pc + 4;
493 whpx_set_reg(cpu, WHvArm64RegisterPc, pc);
494 }
495 } while (!ret);
496
497 cpu_exec_end(cpu);
498
499 bql_lock();
500 current_cpu = cpu;
501
502 if (--whpx->running_cpus == 0) {
503 whpx_last_vcpu_stopping(cpu);
504 }
505
506 qatomic_set(&cpu->exit_request, false);
507
508 return ret < 0;
509 }
510
511 static void clean_whv_register_value(WHV_REGISTER_VALUE *val)
512 {
513 memset(val, 0, sizeof(WHV_REGISTER_VALUE));
514 }
515
516 void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
517 {
518 ARMCPU *arm_cpu = ARM_CPU(cpu);
519 CPUARMState *env = &arm_cpu->env;
520 WHV_REGISTER_VALUE val;
521 int i;
522
523 for (i = 0; i < ARRAY_SIZE(whpx_reg_match); i++) {
524 whpx_get_reg(cpu, whpx_reg_match[i].reg, &val);
525 *(uint64_t *)((char *)env + whpx_reg_match[i].offset) = val.Reg64;
526 }
527
528 for (i = 0; i < ARRAY_SIZE(whpx_fpreg_match); i++) {
529 whpx_get_reg(cpu, whpx_fpreg_match[i].reg, &val);
530 memcpy((char *)env + whpx_fpreg_match[i].offset, &val, sizeof(val.Reg128));
531 }
532
533 whpx_get_reg(cpu, WHvArm64RegisterPc, &val);
534 env->pc = val.Reg64;
535
536 whpx_get_reg(cpu, WHvArm64RegisterFpcr, &val);
537 vfp_set_fpcr(env, val.Reg32);
538
539 whpx_get_reg(cpu, WHvArm64RegisterFpsr, &val);
540 vfp_set_fpsr(env, val.Reg32);
541
542 whpx_get_reg(cpu, WHvArm64RegisterPstate, &val);
543 pstate_write(env, val.Reg32);
544
545 for (i = 0; i < ARRAY_SIZE(whpx_sreg_match); i++) {
546 if (whpx_sreg_match[i].cp_idx == -1) {
547 continue;
548 }
549
550 if (whpx_sreg_match[i].global) {
551 /* WHP disallows us from accessing global regs as a vCPU */
552 whpx_get_global_reg(whpx_sreg_match[i].reg, &val);
553 } else {
554 whpx_get_reg(cpu, whpx_sreg_match[i].reg, &val);
555 }
556 arm_cpu->cpreg_values[whpx_sreg_match[i].cp_idx] = val.Reg64;
557 }
558
559 assert(write_list_to_cpustate(arm_cpu));
560 aarch64_restore_sp(env, arm_current_el(env));
561 }
562
563 void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
564 {
565 ARMCPU *arm_cpu = ARM_CPU(cpu);
566 CPUARMState *env = &arm_cpu->env;
567 WHV_REGISTER_VALUE val;
568 clean_whv_register_value(&val);
569 int i;
570
571 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
572
573 for (i = 0; i < ARRAY_SIZE(whpx_reg_match); i++) {
574 val.Reg64 = *(uint64_t *)((char *)env + whpx_reg_match[i].offset);
575 whpx_set_reg(cpu, whpx_reg_match[i].reg, val);
576 }
577
578 for (i = 0; i < ARRAY_SIZE(whpx_fpreg_match); i++) {
579 memcpy(&val.Reg128, (char *)env + whpx_fpreg_match[i].offset, sizeof(val.Reg128));
580 whpx_set_reg(cpu, whpx_fpreg_match[i].reg, val);
581 }
582
583 clean_whv_register_value(&val);
584 val.Reg64 = env->pc;
585 whpx_set_reg(cpu, WHvArm64RegisterPc, val);
586
587 clean_whv_register_value(&val);
588 val.Reg32 = vfp_get_fpcr(env);
589 whpx_set_reg(cpu, WHvArm64RegisterFpcr, val);
590 val.Reg32 = vfp_get_fpsr(env);
591 whpx_set_reg(cpu, WHvArm64RegisterFpsr, val);
592 val.Reg32 = pstate_read(env);
593 whpx_set_reg(cpu, WHvArm64RegisterPstate, val);
594
595 aarch64_save_sp(env, arm_current_el(env));
596
597 assert(write_cpustate_to_list(arm_cpu, false));
598
599 /* Currently set global regs every time. */
600 for (i = 0; i < ARRAY_SIZE(whpx_sreg_match); i++) {
601 if (whpx_sreg_match[i].cp_idx == -1) {
602 continue;
603 }
604
605 val.Reg64 = arm_cpu->cpreg_values[whpx_sreg_match[i].cp_idx];
606 if (whpx_sreg_match[i].global) {
607 /* WHP disallows us from accessing global regs as a vCPU */
608 whpx_set_global_reg(whpx_sreg_match[i].reg, val);
609 } else {
610 whpx_set_reg(cpu, whpx_sreg_match[i].reg, val);
611 }
612 }
613 }
614
615 static uint32_t max_vcpu_index;
616
617 static void whpx_cpu_update_state(void *opaque, bool running, RunState state)
618 {
619 }
620
621 uint32_t whpx_arm_get_ipa_bit_size(void)
622 {
623 WHV_CAPABILITY whpx_cap;
624 UINT32 whpx_cap_size;
625 HRESULT hr;
626 hr = whp_dispatch.WHvGetCapability(
627 WHvCapabilityCodePhysicalAddressWidth, &whpx_cap,
628 sizeof(whpx_cap), &whpx_cap_size);
629 if (FAILED(hr)) {
630 error_report("WHPX: failed to get supported "
631 "physical address width, hr=%08lx", hr);
632 }
633
634 /*
635 * We clamp any IPA size we want to back the VM with to a valid PARange
636 * value so the guest doesn't try and map memory outside of the valid range.
637 * This logic just clamps the passed in IPA bit size to the first valid
638 * PARange value <= to it.
639 */
640 return round_down_to_parange_bit_size(whpx_cap.PhysicalAddressWidth);
641 }
642
643 static void clamp_id_aa64mmfr0_parange_to_ipa_size(ARMISARegisters *isar)
644 {
645 uint32_t ipa_size = whpx_arm_get_ipa_bit_size();
646 uint64_t id_aa64mmfr0;
647
648 /* Clamp down the PARange to the IPA size the kernel supports. */
649 uint8_t index = round_down_to_parange_index(ipa_size);
650 id_aa64mmfr0 = GET_IDREG(isar, ID_AA64MMFR0);
651 id_aa64mmfr0 = (id_aa64mmfr0 & ~R_ID_AA64MMFR0_PARANGE_MASK) | index;
652 SET_IDREG(isar, ID_AA64MMFR0, id_aa64mmfr0);
653 }
654
655 static uint64_t whpx_read_midr(void)
656 {
657 HKEY key;
658 uint64_t midr_el1;
659 DWORD size = sizeof(midr_el1);
660 const char *path = "Hardware\\Description\\System\\CentralProcessor\\0\\";
661 assert(!RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &key));
662 assert(!RegGetValueA(key, NULL, "CP 4000", RRF_RT_REG_QWORD, NULL, &midr_el1, &size));
663 RegCloseKey(key);
664 return midr_el1;
665 }
666
667 static bool whpx_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
668 {
669 const struct isar_regs {
670 WHV_REGISTER_NAME reg;
671 uint64_t *val;
672 } regs[] = {
673 { WHvArm64RegisterIdAa64Pfr0El1, &ahcf->isar.idregs[ID_AA64PFR0_EL1_IDX] },
674 { WHvArm64RegisterIdAa64Pfr1El1, &ahcf->isar.idregs[ID_AA64PFR1_EL1_IDX] },
675 { WHvArm64RegisterIdAa64Dfr0El1, &ahcf->isar.idregs[ID_AA64DFR0_EL1_IDX] },
676 { WHvArm64RegisterIdAa64Dfr1El1 , &ahcf->isar.idregs[ID_AA64DFR1_EL1_IDX] },
677 { WHvArm64RegisterIdAa64Isar0El1, &ahcf->isar.idregs[ID_AA64ISAR0_EL1_IDX] },
678 { WHvArm64RegisterIdAa64Isar1El1, &ahcf->isar.idregs[ID_AA64ISAR1_EL1_IDX] },
679 { WHvArm64RegisterIdAa64Isar2El1, &ahcf->isar.idregs[ID_AA64ISAR2_EL1_IDX] },
680 { WHvArm64RegisterIdAa64Mmfr0El1, &ahcf->isar.idregs[ID_AA64MMFR0_EL1_IDX] },
681 { WHvArm64RegisterIdAa64Mmfr1El1, &ahcf->isar.idregs[ID_AA64MMFR1_EL1_IDX] },
682 { WHvArm64RegisterIdAa64Mmfr2El1, &ahcf->isar.idregs[ID_AA64MMFR2_EL1_IDX] },
683 { WHvArm64RegisterIdAa64Mmfr3El1, &ahcf->isar.idregs[ID_AA64MMFR3_EL1_IDX] }
684 };
685
686 int i;
687 WHV_REGISTER_VALUE val;
688
689 ahcf->dtb_compatible = "arm,armv8";
690 ahcf->features = (1ULL << ARM_FEATURE_V8) |
691 (1ULL << ARM_FEATURE_NEON) |
692 (1ULL << ARM_FEATURE_AARCH64) |
693 (1ULL << ARM_FEATURE_PMU) |
694 (1ULL << ARM_FEATURE_GENERIC_TIMER);
695
696 for (i = 0; i < ARRAY_SIZE(regs); i++) {
697 clean_whv_register_value(&val);
698 whpx_get_global_reg(regs[i].reg, &val);
699 *regs[i].val = val.Reg64;
700 }
701
702 /*
703 * MIDR_EL1 is not a global register on WHPX
704 * As such, read the CPU0 from the registry to get a consistent value.
705 * Otherwise, on heterogenous systems, you'll get variance between CPUs.
706 */
707 ahcf->midr = whpx_read_midr();
708
709 clamp_id_aa64mmfr0_parange_to_ipa_size(&ahcf->isar);
710
711 /*
712 * Disable SVE, which is not supported by QEMU whpx yet.
713 * Work needed for SVE support:
714 * - SVE state save/restore
715 * - any potentially needed VL management
716 * Also disable SME at the same time.
717 */
718 SET_IDREG(&ahcf->isar, ID_AA64PFR0,
719 GET_IDREG(&ahcf->isar, ID_AA64PFR0) & ~R_ID_AA64PFR0_SVE_MASK);
720
721 SET_IDREG(&ahcf->isar, ID_AA64PFR1,
722 GET_IDREG(&ahcf->isar, ID_AA64PFR1) & ~R_ID_AA64PFR1_SME_MASK);
723
724 return true;
725 }
726
727 void whpx_arm_set_cpu_features_from_host(ARMCPU *cpu)
728 {
729 if (!arm_host_cpu_features.dtb_compatible) {
730 if (!whpx_enabled() ||
731 !whpx_arm_get_host_cpu_features(&arm_host_cpu_features)) {
732 /*
733 * We can't report this error yet, so flag that we need to
734 * in arm_cpu_realizefn().
735 */
736 cpu->host_cpu_probe_failed = true;
737 return;
738 }
739 }
740
741 cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible;
742 cpu->isar = arm_host_cpu_features.isar;
743 cpu->env.features = arm_host_cpu_features.features;
744 cpu->midr = arm_host_cpu_features.midr;
745 cpu->reset_sctlr = arm_host_cpu_features.reset_sctlr;
746 }
747
748 int whpx_init_vcpu(CPUState *cpu)
749 {
750 HRESULT hr;
751 struct whpx_state *whpx = &whpx_global;
752 ARMCPU *arm_cpu = ARM_CPU(cpu);
753 CPUARMState *env = &arm_cpu->env;
754
755 uint32_t sregs_match_len = ARRAY_SIZE(whpx_sreg_match);
756 uint32_t sregs_cnt = 0;
757 WHV_REGISTER_VALUE val;
758 int i;
759
760 hr = whp_dispatch.WHvCreateVirtualProcessor(
761 whpx->partition, cpu->cpu_index, 0);
762 if (FAILED(hr)) {
763 error_report("WHPX: Failed to create a virtual processor,"
764 " hr=%08lx", hr);
765 return -EINVAL;
766 }
767
768 /* Assumption that CNTFRQ_EL0 is the same between the VMM and the partition. */
769 asm volatile("mrs %0, cntfrq_el0" : "=r"(arm_cpu->gt_cntfrq_hz));
770
771 cpu->vcpu_dirty = true;
772 cpu->accel = g_new0(AccelCPUState, 1);
773 max_vcpu_index = MAX(max_vcpu_index, cpu->cpu_index);
774 qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);
775
776 env->aarch64 = true;
777
778 /* Allocate enough space for our sysreg sync */
779 arm_cpu->cpreg_indexes = g_renew(uint64_t, arm_cpu->cpreg_indexes,
780 sregs_match_len);
781 arm_cpu->cpreg_values = g_renew(uint64_t, arm_cpu->cpreg_values,
782 sregs_match_len);
783
784 memset(arm_cpu->cpreg_values, 0, sregs_match_len * sizeof(uint64_t));
785
786 /* Populate cp list for all known sysregs */
787 for (i = 0; i < sregs_match_len; i++) {
788 const ARMCPRegInfo *ri;
789 uint32_t key = whpx_sreg_match[i].key;
790
791 ri = get_arm_cp_reginfo(arm_cpu->cp_regs, key);
792 if (ri) {
793 assert(!(ri->type & ARM_CP_NO_RAW));
794 whpx_sreg_match[i].cp_idx = sregs_cnt;
795 arm_cpu->cpreg_indexes[sregs_cnt++] = cpreg_to_kvm_id(key);
796 } else {
797 whpx_sreg_match[i].cp_idx = -1;
798 }
799 }
800 arm_cpu->cpreg_array_len = sregs_cnt;
801
802 assert(write_cpustate_to_list(arm_cpu, false));
803
804 /* Set CP_NO_RAW system registers on init */
805 val.Reg64 = arm_cpu->midr;
806 whpx_set_reg(cpu, WHvArm64RegisterMidrEl1,
807 val);
808
809 clean_whv_register_value(&val);
810
811 val.Reg64 = deposit64(arm_cpu->mp_affinity, 31, 1, 1 /* RES1 */);
812 whpx_set_reg(cpu, WHvArm64RegisterMpidrEl1, val);
813
814 clamp_id_aa64mmfr0_parange_to_ipa_size(&arm_cpu->isar);
815 return 0;
816 }
817
818 void whpx_cpu_instance_init(CPUState *cs)
819 {
820 }
821
822 void whpx_arch_accel_class_init(ObjectClass *oc)
823 {
824 }
825
826 int whpx_accel_init(AccelState *as, MachineState *ms)
827 {
828 struct whpx_state *whpx;
829 int ret;
830 HRESULT hr;
831 WHV_CAPABILITY whpx_cap;
832 UINT32 whpx_cap_size;
833 WHV_PARTITION_PROPERTY prop;
834 WHV_CAPABILITY_FEATURES features;
835 WHV_SYNTHETIC_PROCESSOR_FEATURES_BANKS synthetic_features;
836 MachineClass *mc = MACHINE_GET_CLASS(ms);
837 int pa_range = 0;
838
839 whpx = &whpx_global;
840 /* on arm64 Windows Hypervisor Platform, vGICv3 always used */
841 whpx_irqchip_in_kernel = true;
842
843 if (!init_whp_dispatch()) {
844 ret = -ENOSYS;
845 goto error;
846 }
847
848 if (mc->get_physical_address_range) {
849 pa_range = mc->get_physical_address_range(ms,
850 whpx_arm_get_ipa_bit_size(), whpx_arm_get_ipa_bit_size());
851 if (pa_range < 0) {
852 return -EINVAL;
853 }
854 }
855
856 whpx->mem_quota = ms->ram_size;
857
858 hr = whp_dispatch.WHvGetCapability(
859 WHvCapabilityCodeHypervisorPresent, &whpx_cap,
860 sizeof(whpx_cap), &whpx_cap_size);
861 if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
862 error_report("WHPX: No accelerator found, hr=%08lx", hr);
863 ret = -ENOSPC;
864 goto error;
865 }
866
867 memset(&features, 0, sizeof(features));
868 hr = whp_dispatch.WHvGetCapability(
869 WHvCapabilityCodeFeatures, &features, sizeof(features), NULL);
870 if (FAILED(hr)) {
871 error_report("WHPX: Failed to query capabilities, hr=%08lx", hr);
872 ret = -EINVAL;
873 goto error;
874 }
875
876 if (!features.Arm64Support) {
877 error_report("WHPX: host OS exposing pre-release WHPX implementation. "
878 "Please update your operating system to at least build 26100.3915");
879 ret = -EINVAL;
880 goto error;
881 }
882
883 hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
884 if (FAILED(hr)) {
885 error_report("WHPX: Failed to create partition, hr=%08lx", hr);
886 ret = -EINVAL;
887 goto error;
888 }
889
890 memset(&prop, 0, sizeof(prop));
891 prop.ProcessorCount = ms->smp.cpus;
892 hr = whp_dispatch.WHvSetPartitionProperty(
893 whpx->partition,
894 WHvPartitionPropertyCodeProcessorCount,
895 &prop,
896 sizeof(prop));
897
898 if (FAILED(hr)) {
899 error_report("WHPX: Failed to set partition processor count to %u,"
900 " hr=%08lx", prop.ProcessorCount, hr);
901 ret = -EINVAL;
902 goto error;
903 }
904
905 if (!whpx->kernel_irqchip_allowed) {
906 error_report("WHPX: on Arm, only kernel-irqchip=on is currently supported");
907 ret = -EINVAL;
908 goto error;
909 }
910
911 memset(&prop, 0, sizeof(prop));
912
913 /*
914 * The only currently supported configuration for the interrupt
915 * controller is kernel-irqchip=on,gic-version=3, with the `virt`
916 * machine.
917 *
918 * Initialising the vGIC here because it needs to be done prior to
919 * WHvSetupPartition.
920 */
921
922 WHV_ARM64_IC_PARAMETERS ic_params = {
923 .EmulationMode = WHvArm64IcEmulationModeGicV3,
924 .GicV3Parameters = {
925 .GicdBaseAddress = 0x08000000,
926 .GitsTranslaterBaseAddress = 0x08080000,
927 .GicLpiIntIdBits = 0,
928 .GicPpiPerformanceMonitorsInterrupt = VIRTUAL_PMU_IRQ,
929 .GicPpiOverflowInterruptFromCntv = ARCH_TIMER_VIRT_IRQ
930 }
931 };
932 prop.Arm64IcParameters = ic_params;
933
934 hr = whp_dispatch.WHvSetPartitionProperty(
935 whpx->partition,
936 WHvPartitionPropertyCodeArm64IcParameters,
937 &prop,
938 sizeof(prop));
939 if (FAILED(hr)) {
940 error_report("WHPX: Failed to enable GICv3 interrupt controller, hr=%08lx", hr);
941 ret = -EINVAL;
942 goto error;
943 }
944
945 /* Enable synthetic processor features */
946 memset(&synthetic_features, 0, sizeof(WHV_SYNTHETIC_PROCESSOR_FEATURES_BANKS));
947 synthetic_features.BanksCount = 1;
948
949 synthetic_features.Bank0.HypervisorPresent = 1;
950 synthetic_features.Bank0.Hv1 = 1;
951 synthetic_features.Bank0.AccessVpRunTimeReg = 1;
952 synthetic_features.Bank0.AccessPartitionReferenceCounter = 1;
953 synthetic_features.Bank0.AccessPartitionReferenceTsc = 1;
954 synthetic_features.Bank0.AccessHypercallRegs = 1;
955 synthetic_features.Bank0.AccessVpIndex = 1;
956 synthetic_features.Bank0.TbFlushHypercalls = 1;
957 synthetic_features.Bank0.AccessSynicRegs = 1;
958 synthetic_features.Bank0.AccessSyntheticTimerRegs = 1;
959 synthetic_features.Bank0.AccessIntrCtrlRegs = 1;
960 synthetic_features.Bank0.SyntheticClusterIpi = 1;
961 synthetic_features.Bank0.DirectSyntheticTimers = 1;
962 synthetic_features.Bank0.FastHypercallOutput = 1;
963 synthetic_features.Bank0.AccessVpRegs = 1;
964 synthetic_features.Bank0.SyncContext = 1;
965
966 /*
967 * On ARM64, have enlightenments off by default
968 * as they're not needed for performance.
969 */
970 if (whpx->hyperv_enlightenments_required) {
971 whpx->hyperv_enlightenments_enabled = true;
972 hr = whp_dispatch.WHvSetPartitionProperty(
973 whpx->partition,
974 WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks,
975 &synthetic_features,
976 sizeof(WHV_SYNTHETIC_PROCESSOR_FEATURES_BANKS));
977 if (FAILED(hr)) {
978 error_report("WHPX: Failed to set synthetic features, hr=%08lx", hr);
979 ret = -EINVAL;
980 goto error;
981 }
982 }
983
984 hr = whp_dispatch.WHvSetupPartition(whpx->partition);
985 if (FAILED(hr)) {
986 error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
987 ret = -EINVAL;
988 goto error;
989 }
990
991 whpx_memory_init();
992
993 return 0;
994
995 error:
996 if (whpx->partition != NULL) {
997 whp_dispatch.WHvDeletePartition(whpx->partition);
998 whpx->partition = NULL;
999 }
1000
1001 return ret;
1002 }