master
c 366 lines 9.25 KB
Raw
1 /*
2 * QEMU MSHV support
3 *
4 * Copyright Microsoft, Corp. 2025
5 *
6 * Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
7 * Magnus Kulke <magnuskulke@microsoft.com>
8 * Stanislav Kinsburskii <skinsburskii@microsoft.com>
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
12
13 #include "qemu/osdep.h"
14 #include "linux/mshv.h"
15 #include "qemu/error-report.h"
16 #include "hw/hyperv/hvhdk_mini.h"
17 #include "hw/hyperv/hvgdk_mini.h"
18 #include "hw/intc/ioapic.h"
19 #include "hw/pci/msi.h"
20 #include "system/mshv.h"
21 #include "system/mshv_int.h"
22 #include "trace.h"
23 #include <sys/ioctl.h>
24
25 #define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE)
26 #define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN)
27
28 /* Pass an eventfd which is to be used for injecting interrupts from userland */
29 static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
30 uint32_t flags)
31 {
32 int ret;
33 struct mshv_user_irqfd arg = {
34 .fd = fd,
35 .resamplefd = resample_fd,
36 .gsi = gsi,
37 .flags = flags,
38 };
39
40 ret = ioctl(vm_fd, MSHV_IRQFD, &arg);
41 if (ret < 0) {
42 error_report("Failed to set irqfd: gsi=%u, fd=%d", gsi, fd);
43 return -1;
44 }
45 return ret;
46 }
47
48 static int register_irqfd(int vm_fd, int event_fd, uint32_t gsi)
49 {
50 int ret;
51
52 trace_mshv_register_irqfd(vm_fd, event_fd, gsi);
53
54 ret = irqfd(vm_fd, event_fd, 0, gsi, 0);
55 if (ret < 0) {
56 error_report("Failed to register irqfd: gsi=%u", gsi);
57 return -1;
58 }
59 return 0;
60 }
61
62 static int register_irqfd_with_resample(int vm_fd, int event_fd,
63 int resample_fd, uint32_t gsi)
64 {
65 int ret;
66 uint32_t flags = MSHV_IRQFD_RESAMPLE_FLAG;
67
68 ret = irqfd(vm_fd, event_fd, resample_fd, gsi, flags);
69 if (ret < 0) {
70 error_report("Failed to register irqfd with resample: gsi=%u", gsi);
71 return -errno;
72 }
73 return 0;
74 }
75
76 static int unregister_irqfd(int vm_fd, int event_fd, uint32_t gsi)
77 {
78 int ret;
79 uint32_t flags = MSHV_IRQFD_BIT_DEASSIGN_FLAG;
80
81 ret = irqfd(vm_fd, event_fd, 0, gsi, flags);
82 if (ret < 0) {
83 error_report("Failed to unregister irqfd: gsi=%u", gsi);
84 return -errno;
85 }
86 return 0;
87 }
88
89 static int irqchip_update_irqfd_notifier_gsi(const EventNotifier *event,
90 const EventNotifier *resample,
91 int virq, bool add)
92 {
93 int fd = event_notifier_get_fd(event);
94 int rfd = resample ? event_notifier_get_fd(resample) : -1;
95 int vm_fd = mshv_state->vm;
96
97 trace_mshv_irqchip_update_irqfd_notifier_gsi(fd, rfd, virq, add);
98
99 if (!add) {
100 return unregister_irqfd(vm_fd, fd, virq);
101 }
102
103 if (rfd > 0) {
104 return register_irqfd_with_resample(vm_fd, fd, rfd, virq);
105 }
106
107 return register_irqfd(vm_fd, fd, virq);
108 }
109
110 static int irqchip_allocate_gsi(MshvState *s, int *gsi)
111 {
112 int next_gsi;
113
114 /* Return the lowest unused GSI in the bitmap */
115 next_gsi = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count);
116 if (next_gsi >= s->gsi_count) {
117 return -ENOSPC;
118 }
119
120 *gsi = next_gsi;
121
122 return 0;
123 }
124
125 static void irqchip_release_gsi(MshvState *s, int gsi)
126 {
127 clear_bit(gsi, s->used_gsi_bitmap);
128 }
129
130 static void add_routing_entry(MshvState *s, struct mshv_user_irq_entry *entry)
131 {
132 struct mshv_user_irq_entry *new;
133 int n, size;
134
135 if (s->irq_routes->nr == s->nr_allocated_irq_routes) {
136 n = s->nr_allocated_irq_routes * 2;
137 if (n < MSHV_MIN_ALLOCATED_MSI_ROUTES) {
138 n = MSHV_MIN_ALLOCATED_MSI_ROUTES;
139 }
140 size = sizeof(struct mshv_user_irq_table);
141 size += n * sizeof(*new);
142 s->irq_routes = g_realloc(s->irq_routes, size);
143 s->nr_allocated_irq_routes = n;
144 }
145
146 n = s->irq_routes->nr;
147 s->irq_routes->nr++;
148 new = &s->irq_routes->entries[n];
149
150 *new = *entry;
151
152 set_bit(entry->gsi, s->used_gsi_bitmap);
153
154 trace_mshv_add_msi_routing(entry->address_lo | entry->address_hi,
155 entry->data);
156 }
157
158 int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
159 {
160 struct mshv_user_irq_entry entry = { 0 };
161 MSIMessage msg = { 0 };
162 uint32_t data, high_addr, low_addr;
163 int gsi, ret;
164 MshvState *s = MSHV_STATE(c->accel);
165
166 if (!pci_available || !dev) {
167 return 0;
168 }
169
170 msg = pci_get_msi_message(dev, vector);
171
172 ret = irqchip_allocate_gsi(mshv_state, &gsi);
173 if (ret < 0) {
174 error_report("Could not allocate GSI for MSI route");
175 return -1;
176 }
177 high_addr = msg.address >> 32;
178 low_addr = msg.address & 0xFFFFFFFF;
179 data = le32_to_cpu(msg.data);
180
181 entry.gsi = gsi;
182 entry.address_hi = high_addr;
183 entry.address_lo = low_addr;
184 entry.data = data;
185
186 if (s->irq_routes->nr < s->gsi_count) {
187 add_routing_entry(s, &entry);
188 c->changes++;
189 } else {
190 irqchip_release_gsi(s, gsi);
191 return -ENOSPC;
192 }
193
194 return gsi;
195 }
196
197 void mshv_irqchip_release_virq(MshvState *s, int virq)
198 {
199 struct mshv_user_irq_entry *e;
200 int i;
201
202 for (i = 0; i < s->irq_routes->nr; i++) {
203 e = &s->irq_routes->entries[i];
204 if (e->gsi == virq) {
205 s->irq_routes->nr--;
206 *e = s->irq_routes->entries[s->irq_routes->nr];
207 }
208 }
209 irqchip_release_gsi(s, virq);
210
211 trace_mshv_remove_msi_routing(virq);
212 }
213
214 static int update_routing_entry(MshvState *s,
215 struct mshv_user_irq_entry *new_entry)
216 {
217 struct mshv_user_irq_entry *entry;
218 int n;
219
220 for (n = 0; n < s->irq_routes->nr; n++) {
221 entry = &s->irq_routes->entries[n];
222 if (entry->gsi != new_entry->gsi) {
223 continue;
224 }
225
226 if (!memcmp(entry, new_entry, sizeof *entry)) {
227 return 0;
228 }
229
230 *entry = *new_entry;
231
232 return 0;
233 }
234
235 return -ESRCH;
236 }
237
238 int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
239 {
240 uint32_t addr_hi = msg.address >> 32;
241 uint32_t addr_lo = msg.address & 0xFFFFFFFF;
242 uint32_t data = le32_to_cpu(msg.data);
243 struct mshv_user_irq_entry entry = {
244 .gsi = virq,
245 .address_hi = addr_hi,
246 .address_lo = addr_lo,
247 .data = data,
248 };
249 int ret;
250
251 ret = update_routing_entry(mshv_state, &entry);
252 if (ret < 0) {
253 error_report("Failed to set msi routing for gsi %d", virq);
254 abort();
255 }
256
257 trace_mshv_set_msi_routing(virq, msg.address, data);
258
259 return 0;
260 }
261
262 int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector,
263 uint32_t vp_index, bool logical_dest_mode,
264 bool level_triggered)
265 {
266 int ret;
267 int vm_fd = mshv_state->vm;
268
269 if (vector == 0) {
270 warn_report("Ignoring request for interrupt vector 0");
271 return 0;
272 }
273
274 union hv_interrupt_control control = {
275 .interrupt_type = interrupt_type,
276 .level_triggered = level_triggered,
277 .logical_dest_mode = logical_dest_mode,
278 .rsvd = 0,
279 };
280
281 struct hv_input_assert_virtual_interrupt arg = {0};
282 arg.control = control;
283 arg.dest_addr = (uint64_t)vp_index;
284 arg.vector = vector;
285
286 struct mshv_root_hvcall args = {0};
287 args.code = HVCALL_ASSERT_VIRTUAL_INTERRUPT;
288 args.in_sz = sizeof(arg);
289 args.in_ptr = (uint64_t)&arg;
290
291 ret = mshv_hvcall(vm_fd, &args);
292 if (ret < 0) {
293 error_report("Failed to request interrupt");
294 return -errno;
295 }
296 return 0;
297 }
298
299 void mshv_irqchip_commit_routes(MshvState *s)
300 {
301 int ret;
302 int vm_fd = s->vm;
303
304 ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, s->irq_routes);
305 if (ret < 0) {
306 error_report("Failed to commit msi routing table");
307 abort();
308 }
309 trace_mshv_commit_msi_routing_table(vm_fd, s->irq_routes->nr);
310 }
311
312 int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event,
313 const EventNotifier *resample,
314 int virq)
315 {
316 return irqchip_update_irqfd_notifier_gsi(event, resample, virq, true);
317 }
318
319 int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *event,
320 int virq)
321 {
322 return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false);
323 }
324
325 static int mshv_reserve_ioapic_msi_routes(MshvState *s)
326 {
327 int ret, i;
328 int gsi = 0;
329 struct mshv_user_irq_entry blank_entry = { 0 };
330
331 /*
332 * Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy
333 * peripherals with MSI-X devices
334 */
335 for (i = 0; i < IOAPIC_NUM_PINS; i++) {
336 /* ret = add_msi_routing(0, 0); */
337 ret = irqchip_allocate_gsi(s, &gsi);
338 if (ret < 0) {
339 error_report("Failed to reserve GSI %d: %s", gsi, strerror(-ret));
340 return -1;
341 }
342 blank_entry.gsi = gsi;
343 add_routing_entry(s, &blank_entry);
344 }
345
346 mshv_irqchip_commit_routes(s);
347
348 return 0;
349 }
350
351 void mshv_init_irq_routing(MshvState *s)
352 {
353 int ret;
354 int gsi_count = MSHV_MAX_MSI_ROUTES;
355
356 s->irq_routes = g_malloc0(sizeof(*s->irq_routes));
357 s->nr_allocated_irq_routes = 0;
358 s->gsi_count = gsi_count;
359 s->used_gsi_bitmap = bitmap_new(gsi_count);
360
361 ret = mshv_reserve_ioapic_msi_routes(s);
362 if (ret < 0) {
363 error_report("Failed to reserve IOAPIC MSI routes");
364 abort();
365 }
366 }