]>
Commit | Line | Data |
---|---|---|
82cffa2e CLG |
1 | /* |
2 | * QEMU PowerPC sPAPR IRQ interface | |
3 | * | |
4 | * Copyright (c) 2018, IBM Corporation. | |
5 | * | |
6 | * This code is licensed under the GPL version 2 or later. See the | |
7 | * COPYING file in the top-level directory. | |
8 | */ | |
9 | ||
10 | #include "qemu/osdep.h" | |
11 | #include "qemu/log.h" | |
12 | #include "qemu/error-report.h" | |
13 | #include "qapi/error.h" | |
64552b6b | 14 | #include "hw/irq.h" |
82cffa2e | 15 | #include "hw/ppc/spapr.h" |
a28b9a5a | 16 | #include "hw/ppc/spapr_cpu_core.h" |
dcc345b6 | 17 | #include "hw/ppc/spapr_xive.h" |
82cffa2e | 18 | #include "hw/ppc/xics.h" |
a51d5afc | 19 | #include "hw/ppc/xics_spapr.h" |
a27bd6c7 | 20 | #include "hw/qdev-properties.h" |
273fef83 | 21 | #include "cpu-models.h" |
ef01ed9d CLG |
22 | #include "sysemu/kvm.h" |
23 | ||
24 | #include "trace.h" | |
82cffa2e | 25 | |
150e25f8 DG |
26 | static const TypeInfo spapr_intc_info = { |
27 | .name = TYPE_SPAPR_INTC, | |
28 | .parent = TYPE_INTERFACE, | |
29 | .class_size = sizeof(SpaprInterruptControllerClass), | |
30 | }; | |
31 | ||
8cbe71ec | 32 | static void spapr_irq_msi_init(SpaprMachineState *spapr) |
82cffa2e | 33 | { |
8cbe71ec DG |
34 | if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { |
35 | /* Legacy mode doesn't use this allocator */ | |
36 | return; | |
37 | } | |
38 | ||
39 | spapr->irq_map_nr = spapr_irq_nr_msis(spapr); | |
82cffa2e CLG |
40 | spapr->irq_map = bitmap_new(spapr->irq_map_nr); |
41 | } | |
42 | ||
ce2918cb | 43 | int spapr_irq_msi_alloc(SpaprMachineState *spapr, uint32_t num, bool align, |
82cffa2e CLG |
44 | Error **errp) |
45 | { | |
46 | int irq; | |
47 | ||
48 | /* | |
49 | * The 'align_mask' parameter of bitmap_find_next_zero_area() | |
50 | * should be one less than a power of 2; 0 means no | |
51 | * alignment. Adapt the 'align' value of the former allocator | |
52 | * to fit the requirements of bitmap_find_next_zero_area() | |
53 | */ | |
54 | align -= 1; | |
55 | ||
56 | irq = bitmap_find_next_zero_area(spapr->irq_map, spapr->irq_map_nr, 0, num, | |
57 | align); | |
58 | if (irq == spapr->irq_map_nr) { | |
59 | error_setg(errp, "can't find a free %d-IRQ block", num); | |
60 | return -1; | |
61 | } | |
62 | ||
63 | bitmap_set(spapr->irq_map, irq, num); | |
64 | ||
65 | return irq + SPAPR_IRQ_MSI; | |
66 | } | |
67 | ||
ce2918cb | 68 | void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num) |
82cffa2e CLG |
69 | { |
70 | bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num); | |
71 | } | |
72 | ||
4ffb7496 | 73 | int spapr_irq_init_kvm(SpaprInterruptControllerInitKvm fn, |
567192d4 | 74 | SpaprInterruptController *intc, |
4ffb7496 | 75 | uint32_t nr_servers, |
567192d4 | 76 | Error **errp) |
ef01ed9d | 77 | { |
ef01ed9d CLG |
78 | Error *local_err = NULL; |
79 | ||
4376c40d | 80 | if (kvm_enabled() && kvm_kernel_irqchip_allowed()) { |
4ffb7496 | 81 | if (fn(intc, nr_servers, &local_err) < 0) { |
4376c40d | 82 | if (kvm_kernel_irqchip_required()) { |
0a17e0c3 DG |
83 | error_prepend(&local_err, |
84 | "kernel_irqchip requested but unavailable: "); | |
85 | error_propagate(errp, local_err); | |
86 | return -1; | |
87 | } | |
ae805ea9 | 88 | |
0a17e0c3 DG |
89 | /* |
90 | * We failed to initialize the KVM device, fallback to | |
91 | * emulated mode | |
92 | */ | |
93 | error_prepend(&local_err, | |
94 | "kernel_irqchip allowed but unavailable: "); | |
95 | error_append_hint(&local_err, | |
96 | "Falling back to kernel-irqchip=off\n"); | |
97 | warn_report_err(local_err); | |
ae805ea9 | 98 | } |
ef01ed9d | 99 | } |
0a17e0c3 DG |
100 | |
101 | return 0; | |
ae805ea9 CLG |
102 | } |
103 | ||
104 | /* | |
105 | * XICS IRQ backend. | |
106 | */ | |
107 | ||
ce2918cb | 108 | SpaprIrq spapr_irq_xics = { |
ca62823b DG |
109 | .xics = true, |
110 | .xive = false, | |
ef01ed9d CLG |
111 | }; |
112 | ||
dcc345b6 CLG |
113 | /* |
114 | * XIVE IRQ backend. | |
115 | */ | |
dcc345b6 | 116 | |
ce2918cb | 117 | SpaprIrq spapr_irq_xive = { |
ca62823b DG |
118 | .xics = false, |
119 | .xive = true, | |
dcc345b6 CLG |
120 | }; |
121 | ||
13db0cd9 CLG |
122 | /* |
123 | * Dual XIVE and XICS IRQ backend. | |
124 | * | |
125 | * Both interrupt mode, XIVE and XICS, objects are created but the | |
126 | * machine starts in legacy interrupt mode (XICS). It can be changed | |
127 | * by the CAS negotiation process and, in that case, the new mode is | |
128 | * activated after an extra machine reset. | |
129 | */ | |
130 | ||
13db0cd9 CLG |
131 | /* |
132 | * Define values in sync with the XIVE and XICS backend | |
133 | */ | |
ce2918cb | 134 | SpaprIrq spapr_irq_dual = { |
ca62823b DG |
135 | .xics = true, |
136 | .xive = true, | |
13db0cd9 CLG |
137 | }; |
138 | ||
273fef83 | 139 | |
0a3fd3df | 140 | static int spapr_irq_check(SpaprMachineState *spapr, Error **errp) |
273fef83 CLG |
141 | { |
142 | MachineState *machine = MACHINE(spapr); | |
143 | ||
144 | /* | |
145 | * Sanity checks on non-P9 machines. On these, XIVE is not | |
146 | * advertised, see spapr_dt_ov5_platform_support() | |
147 | */ | |
148 | if (!ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, | |
149 | 0, spapr->max_compat_pvr)) { | |
150 | /* | |
151 | * If the 'dual' interrupt mode is selected, force XICS as CAS | |
152 | * negotiation is useless. | |
153 | */ | |
154 | if (spapr->irq == &spapr_irq_dual) { | |
155 | spapr->irq = &spapr_irq_xics; | |
0a3fd3df | 156 | return 0; |
273fef83 CLG |
157 | } |
158 | ||
159 | /* | |
160 | * Non-P9 machines using only XIVE is a bogus setup. We have two | |
161 | * scenarios to take into account because of the compat mode: | |
162 | * | |
163 | * 1. POWER7/8 machines should fail to init later on when creating | |
164 | * the XIVE interrupt presenters because a POWER9 exception | |
165 | * model is required. | |
166 | ||
167 | * 2. POWER9 machines using the POWER8 compat mode won't fail and | |
168 | * will let the OS boot with a partial XIVE setup : DT | |
169 | * properties but no hcalls. | |
170 | * | |
171 | * To cover both and not confuse the OS, add an early failure in | |
172 | * QEMU. | |
173 | */ | |
174 | if (spapr->irq == &spapr_irq_xive) { | |
175 | error_setg(errp, "XIVE-only machines require a POWER9 CPU"); | |
0a3fd3df | 176 | return -1; |
273fef83 CLG |
177 | } |
178 | } | |
7abc0c6d GK |
179 | |
180 | /* | |
181 | * On a POWER9 host, some older KVM XICS devices cannot be destroyed and | |
182 | * re-created. Detect that early to avoid QEMU to exit later when the | |
183 | * guest reboots. | |
184 | */ | |
185 | if (kvm_enabled() && | |
186 | spapr->irq == &spapr_irq_dual && | |
4376c40d | 187 | kvm_kernel_irqchip_required() && |
7abc0c6d GK |
188 | xics_kvm_has_broken_disconnect(spapr)) { |
189 | error_setg(errp, "KVM is too old to support ic-mode=dual,kernel-irqchip=on"); | |
0a3fd3df | 190 | return -1; |
7abc0c6d | 191 | } |
0a3fd3df DG |
192 | |
193 | return 0; | |
273fef83 CLG |
194 | } |
195 | ||
ef01ed9d CLG |
196 | /* |
197 | * sPAPR IRQ frontend routines for devices | |
198 | */ | |
ebd6be08 DG |
199 | #define ALL_INTCS(spapr_) \ |
200 | { SPAPR_INTC((spapr_)->ics), SPAPR_INTC((spapr_)->xive), } | |
201 | ||
202 | int spapr_irq_cpu_intc_create(SpaprMachineState *spapr, | |
203 | PowerPCCPU *cpu, Error **errp) | |
204 | { | |
205 | SpaprInterruptController *intcs[] = ALL_INTCS(spapr); | |
206 | int i; | |
207 | int rc; | |
208 | ||
209 | for (i = 0; i < ARRAY_SIZE(intcs); i++) { | |
210 | SpaprInterruptController *intc = intcs[i]; | |
211 | if (intc) { | |
212 | SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc); | |
213 | rc = sicc->cpu_intc_create(intc, cpu, errp); | |
214 | if (rc < 0) { | |
215 | return rc; | |
216 | } | |
217 | } | |
218 | } | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
d49e8a9b CLG |
223 | void spapr_irq_cpu_intc_reset(SpaprMachineState *spapr, PowerPCCPU *cpu) |
224 | { | |
225 | SpaprInterruptController *intcs[] = ALL_INTCS(spapr); | |
226 | int i; | |
227 | ||
228 | for (i = 0; i < ARRAY_SIZE(intcs); i++) { | |
229 | SpaprInterruptController *intc = intcs[i]; | |
230 | if (intc) { | |
231 | SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc); | |
232 | sicc->cpu_intc_reset(intc, cpu); | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
0990ce6a GK |
237 | void spapr_irq_cpu_intc_destroy(SpaprMachineState *spapr, PowerPCCPU *cpu) |
238 | { | |
239 | SpaprInterruptController *intcs[] = ALL_INTCS(spapr); | |
240 | int i; | |
241 | ||
242 | for (i = 0; i < ARRAY_SIZE(intcs); i++) { | |
243 | SpaprInterruptController *intc = intcs[i]; | |
244 | if (intc) { | |
245 | SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc); | |
246 | sicc->cpu_intc_destroy(intc, cpu); | |
247 | } | |
248 | } | |
249 | } | |
250 | ||
7bcdbcca DG |
251 | static void spapr_set_irq(void *opaque, int irq, int level) |
252 | { | |
253 | SpaprMachineState *spapr = SPAPR_MACHINE(opaque); | |
254 | SpaprInterruptControllerClass *sicc | |
255 | = SPAPR_INTC_GET_CLASS(spapr->active_intc); | |
256 | ||
257 | sicc->set_irq(spapr->active_intc, irq, level); | |
258 | } | |
259 | ||
328d8eb2 DG |
260 | void spapr_irq_print_info(SpaprMachineState *spapr, Monitor *mon) |
261 | { | |
262 | SpaprInterruptControllerClass *sicc | |
263 | = SPAPR_INTC_GET_CLASS(spapr->active_intc); | |
264 | ||
265 | sicc->print_info(spapr->active_intc, mon); | |
266 | } | |
267 | ||
05289273 DG |
268 | void spapr_irq_dt(SpaprMachineState *spapr, uint32_t nr_servers, |
269 | void *fdt, uint32_t phandle) | |
270 | { | |
271 | SpaprInterruptControllerClass *sicc | |
272 | = SPAPR_INTC_GET_CLASS(spapr->active_intc); | |
273 | ||
274 | sicc->dt(spapr->active_intc, nr_servers, fdt, phandle); | |
275 | } | |
276 | ||
8cbe71ec DG |
277 | uint32_t spapr_irq_nr_msis(SpaprMachineState *spapr) |
278 | { | |
54255c1f DG |
279 | SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); |
280 | ||
281 | if (smc->legacy_irq_allocation) { | |
282 | return smc->nr_xirqs; | |
8cbe71ec | 283 | } else { |
54255c1f | 284 | return SPAPR_XIRQ_BASE + smc->nr_xirqs - SPAPR_IRQ_MSI; |
8cbe71ec DG |
285 | } |
286 | } | |
287 | ||
ce2918cb | 288 | void spapr_irq_init(SpaprMachineState *spapr, Error **errp) |
fab397d8 | 289 | { |
54255c1f | 290 | SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); |
1a511340 | 291 | |
4376c40d | 292 | if (kvm_enabled() && kvm_kernel_irqchip_split()) { |
1a511340 GK |
293 | error_setg(errp, "kernel_irqchip split mode not supported on pseries"); |
294 | return; | |
295 | } | |
296 | ||
0a3fd3df | 297 | if (spapr_irq_check(spapr, errp) < 0) { |
273fef83 CLG |
298 | return; |
299 | } | |
300 | ||
fab397d8 | 301 | /* Initialize the MSI IRQ allocator. */ |
8cbe71ec | 302 | spapr_irq_msi_init(spapr); |
fab397d8 | 303 | |
f478d9af DG |
304 | if (spapr->irq->xics) { |
305 | Error *local_err = NULL; | |
306 | Object *obj; | |
307 | ||
308 | obj = object_new(TYPE_ICS_SPAPR); | |
f478d9af | 309 | |
818a6d30 | 310 | object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort); |
b015a980 GK |
311 | object_property_set_link(obj, OBJECT(spapr), ICS_PROP_XICS, |
312 | &error_abort); | |
818a6d30 | 313 | object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &error_abort); |
f478d9af DG |
314 | object_property_set_bool(obj, true, "realized", &local_err); |
315 | if (local_err) { | |
316 | error_propagate(errp, local_err); | |
317 | return; | |
318 | } | |
319 | ||
320 | spapr->ics = ICS_SPAPR(obj); | |
321 | } | |
322 | ||
323 | if (spapr->irq->xive) { | |
324 | uint32_t nr_servers = spapr_max_server_number(spapr); | |
325 | DeviceState *dev; | |
326 | int i; | |
327 | ||
328 | dev = qdev_create(NULL, TYPE_SPAPR_XIVE); | |
54255c1f | 329 | qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_XIRQ_BASE); |
f478d9af DG |
330 | /* |
331 | * 8 XIVE END structures per CPU. One for each available | |
332 | * priority | |
333 | */ | |
334 | qdev_prop_set_uint32(dev, "nr-ends", nr_servers << 3); | |
335 | qdev_init_nofail(dev); | |
336 | ||
337 | spapr->xive = SPAPR_XIVE(dev); | |
338 | ||
339 | /* Enable the CPU IPIs */ | |
340 | for (i = 0; i < nr_servers; ++i) { | |
0b0e52b1 DG |
341 | SpaprInterruptControllerClass *sicc |
342 | = SPAPR_INTC_GET_CLASS(spapr->xive); | |
343 | ||
344 | if (sicc->claim_irq(SPAPR_INTC(spapr->xive), SPAPR_IRQ_IPI + i, | |
345 | false, errp) < 0) { | |
f478d9af DG |
346 | return; |
347 | } | |
348 | } | |
349 | ||
350 | spapr_xive_hcall_init(spapr); | |
351 | } | |
872ff3de | 352 | |
7bcdbcca | 353 | spapr->qirqs = qemu_allocate_irqs(spapr_set_irq, spapr, |
54255c1f | 354 | smc->nr_xirqs + SPAPR_XIRQ_BASE); |
b14848f5 DG |
355 | |
356 | /* | |
357 | * Mostly we don't actually need this until reset, except that not | |
358 | * having this set up can cause VFIO devices to issue a | |
359 | * false-positive warning during realize(), because they don't yet | |
360 | * have an in-kernel irq chip. | |
361 | */ | |
362 | spapr_irq_update_active_intc(spapr); | |
fab397d8 | 363 | } |
ef01ed9d | 364 | |
ce2918cb | 365 | int spapr_irq_claim(SpaprMachineState *spapr, int irq, bool lsi, Error **errp) |
ef01ed9d | 366 | { |
0b0e52b1 DG |
367 | SpaprInterruptController *intcs[] = ALL_INTCS(spapr); |
368 | int i; | |
54255c1f | 369 | SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); |
0b0e52b1 DG |
370 | int rc; |
371 | ||
580dde5e | 372 | assert(irq >= SPAPR_XIRQ_BASE); |
54255c1f | 373 | assert(irq < (smc->nr_xirqs + SPAPR_XIRQ_BASE)); |
580dde5e | 374 | |
0b0e52b1 DG |
375 | for (i = 0; i < ARRAY_SIZE(intcs); i++) { |
376 | SpaprInterruptController *intc = intcs[i]; | |
377 | if (intc) { | |
378 | SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc); | |
379 | rc = sicc->claim_irq(intc, irq, lsi, errp); | |
380 | if (rc < 0) { | |
381 | return rc; | |
382 | } | |
383 | } | |
384 | } | |
385 | ||
386 | return 0; | |
ef01ed9d CLG |
387 | } |
388 | ||
ce2918cb | 389 | void spapr_irq_free(SpaprMachineState *spapr, int irq, int num) |
ef01ed9d | 390 | { |
0b0e52b1 DG |
391 | SpaprInterruptController *intcs[] = ALL_INTCS(spapr); |
392 | int i, j; | |
54255c1f | 393 | SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); |
f233cee9 | 394 | |
580dde5e | 395 | assert(irq >= SPAPR_XIRQ_BASE); |
54255c1f | 396 | assert((irq + num) <= (smc->nr_xirqs + SPAPR_XIRQ_BASE)); |
580dde5e | 397 | |
f233cee9 | 398 | for (i = irq; i < (irq + num); i++) { |
0b0e52b1 DG |
399 | for (j = 0; j < ARRAY_SIZE(intcs); j++) { |
400 | SpaprInterruptController *intc = intcs[j]; | |
401 | ||
402 | if (intc) { | |
403 | SpaprInterruptControllerClass *sicc | |
404 | = SPAPR_INTC_GET_CLASS(intc); | |
405 | sicc->free_irq(intc, i); | |
406 | } | |
407 | } | |
f233cee9 | 408 | } |
ef01ed9d CLG |
409 | } |
410 | ||
ce2918cb | 411 | qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq) |
ef01ed9d | 412 | { |
54255c1f DG |
413 | SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); |
414 | ||
af186151 DG |
415 | /* |
416 | * This interface is basically for VIO and PHB devices to find the | |
417 | * right qemu_irq to manipulate, so we only allow access to the | |
418 | * external irqs for now. Currently anything which needs to | |
419 | * access the IPIs most naturally gets there via the guest side | |
420 | * interfaces, we can change this if we need to in future. | |
421 | */ | |
422 | assert(irq >= SPAPR_XIRQ_BASE); | |
54255c1f | 423 | assert(irq < (smc->nr_xirqs + SPAPR_XIRQ_BASE)); |
af186151 DG |
424 | |
425 | if (spapr->ics) { | |
426 | assert(ics_valid_irq(spapr->ics, irq)); | |
427 | } | |
428 | if (spapr->xive) { | |
429 | assert(irq < spapr->xive->nr_irqs); | |
430 | assert(xive_eas_is_valid(&spapr->xive->eat[irq])); | |
431 | } | |
432 | ||
433 | return spapr->qirqs[irq]; | |
ef01ed9d CLG |
434 | } |
435 | ||
ce2918cb | 436 | int spapr_irq_post_load(SpaprMachineState *spapr, int version_id) |
1c53b06c | 437 | { |
605994e5 DG |
438 | SpaprInterruptControllerClass *sicc; |
439 | ||
81106ddd | 440 | spapr_irq_update_active_intc(spapr); |
605994e5 DG |
441 | sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc); |
442 | return sicc->post_load(spapr->active_intc, version_id); | |
1c53b06c CLG |
443 | } |
444 | ||
ce2918cb | 445 | void spapr_irq_reset(SpaprMachineState *spapr, Error **errp) |
b2e22477 | 446 | { |
e1588bcd GK |
447 | assert(!spapr->irq_map || bitmap_empty(spapr->irq_map, spapr->irq_map_nr)); |
448 | ||
81106ddd | 449 | spapr_irq_update_active_intc(spapr); |
b2e22477 CLG |
450 | } |
451 | ||
ce2918cb | 452 | int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp) |
ad62bff6 | 453 | { |
14789694 | 454 | const char *nodename = "interrupt-controller"; |
ad62bff6 GK |
455 | int offset, phandle; |
456 | ||
457 | offset = fdt_subnode_offset(fdt, 0, nodename); | |
458 | if (offset < 0) { | |
14789694 DG |
459 | error_setg(errp, "Can't find node \"%s\": %s", |
460 | nodename, fdt_strerror(offset)); | |
ad62bff6 GK |
461 | return -1; |
462 | } | |
463 | ||
464 | phandle = fdt_get_phandle(fdt, offset); | |
465 | if (!phandle) { | |
466 | error_setg(errp, "Can't get phandle of node \"%s\"", nodename); | |
467 | return -1; | |
468 | } | |
469 | ||
470 | return phandle; | |
471 | } | |
472 | ||
81106ddd DG |
473 | static void set_active_intc(SpaprMachineState *spapr, |
474 | SpaprInterruptController *new_intc) | |
475 | { | |
476 | SpaprInterruptControllerClass *sicc; | |
4ffb7496 | 477 | uint32_t nr_servers = spapr_max_server_number(spapr); |
81106ddd DG |
478 | |
479 | assert(new_intc); | |
480 | ||
481 | if (new_intc == spapr->active_intc) { | |
482 | /* Nothing to do */ | |
483 | return; | |
484 | } | |
485 | ||
486 | if (spapr->active_intc) { | |
487 | sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc); | |
488 | if (sicc->deactivate) { | |
489 | sicc->deactivate(spapr->active_intc); | |
490 | } | |
491 | } | |
492 | ||
493 | sicc = SPAPR_INTC_GET_CLASS(new_intc); | |
494 | if (sicc->activate) { | |
4ffb7496 | 495 | sicc->activate(new_intc, nr_servers, &error_fatal); |
81106ddd DG |
496 | } |
497 | ||
498 | spapr->active_intc = new_intc; | |
e532e1d9 DG |
499 | |
500 | /* | |
501 | * We've changed the kernel irqchip, let VFIO devices know they | |
502 | * need to readjust. | |
503 | */ | |
504 | kvm_irqchip_change_notify(); | |
81106ddd DG |
505 | } |
506 | ||
507 | void spapr_irq_update_active_intc(SpaprMachineState *spapr) | |
508 | { | |
509 | SpaprInterruptController *new_intc; | |
510 | ||
511 | if (!spapr->ics) { | |
512 | /* | |
513 | * XXX before we run CAS, ov5_cas is initialized empty, which | |
514 | * indicates XICS, even if we have ic-mode=xive. TODO: clean | |
515 | * up the CAS path so that we have a clearer way of handling | |
516 | * this. | |
517 | */ | |
518 | new_intc = SPAPR_INTC(spapr->xive); | |
b14848f5 DG |
519 | } else if (spapr->ov5_cas |
520 | && spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { | |
81106ddd DG |
521 | new_intc = SPAPR_INTC(spapr->xive); |
522 | } else { | |
523 | new_intc = SPAPR_INTC(spapr->ics); | |
524 | } | |
525 | ||
526 | set_active_intc(spapr, new_intc); | |
527 | } | |
528 | ||
ef01ed9d CLG |
529 | /* |
530 | * XICS legacy routines - to deprecate one day | |
531 | */ | |
532 | ||
533 | static int ics_find_free_block(ICSState *ics, int num, int alignnum) | |
534 | { | |
535 | int first, i; | |
536 | ||
537 | for (first = 0; first < ics->nr_irqs; first += alignnum) { | |
538 | if (num > (ics->nr_irqs - first)) { | |
539 | return -1; | |
540 | } | |
541 | for (i = first; i < first + num; ++i) { | |
4a99d405 | 542 | if (!ics_irq_free(ics, i)) { |
ef01ed9d CLG |
543 | break; |
544 | } | |
545 | } | |
546 | if (i == (first + num)) { | |
547 | return first; | |
548 | } | |
549 | } | |
550 | ||
551 | return -1; | |
552 | } | |
553 | ||
ce2918cb | 554 | int spapr_irq_find(SpaprMachineState *spapr, int num, bool align, Error **errp) |
ef01ed9d CLG |
555 | { |
556 | ICSState *ics = spapr->ics; | |
557 | int first = -1; | |
558 | ||
559 | assert(ics); | |
560 | ||
561 | /* | |
562 | * MSIMesage::data is used for storing VIRQ so | |
563 | * it has to be aligned to num to support multiple | |
564 | * MSI vectors. MSI-X is not affected by this. | |
565 | * The hint is used for the first IRQ, the rest should | |
566 | * be allocated continuously. | |
567 | */ | |
568 | if (align) { | |
569 | assert((num == 1) || (num == 2) || (num == 4) || | |
570 | (num == 8) || (num == 16) || (num == 32)); | |
571 | first = ics_find_free_block(ics, num, num); | |
572 | } else { | |
573 | first = ics_find_free_block(ics, num, 1); | |
574 | } | |
575 | ||
576 | if (first < 0) { | |
577 | error_setg(errp, "can't find a free %d-IRQ block", num); | |
578 | return -1; | |
579 | } | |
580 | ||
581 | return first + ics->offset; | |
582 | } | |
ae837402 | 583 | |
ce2918cb | 584 | SpaprIrq spapr_irq_xics_legacy = { |
ca62823b DG |
585 | .xics = true, |
586 | .xive = false, | |
ae837402 | 587 | }; |
150e25f8 DG |
588 | |
589 | static void spapr_irq_register_types(void) | |
590 | { | |
591 | type_register_static(&spapr_intc_info); | |
592 | } | |
593 | ||
594 | type_init(spapr_irq_register_types) |