master
c 405 lines 11.3 KB
Raw
1 /*
2 * MSHV in-kernel APIC support
3 *
4 * Copyright Microsoft, Corp. 2026
5 *
6 * Authors: Magnus Kulke <magnuskulke@microsoft.com>
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "qemu/osdep.h"
12 #include "qemu/module.h"
13 #include "qemu/memalign.h"
14 #include "qemu/error-report.h"
15 #include "hw/i386/apic_internal.h"
16 #include "hw/i386/apic-msidef.h"
17 #include "hw/pci/msi.h"
18 #include "migration/vmstate.h"
19 #include "qemu/typedefs.h"
20 #include "system/hw_accel.h"
21 #include "system/mshv.h"
22 #include "system/mshv_int.h"
23
24 typedef struct hv_local_interrupt_controller_state
25 hv_local_interrupt_controller_state;
26
27 #define TYPE_MSHV_APIC "mshv-apic"
28 OBJECT_DECLARE_SIMPLE_TYPE(MshvAPICState, MSHV_APIC)
29
30 struct MshvAPICState {
31 APICCommonState parent_obj;
32
33 uint32_t apic_version;
34 uint32_t apic_lvt_cmci;
35 uint32_t apic_error_status;
36 uint32_t apic_counter_value;
37 uint32_t apic_remote_read;
38 };
39
40 static int get_lapic(int cpu_fd,
41 struct hv_local_interrupt_controller_state *state)
42 {
43 int ret;
44 size_t size = 4096;
45 /* buffer aligned to 4k, as *state requires that */
46 void *buffer = qemu_memalign(size, size);
47 struct mshv_get_set_vp_state mshv_state = { 0 };
48
49 mshv_state.buf_ptr = (uint64_t) buffer;
50 mshv_state.buf_sz = size;
51 mshv_state.type = MSHV_VP_STATE_LAPIC;
52
53 ret = mshv_get_vp_state(cpu_fd, &mshv_state);
54 if (ret == 0) {
55 memcpy(state, buffer, sizeof(*state));
56 }
57 qemu_vfree(buffer);
58 if (ret < 0) {
59 error_report("failed to get lapic");
60 return -1;
61 }
62
63 return 0;
64 }
65
66 static int set_lapic(int cpu_fd,
67 const struct hv_local_interrupt_controller_state *state)
68 {
69 int ret;
70 size_t size = 4096;
71 /* buffer aligned to 4k, as *state requires that */
72 void *buffer = qemu_memalign(size, size);
73 struct mshv_get_set_vp_state mshv_state = { 0 };
74
75 if (!state) {
76 error_report("lapic state is NULL");
77 return -1;
78 }
79 memcpy(buffer, state, sizeof(*state));
80
81 mshv_state.buf_ptr = (uint64_t) buffer;
82 mshv_state.buf_sz = size;
83 mshv_state.type = MSHV_VP_STATE_LAPIC;
84
85 ret = mshv_set_vp_state(cpu_fd, &mshv_state);
86 qemu_vfree(buffer);
87 if (ret < 0) {
88 error_report("failed to set lapic: %s", strerror(errno));
89 return -1;
90 }
91
92 return 0;
93 }
94
95 static void populate_apic_state(CPUState *cpu,
96 const hv_local_interrupt_controller_state *hv)
97 {
98 X86CPU *x86cpu = X86_CPU(cpu);
99 MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state);
100 APICCommonState *s = &ms->parent_obj;
101 size_t i;
102
103 /*
104 * x2APIC:
105 * - APIC ID is the full 32-bit initial_apic_id
106 * - LDR is read-only, architecturally derived from the ID
107 * - DFR does not exist in x2APIC mode
108 */
109 if (is_x2apic_mode(s)) {
110 s->initial_apic_id = hv->apic_id;
111 } else {
112 s->id = hv->apic_id >> 24;
113 s->log_dest = hv->apic_ldr >> 24;
114 s->dest_mode = hv->apic_dfr >> 28;
115 }
116 ms->apic_version = hv->apic_version;
117 s->spurious_vec = hv->apic_spurious;
118 for (i = 0; i < 8; i++) {
119 s->isr[i] = hv->apic_isr[i];
120 s->tmr[i] = hv->apic_tmr[i];
121 s->irr[i] = hv->apic_irr[i];
122 }
123 s->esr = hv->apic_esr;
124 s->icr[1] = hv->apic_icr_high;
125 s->icr[0] = hv->apic_icr_low;
126
127 s->lvt[APIC_LVT_TIMER] = hv->apic_lvt_timer;
128 s->lvt[APIC_LVT_THERMAL] = hv->apic_lvt_thermal;
129 s->lvt[APIC_LVT_PERFORM] = hv->apic_lvt_perfmon;
130 s->lvt[APIC_LVT_LINT0] = hv->apic_lvt_lint0;
131 s->lvt[APIC_LVT_LINT1] = hv->apic_lvt_lint1;
132 s->lvt[APIC_LVT_ERROR] = hv->apic_lvt_error;
133 ms->apic_lvt_cmci = hv->apic_lvt_cmci;
134
135 ms->apic_error_status = hv->apic_error_status;
136 s->initial_count = hv->apic_initial_count;
137 ms->apic_counter_value = hv->apic_counter_value;
138 s->divide_conf = hv->apic_divide_configuration;
139 ms->apic_remote_read = hv->apic_remote_read;
140 }
141
142 static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode)
143 {
144 return ((reg) & ~0x700) | ((mode) << 8);
145 }
146
147 int mshv_init_lint(CPUState *cpu)
148 {
149 uint32_t *lvt_lint0, *lvt_lint1;
150 int cpu_fd = mshv_vcpufd(cpu);
151 int ret;
152 struct hv_local_interrupt_controller_state lapic_state = { 0 };
153
154 ret = get_lapic(cpu_fd, &lapic_state);
155 if (ret < 0) {
156 return ret;
157 }
158
159 lvt_lint0 = &lapic_state.apic_lvt_lint0;
160 *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT);
161
162 lvt_lint1 = &lapic_state.apic_lvt_lint1;
163 *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI);
164
165 /* TODO: should we skip setting lapic if the values are the same? */
166
167 ret = set_lapic(cpu_fd, &lapic_state);
168 if (ret < 0) {
169 return -1;
170 }
171
172 populate_apic_state(cpu, &lapic_state);
173
174 return 0;
175 }
176
177 static void populate_hv_lapic_state(hv_local_interrupt_controller_state *hv,
178 const CPUState *cpu)
179 {
180 uint32_t x2apic_id;
181 X86CPU *x86cpu = X86_CPU(cpu);
182 MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state);
183 APICCommonState *s = &ms->parent_obj;
184 size_t i;
185
186 /*
187 * x2APIC:
188 * - APIC ID is the full 32-bit initial_apic_id
189 * - LDR is read-only, architecturally derived from the ID
190 * - DFR does not exist in x2APIC mode
191 */
192 if (is_x2apic_mode(s)) {
193 x2apic_id = s->initial_apic_id;
194
195 hv->apic_id = x2apic_id;
196 hv->apic_ldr = ((x2apic_id >> 4) << 16) | (1 << (x2apic_id & 0xf));
197 hv->apic_dfr = 0;
198 } else {
199 hv->apic_id = s->id << 24;
200 hv->apic_ldr = s->log_dest << 24;
201 hv->apic_dfr = s->dest_mode << 28 | 0x0fffffff;
202 }
203 hv->apic_version = ms->apic_version;
204 hv->apic_spurious = s->spurious_vec;
205 for (i = 0; i < 8; i++) {
206 hv->apic_isr[i] = s->isr[i];
207 hv->apic_tmr[i] = s->tmr[i];
208 hv->apic_irr[i] = s->irr[i];
209 }
210 hv->apic_esr = s->esr;
211 hv->apic_icr_high = s->icr[1];
212 hv->apic_icr_low = s->icr[0];
213
214 hv->apic_lvt_timer = s->lvt[APIC_LVT_TIMER];
215 hv->apic_lvt_thermal = s->lvt[APIC_LVT_THERMAL];
216 hv->apic_lvt_perfmon = s->lvt[APIC_LVT_PERFORM];
217 hv->apic_lvt_lint0 = s->lvt[APIC_LVT_LINT0];
218 hv->apic_lvt_lint1 = s->lvt[APIC_LVT_LINT1];
219 hv->apic_lvt_error = s->lvt[APIC_LVT_ERROR];
220 hv->apic_lvt_cmci = ms->apic_lvt_cmci;
221
222 hv->apic_error_status = ms->apic_error_status;
223 hv->apic_initial_count = s->initial_count;
224 hv->apic_counter_value = ms->apic_counter_value;
225 hv->apic_divide_configuration = s->divide_conf;
226 hv->apic_remote_read = ms->apic_remote_read;
227 }
228
229 int mshv_set_lapic(const CPUState *cpu)
230 {
231 int cpu_fd = mshv_vcpufd(cpu);
232 struct hv_local_interrupt_controller_state lapic_state = { 0 };
233
234 populate_hv_lapic_state(&lapic_state, cpu);
235
236 return set_lapic(cpu_fd, &lapic_state);
237 }
238
239 int mshv_get_lapic(CPUState *cpu)
240 {
241 int cpu_fd = mshv_vcpufd(cpu);
242 int ret;
243 struct hv_local_interrupt_controller_state lapic_state = { 0 };
244
245 ret = get_lapic(cpu_fd, &lapic_state);
246 if (ret < 0) {
247 return -1;
248 }
249
250 populate_apic_state(cpu, &lapic_state);
251
252 return 0;
253 }
254
255 static int mshv_apic_set_base(APICCommonState *s, uint64_t val)
256 {
257 s->apicbase = val;
258
259 return 0;
260 }
261
262 static void mshv_apic_set_tpr(APICCommonState *s, uint8_t val)
263 {
264 s->tpr = (val & APIC_PR_SUB_CLASS) << APIC_PR_CLASS_SHIFT;
265 }
266
267 static uint8_t mshv_apic_get_tpr(APICCommonState *s)
268 {
269 return s->tpr >> APIC_PR_CLASS_SHIFT;
270 }
271
272 static void mshv_apic_external_nmi(APICCommonState *s)
273 {
274 }
275
276 static void mshv_apic_vapic_base_update(APICCommonState *s)
277 {
278 }
279
280 static void mshv_send_msi(MSIMessage *msi)
281 {
282 uint64_t addr;
283 uint32_t data, dest;
284 uint8_t vector, dest_mode, trigger_mode, delivery;
285
286 addr = msi->address;
287 data = msi->data;
288 dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT |
289 (addr >> 32);
290 vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
291 dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
292 trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
293 delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) &
294 MSI_DATA_DELIVERY_MODE_MASK;
295
296 /*
297 * Vector 0 is not a valid interrupt vector (0-15 are reserved for CPU
298 * exceptions). This can trigger during machine reset, if hpet_reset()
299 * forces the PIT to pulse GSI 2 before IOAPIC's own reset has masked its
300 * redirection entries.
301 */
302 if (vector == 0) {
303 return;
304 }
305
306 mshv_request_interrupt(mshv_state, delivery, vector, dest, dest_mode,
307 trigger_mode);
308 }
309
310 static uint64_t mshv_apic_mem_read(void *opaque, hwaddr addr,
311 unsigned size)
312 {
313 return UINT64_MAX;
314 }
315
316 static void mshv_apic_mem_write(void *opaque, hwaddr addr,
317 uint64_t data, unsigned size)
318 {
319 MSIMessage msg = { .address = addr, .data = data };
320
321 mshv_send_msi(&msg);
322 }
323
324 static const MemoryRegionOps mshv_apic_io_ops = {
325 .read = mshv_apic_mem_read,
326 .write = mshv_apic_mem_write,
327 .endianness = DEVICE_LITTLE_ENDIAN,
328 };
329
330 static void mshv_apic_reset(APICCommonState *s)
331 {
332 s->wait_for_sipi = 0;
333 }
334
335 static const VMStateDescription vmstate_mshv_apic = {
336 .name = "mshv-apic",
337 .version_id = 1,
338 .minimum_version_id = 1,
339 .fields = (const VMStateField[]) {
340 VMSTATE_UINT32(apic_version, MshvAPICState),
341 VMSTATE_UINT32(apic_lvt_cmci, MshvAPICState),
342 VMSTATE_UINT32(apic_error_status, MshvAPICState),
343 VMSTATE_UINT32(apic_counter_value, MshvAPICState),
344 VMSTATE_UINT32(apic_remote_read, MshvAPICState),
345 VMSTATE_END_OF_LIST()
346 }
347 };
348
349 static void mshv_apic_realize(DeviceState *dev, Error **errp)
350 {
351 APICCommonState *s = APIC_COMMON(dev);
352 MshvAPICState *ms = MSHV_APIC(dev);
353
354 memory_region_init_io(&s->io_memory, OBJECT(s), &mshv_apic_io_ops, s,
355 "mshv-apic-msi", APIC_SPACE_SIZE);
356
357 msi_nonbroken = true;
358
359 /*
360 * We register this state explicity, rather than going via dc->vmsd.
361 * The auto-wiring would register the state with
362 * instance_id == VMSTATE_INSTANCE_ID_ANY, which for the APIC doesn't
363 * work, b/c the ID carries semantic meaning for restoring the state
364 * on the destination (which vcpu it belongs to).
365 */
366 vmstate_register_with_alias_id(NULL,
367 s->initial_apic_id, &vmstate_mshv_apic, ms,
368 -1, 0, NULL);
369 }
370
371 static void mshv_apic_unrealize(DeviceState *dev)
372 {
373 MshvAPICState *ms = MSHV_APIC(dev);
374
375 vmstate_unregister(NULL, &vmstate_mshv_apic, ms);
376 }
377
378 static void mshv_apic_class_init(ObjectClass *klass, const void *data)
379 {
380 APICCommonClass *k = APIC_COMMON_CLASS(klass);
381
382 k->realize = mshv_apic_realize;
383 k->unrealize = mshv_apic_unrealize;
384 k->reset = mshv_apic_reset;
385 k->set_base = mshv_apic_set_base;
386 k->set_tpr = mshv_apic_set_tpr;
387 k->get_tpr = mshv_apic_get_tpr;
388 k->external_nmi = mshv_apic_external_nmi;
389 k->vapic_base_update = mshv_apic_vapic_base_update;
390 k->send_msi = mshv_send_msi;
391 }
392
393 static const TypeInfo mshv_apic_info = {
394 .name = TYPE_MSHV_APIC,
395 .parent = TYPE_APIC_COMMON,
396 .instance_size = sizeof(MshvAPICState),
397 .class_init = mshv_apic_class_init,
398 };
399
400 static void mshv_apic_register_types(void)
401 {
402 type_register_static(&mshv_apic_info);
403 }
404
405 type_init(mshv_apic_register_types)