xref: /openbmc/qemu/hw/ppc/spapr_irq.c (revision b3d6b8f5)
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, smc->irq->nr_msis);
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 #define SPAPR_IRQ_XICS_NR_IRQS     0x1000
199 #define SPAPR_IRQ_XICS_NR_MSIS     \
200     (XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
201 
202 sPAPRIrq spapr_irq_xics = {
203     .nr_irqs     = SPAPR_IRQ_XICS_NR_IRQS,
204     .nr_msis     = SPAPR_IRQ_XICS_NR_MSIS,
205 
206     .init        = spapr_irq_init_xics,
207     .claim       = spapr_irq_claim_xics,
208     .free        = spapr_irq_free_xics,
209     .qirq        = spapr_qirq_xics,
210     .print_info  = spapr_irq_print_info_xics,
211 };
212 
213 /*
214  * sPAPR IRQ frontend routines for devices
215  */
216 
217 int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp)
218 {
219     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
220 
221     return smc->irq->claim(spapr, irq, lsi, errp);
222 }
223 
224 void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num)
225 {
226     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
227 
228     smc->irq->free(spapr, irq, num);
229 }
230 
231 qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq)
232 {
233     sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
234 
235     return smc->irq->qirq(spapr, irq);
236 }
237 
238 /*
239  * XICS legacy routines - to deprecate one day
240  */
241 
242 static int ics_find_free_block(ICSState *ics, int num, int alignnum)
243 {
244     int first, i;
245 
246     for (first = 0; first < ics->nr_irqs; first += alignnum) {
247         if (num > (ics->nr_irqs - first)) {
248             return -1;
249         }
250         for (i = first; i < first + num; ++i) {
251             if (!ICS_IRQ_FREE(ics, i)) {
252                 break;
253             }
254         }
255         if (i == (first + num)) {
256             return first;
257         }
258     }
259 
260     return -1;
261 }
262 
263 int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp)
264 {
265     ICSState *ics = spapr->ics;
266     int first = -1;
267 
268     assert(ics);
269 
270     /*
271      * MSIMesage::data is used for storing VIRQ so
272      * it has to be aligned to num to support multiple
273      * MSI vectors. MSI-X is not affected by this.
274      * The hint is used for the first IRQ, the rest should
275      * be allocated continuously.
276      */
277     if (align) {
278         assert((num == 1) || (num == 2) || (num == 4) ||
279                (num == 8) || (num == 16) || (num == 32));
280         first = ics_find_free_block(ics, num, num);
281     } else {
282         first = ics_find_free_block(ics, num, 1);
283     }
284 
285     if (first < 0) {
286         error_setg(errp, "can't find a free %d-IRQ block", num);
287         return -1;
288     }
289 
290     return first + ics->offset;
291 }
292 
293 #define SPAPR_IRQ_XICS_LEGACY_NR_IRQS     0x400
294 
295 sPAPRIrq spapr_irq_xics_legacy = {
296     .nr_irqs     = SPAPR_IRQ_XICS_LEGACY_NR_IRQS,
297     .nr_msis     = SPAPR_IRQ_XICS_LEGACY_NR_IRQS,
298 
299     .init        = spapr_irq_init_xics,
300     .claim       = spapr_irq_claim_xics,
301     .free        = spapr_irq_free_xics,
302     .qirq        = spapr_qirq_xics,
303     .print_info  = spapr_irq_print_info_xics,
304 };
305