xref: /qemu/hw/ppc/spapr_irq.c (revision ef01ed9d19ffffbb5d5517ecb424c543cde373a1)
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"
14 #include "hw/ppc/spapr.h"
15 #include "hw/ppc/xics.h"
16 #include "sysemu/kvm.h"
17 
18 #include "trace.h"
19 
20 void spapr_irq_msi_init(sPAPRMachineState *spapr, uint32_t nr_msis)
21 {
22     spapr->irq_map_nr = nr_msis;
23     spapr->irq_map = bitmap_new(spapr->irq_map_nr);
24 }
25 
26 int spapr_irq_msi_alloc(sPAPRMachineState *spapr, uint32_t num, bool align,
27                         Error **errp)
28 {
29     int irq;
30 
31     /*
32      * The 'align_mask' parameter of bitmap_find_next_zero_area()
33      * should be one less than a power of 2; 0 means no
34      * alignment. Adapt the 'align' value of the former allocator
35      * to fit the requirements of bitmap_find_next_zero_area()
36      */
37     align -= 1;
38 
39     irq = bitmap_find_next_zero_area(spapr->irq_map, spapr->irq_map_nr, 0, num,
40                                      align);
41     if (irq == spapr->irq_map_nr) {
42         error_setg(errp, "can't find a free %d-IRQ block", num);
43         return -1;
44     }
45 
46     bitmap_set(spapr->irq_map, irq, num);
47 
48     return irq + SPAPR_IRQ_MSI;
49 }
50 
51 void spapr_irq_msi_free(sPAPRMachineState *spapr, int irq, uint32_t num)
52 {
53     bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num);
54 }
55 
56 void spapr_irq_msi_reset(sPAPRMachineState *spapr)
57 {
58     bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr);
59 }
60 
61 
62 /*
63  * XICS IRQ backend.
64  */
65 
66 static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
67                                   const char *type_ics,
68                                   int nr_irqs, Error **errp)
69 {
70     Error *local_err = NULL;
71     Object *obj;
72 
73     obj = object_new(type_ics);
74     object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
75     object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
76                                    &error_abort);
77     object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err);
78     if (local_err) {
79         goto error;
80     }
81     object_property_set_bool(obj, true, "realized", &local_err);
82     if (local_err) {
83         goto error;
84     }
85 
86     return ICS_BASE(obj);
87 
88 error:
89     error_propagate(errp, local_err);
90     return NULL;
91 }
92 
93 static void spapr_irq_init_xics(sPAPRMachineState *spapr, Error **errp)
94 {
95     MachineState *machine = MACHINE(spapr);
96     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
97     int nr_irqs = smc->irq->nr_irqs;
98     Error *local_err = NULL;
99 
100     /* Initialize the MSI IRQ allocator. */
101     if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
102         spapr_irq_msi_init(spapr, XICS_IRQ_BASE + nr_irqs - SPAPR_IRQ_MSI);
103     }
104 
105     if (kvm_enabled()) {
106         if (machine_kernel_irqchip_allowed(machine) &&
107             !xics_kvm_init(spapr, &local_err)) {
108             spapr->icp_type = TYPE_KVM_ICP;
109             spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs,
110                                           &local_err);
111         }
112         if (machine_kernel_irqchip_required(machine) && !spapr->ics) {
113             error_prepend(&local_err,
114                           "kernel_irqchip requested but unavailable: ");
115             goto error;
116         }
117         error_free(local_err);
118         local_err = NULL;
119     }
120 
121     if (!spapr->ics) {
122         xics_spapr_init(spapr);
123         spapr->icp_type = TYPE_ICP;
124         spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs,
125                                       &local_err);
126     }
127 
128 error:
129     error_propagate(errp, local_err);
130 }
131 
132 #define ICS_IRQ_FREE(ics, srcno)   \
133     (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
134 
135 static int spapr_irq_claim_xics(sPAPRMachineState *spapr, int irq, bool lsi,
136                                 Error **errp)
137 {
138     ICSState *ics = spapr->ics;
139 
140     assert(ics);
141 
142     if (!ics_valid_irq(ics, irq)) {
143         error_setg(errp, "IRQ %d is invalid", irq);
144         return -1;
145     }
146 
147     if (!ICS_IRQ_FREE(ics, irq - ics->offset)) {
148         error_setg(errp, "IRQ %d is not free", irq);
149         return -1;
150     }
151 
152     ics_set_irq_type(ics, irq - ics->offset, lsi);
153     return 0;
154 }
155 
156 static void spapr_irq_free_xics(sPAPRMachineState *spapr, int irq, int num)
157 {
158     ICSState *ics = spapr->ics;
159     uint32_t srcno = irq - ics->offset;
160     int i;
161 
162     if (ics_valid_irq(ics, irq)) {
163         trace_spapr_irq_free(0, irq, num);
164         for (i = srcno; i < srcno + num; ++i) {
165             if (ICS_IRQ_FREE(ics, i)) {
166                 trace_spapr_irq_free_warn(0, i);
167             }
168             memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
169         }
170     }
171 }
172 
173 static qemu_irq spapr_qirq_xics(sPAPRMachineState *spapr, int irq)
174 {
175     ICSState *ics = spapr->ics;
176     uint32_t srcno = irq - ics->offset;
177 
178     if (ics_valid_irq(ics, irq)) {
179         return ics->qirqs[srcno];
180     }
181 
182     return NULL;
183 }
184 
185 static void spapr_irq_print_info_xics(sPAPRMachineState *spapr, Monitor *mon)
186 {
187     CPUState *cs;
188 
189     CPU_FOREACH(cs) {
190         PowerPCCPU *cpu = POWERPC_CPU(cs);
191 
192         icp_pic_print_info(ICP(cpu->intc), mon);
193     }
194 
195     ics_pic_print_info(spapr->ics, mon);
196 }
197 
198 sPAPRIrq spapr_irq_xics = {
199     .nr_irqs     = XICS_IRQS_SPAPR,
200 
201     .init        = spapr_irq_init_xics,
202     .claim       = spapr_irq_claim_xics,
203     .free        = spapr_irq_free_xics,
204     .qirq        = spapr_qirq_xics,
205     .print_info  = spapr_irq_print_info_xics,
206 };
207 
208 /*
209  * sPAPR IRQ frontend routines for devices
210  */
211 
212 int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp)
213 {
214     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
215 
216     return smc->irq->claim(spapr, irq, lsi, errp);
217 }
218 
219 void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num)
220 {
221     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
222 
223     smc->irq->free(spapr, irq, num);
224 }
225 
226 qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq)
227 {
228     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
229 
230     return smc->irq->qirq(spapr, irq);
231 }
232 
233 /*
234  * XICS legacy routines - to deprecate one day
235  */
236 
237 static int ics_find_free_block(ICSState *ics, int num, int alignnum)
238 {
239     int first, i;
240 
241     for (first = 0; first < ics->nr_irqs; first += alignnum) {
242         if (num > (ics->nr_irqs - first)) {
243             return -1;
244         }
245         for (i = first; i < first + num; ++i) {
246             if (!ICS_IRQ_FREE(ics, i)) {
247                 break;
248             }
249         }
250         if (i == (first + num)) {
251             return first;
252         }
253     }
254 
255     return -1;
256 }
257 
258 int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp)
259 {
260     ICSState *ics = spapr->ics;
261     int first = -1;
262 
263     assert(ics);
264 
265     /*
266      * MSIMesage::data is used for storing VIRQ so
267      * it has to be aligned to num to support multiple
268      * MSI vectors. MSI-X is not affected by this.
269      * The hint is used for the first IRQ, the rest should
270      * be allocated continuously.
271      */
272     if (align) {
273         assert((num == 1) || (num == 2) || (num == 4) ||
274                (num == 8) || (num == 16) || (num == 32));
275         first = ics_find_free_block(ics, num, num);
276     } else {
277         first = ics_find_free_block(ics, num, 1);
278     }
279 
280     if (first < 0) {
281         error_setg(errp, "can't find a free %d-IRQ block", num);
282         return -1;
283     }
284 
285     return first + ics->offset;
286 }
287