xref: /openbmc/qemu/hw/intc/xics_spapr.c (revision e3403258a20c61859ca1917bb86bc206e5846784)
1 /*
2  * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
3  *
4  * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
5  *
6  * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  *
26  */
27 
28 #include "qemu/osdep.h"
29 #include "cpu.h"
30 #include "hw/hw.h"
31 #include "trace.h"
32 #include "qemu/timer.h"
33 #include "hw/ppc/spapr.h"
34 #include "hw/ppc/xics.h"
35 #include "qapi/visitor.h"
36 #include "qapi/error.h"
37 
38 /*
39  * Guest interfaces
40  */
41 
42 static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
43                            target_ulong opcode, target_ulong *args)
44 {
45     CPUState *cs = CPU(cpu);
46     ICPState *icp = &spapr->xics->ss[cs->cpu_index];
47     target_ulong cppr = args[0];
48 
49     icp_set_cppr(icp, cppr);
50     return H_SUCCESS;
51 }
52 
53 static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
54                           target_ulong opcode, target_ulong *args)
55 {
56     target_ulong server = xics_get_cpu_index_by_dt_id(args[0]);
57     target_ulong mfrr = args[1];
58 
59     if (server >= spapr->xics->nr_servers) {
60         return H_PARAMETER;
61     }
62 
63     icp_set_mfrr(spapr->xics->ss + server, mfrr);
64     return H_SUCCESS;
65 }
66 
67 static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
68                            target_ulong opcode, target_ulong *args)
69 {
70     CPUState *cs = CPU(cpu);
71     ICPState *icp = &spapr->xics->ss[cs->cpu_index];
72     uint32_t xirr = icp_accept(icp);
73 
74     args[0] = xirr;
75     return H_SUCCESS;
76 }
77 
78 static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
79                              target_ulong opcode, target_ulong *args)
80 {
81     CPUState *cs = CPU(cpu);
82     ICPState *icp = &spapr->xics->ss[cs->cpu_index];
83     uint32_t xirr = icp_accept(icp);
84 
85     args[0] = xirr;
86     args[1] = cpu_get_host_ticks();
87     return H_SUCCESS;
88 }
89 
90 static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
91                           target_ulong opcode, target_ulong *args)
92 {
93     CPUState *cs = CPU(cpu);
94     ICPState *icp = &spapr->xics->ss[cs->cpu_index];
95     target_ulong xirr = args[0];
96 
97     icp_eoi(icp, xirr);
98     return H_SUCCESS;
99 }
100 
101 static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
102                             target_ulong opcode, target_ulong *args)
103 {
104     CPUState *cs = CPU(cpu);
105     ICPState *icp = &spapr->xics->ss[cs->cpu_index];
106     uint32_t mfrr;
107     uint32_t xirr = icp_ipoll(icp, &mfrr);
108 
109     args[0] = xirr;
110     args[1] = mfrr;
111 
112     return H_SUCCESS;
113 }
114 
115 static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
116                           uint32_t token,
117                           uint32_t nargs, target_ulong args,
118                           uint32_t nret, target_ulong rets)
119 {
120     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
121     uint32_t nr, srcno, server, priority;
122 
123     if ((nargs != 3) || (nret != 1)) {
124         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
125         return;
126     }
127     if (!ics) {
128         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
129         return;
130     }
131 
132     nr = rtas_ld(args, 0);
133     server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
134     priority = rtas_ld(args, 2);
135 
136     if (!ics_valid_irq(ics, nr) || (server >= ics->xics->nr_servers)
137         || (priority > 0xff)) {
138         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
139         return;
140     }
141 
142     srcno = nr - ics->offset;
143     ics_simple_write_xive(ics, srcno, server, priority, priority);
144 
145     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
146 }
147 
148 static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
149                           uint32_t token,
150                           uint32_t nargs, target_ulong args,
151                           uint32_t nret, target_ulong rets)
152 {
153     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
154     uint32_t nr, srcno;
155 
156     if ((nargs != 1) || (nret != 3)) {
157         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
158         return;
159     }
160     if (!ics) {
161         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
162         return;
163     }
164 
165     nr = rtas_ld(args, 0);
166 
167     if (!ics_valid_irq(ics, nr)) {
168         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
169         return;
170     }
171 
172     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
173     srcno = nr - ics->offset;
174     rtas_st(rets, 1, ics->irqs[srcno].server);
175     rtas_st(rets, 2, ics->irqs[srcno].priority);
176 }
177 
178 static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
179                          uint32_t token,
180                          uint32_t nargs, target_ulong args,
181                          uint32_t nret, target_ulong rets)
182 {
183     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
184     uint32_t nr, srcno;
185 
186     if ((nargs != 1) || (nret != 1)) {
187         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
188         return;
189     }
190     if (!ics) {
191         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
192         return;
193     }
194 
195     nr = rtas_ld(args, 0);
196 
197     if (!ics_valid_irq(ics, nr)) {
198         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
199         return;
200     }
201 
202     srcno = nr - ics->offset;
203     ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, 0xff,
204                           ics->irqs[srcno].priority);
205 
206     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
207 }
208 
209 static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
210                         uint32_t token,
211                         uint32_t nargs, target_ulong args,
212                         uint32_t nret, target_ulong rets)
213 {
214     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
215     uint32_t nr, srcno;
216 
217     if ((nargs != 1) || (nret != 1)) {
218         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
219         return;
220     }
221     if (!ics) {
222         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
223         return;
224     }
225 
226     nr = rtas_ld(args, 0);
227 
228     if (!ics_valid_irq(ics, nr)) {
229         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
230         return;
231     }
232 
233     srcno = nr - ics->offset;
234     ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server,
235                           ics->irqs[srcno].saved_priority,
236                           ics->irqs[srcno].saved_priority);
237 
238     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
239 }
240 
241 static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
242                                    Error **errp)
243 {
244     ICSState *ics = QLIST_FIRST(&xics->ics);
245 
246     /* This needs to be deprecated ... */
247     xics->nr_irqs = nr_irqs;
248     if (ics) {
249         ics->nr_irqs = nr_irqs;
250     }
251 }
252 
253 static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
254                                       Error **errp)
255 {
256     xics_set_nr_servers(xics, nr_servers, TYPE_ICP, errp);
257 }
258 
259 static void xics_spapr_realize(DeviceState *dev, Error **errp)
260 {
261     XICSState *xics = XICS_SPAPR(dev);
262     ICSState *ics;
263     Error *error = NULL;
264     int i;
265 
266     if (!xics->nr_servers) {
267         error_setg(errp, "Number of servers needs to be greater 0");
268         return;
269     }
270 
271     /* Registration of global state belongs into realize */
272     spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
273     spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
274     spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
275     spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
276 
277     spapr_register_hypercall(H_CPPR, h_cppr);
278     spapr_register_hypercall(H_IPI, h_ipi);
279     spapr_register_hypercall(H_XIRR, h_xirr);
280     spapr_register_hypercall(H_XIRR_X, h_xirr_x);
281     spapr_register_hypercall(H_EOI, h_eoi);
282     spapr_register_hypercall(H_IPOLL, h_ipoll);
283 
284     QLIST_FOREACH(ics, &xics->ics, list) {
285         object_property_set_bool(OBJECT(ics), true, "realized", &error);
286         if (error) {
287             error_propagate(errp, error);
288             return;
289         }
290     }
291 
292     for (i = 0; i < xics->nr_servers; i++) {
293         object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
294                                  &error);
295         if (error) {
296             error_propagate(errp, error);
297             return;
298         }
299     }
300 }
301 
302 static void xics_spapr_initfn(Object *obj)
303 {
304     XICSState *xics = XICS_SPAPR(obj);
305     ICSState *ics;
306 
307     ics = ICS_SIMPLE(object_new(TYPE_ICS_SIMPLE));
308     object_property_add_child(obj, "ics", OBJECT(ics), NULL);
309     ics->xics = xics;
310     QLIST_INSERT_HEAD(&xics->ics, ics, list);
311 }
312 
313 static void xics_spapr_class_init(ObjectClass *oc, void *data)
314 {
315     DeviceClass *dc = DEVICE_CLASS(oc);
316     XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
317 
318     dc->realize = xics_spapr_realize;
319     xsc->set_nr_irqs = xics_spapr_set_nr_irqs;
320     xsc->set_nr_servers = xics_spapr_set_nr_servers;
321 }
322 
323 static const TypeInfo xics_spapr_info = {
324     .name          = TYPE_XICS_SPAPR,
325     .parent        = TYPE_XICS_COMMON,
326     .instance_size = sizeof(XICSState),
327     .class_size = sizeof(XICSStateClass),
328     .class_init    = xics_spapr_class_init,
329     .instance_init = xics_spapr_initfn,
330 };
331 
332 #define ICS_IRQ_FREE(ics, srcno)   \
333     (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
334 
335 static int ics_find_free_block(ICSState *ics, int num, int alignnum)
336 {
337     int first, i;
338 
339     for (first = 0; first < ics->nr_irqs; first += alignnum) {
340         if (num > (ics->nr_irqs - first)) {
341             return -1;
342         }
343         for (i = first; i < first + num; ++i) {
344             if (!ICS_IRQ_FREE(ics, i)) {
345                 break;
346             }
347         }
348         if (i == (first + num)) {
349             return first;
350         }
351     }
352 
353     return -1;
354 }
355 
356 int xics_spapr_alloc(XICSState *xics, int irq_hint, bool lsi, Error **errp)
357 {
358     ICSState *ics = QLIST_FIRST(&xics->ics);
359     int irq;
360 
361     if (!ics) {
362         return -1;
363     }
364     if (irq_hint) {
365         if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
366             error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
367             return -1;
368         }
369         irq = irq_hint;
370     } else {
371         irq = ics_find_free_block(ics, 1, 1);
372         if (irq < 0) {
373             error_setg(errp, "can't allocate IRQ: no IRQ left");
374             return -1;
375         }
376         irq += ics->offset;
377     }
378 
379     ics_set_irq_type(ics, irq - ics->offset, lsi);
380     trace_xics_alloc(irq);
381 
382     return irq;
383 }
384 
385 /*
386  * Allocate block of consecutive IRQs, and return the number of the first IRQ in
387  * the block. If align==true, aligns the first IRQ number to num.
388  */
389 int xics_spapr_alloc_block(XICSState *xics, int num, bool lsi, bool align,
390                            Error **errp)
391 {
392     ICSState *ics = QLIST_FIRST(&xics->ics);
393     int i, first = -1;
394 
395     if (!ics) {
396         return -1;
397     }
398 
399     /*
400      * MSIMesage::data is used for storing VIRQ so
401      * it has to be aligned to num to support multiple
402      * MSI vectors. MSI-X is not affected by this.
403      * The hint is used for the first IRQ, the rest should
404      * be allocated continuously.
405      */
406     if (align) {
407         assert((num == 1) || (num == 2) || (num == 4) ||
408                (num == 8) || (num == 16) || (num == 32));
409         first = ics_find_free_block(ics, num, num);
410     } else {
411         first = ics_find_free_block(ics, num, 1);
412     }
413     if (first < 0) {
414         error_setg(errp, "can't find a free %d-IRQ block", num);
415         return -1;
416     }
417 
418     if (first >= 0) {
419         for (i = first; i < first + num; ++i) {
420             ics_set_irq_type(ics, i, lsi);
421         }
422     }
423     first += ics->offset;
424 
425     trace_xics_alloc_block(first, num, lsi, align);
426 
427     return first;
428 }
429 
430 static void ics_free(ICSState *ics, int srcno, int num)
431 {
432     int i;
433 
434     for (i = srcno; i < srcno + num; ++i) {
435         if (ICS_IRQ_FREE(ics, i)) {
436             trace_xics_ics_free_warn(0, i + ics->offset);
437         }
438         memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
439     }
440 }
441 
442 void xics_spapr_free(XICSState *xics, int irq, int num)
443 {
444     ICSState *ics = xics_find_source(xics, irq);
445 
446     if (ics) {
447         trace_xics_ics_free(0, irq, num);
448         ics_free(ics, irq - ics->offset, num);
449     }
450 }
451 
452 static void xics_spapr_register_types(void)
453 {
454     type_register_static(&xics_spapr_info);
455 }
456 
457 type_init(xics_spapr_register_types)
458