]> Git Repo - qemu.git/blob - hw/intc/xics_spapr.c
ppc/xics: Make the ICSState a list
[qemu.git] / hw / intc / xics_spapr.c
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     target_ulong cppr = args[0];
47
48     icp_set_cppr(spapr->xics, cs->cpu_index, cppr);
49     return H_SUCCESS;
50 }
51
52 static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
53                           target_ulong opcode, target_ulong *args)
54 {
55     target_ulong server = xics_get_cpu_index_by_dt_id(args[0]);
56     target_ulong mfrr = args[1];
57
58     if (server >= spapr->xics->nr_servers) {
59         return H_PARAMETER;
60     }
61
62     icp_set_mfrr(spapr->xics, server, mfrr);
63     return H_SUCCESS;
64 }
65
66 static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
67                            target_ulong opcode, target_ulong *args)
68 {
69     CPUState *cs = CPU(cpu);
70     uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
71
72     args[0] = xirr;
73     return H_SUCCESS;
74 }
75
76 static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
77                              target_ulong opcode, target_ulong *args)
78 {
79     CPUState *cs = CPU(cpu);
80     ICPState *ss = &spapr->xics->ss[cs->cpu_index];
81     uint32_t xirr = icp_accept(ss);
82
83     args[0] = xirr;
84     args[1] = cpu_get_host_ticks();
85     return H_SUCCESS;
86 }
87
88 static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
89                           target_ulong opcode, target_ulong *args)
90 {
91     CPUState *cs = CPU(cpu);
92     target_ulong xirr = args[0];
93
94     icp_eoi(spapr->xics, cs->cpu_index, xirr);
95     return H_SUCCESS;
96 }
97
98 static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
99                             target_ulong opcode, target_ulong *args)
100 {
101     CPUState *cs = CPU(cpu);
102     uint32_t mfrr;
103     uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
104
105     args[0] = xirr;
106     args[1] = mfrr;
107
108     return H_SUCCESS;
109 }
110
111 static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
112                           uint32_t token,
113                           uint32_t nargs, target_ulong args,
114                           uint32_t nret, target_ulong rets)
115 {
116     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
117     uint32_t nr, server, priority;
118
119     if ((nargs != 3) || (nret != 1)) {
120         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
121         return;
122     }
123     if (!ics) {
124         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
125         return;
126     }
127
128     nr = rtas_ld(args, 0);
129     server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
130     priority = rtas_ld(args, 2);
131
132     if (!ics_valid_irq(ics, nr) || (server >= ics->xics->nr_servers)
133         || (priority > 0xff)) {
134         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
135         return;
136     }
137
138     ics_write_xive(ics, nr, server, priority, priority);
139
140     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
141 }
142
143 static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
144                           uint32_t token,
145                           uint32_t nargs, target_ulong args,
146                           uint32_t nret, target_ulong rets)
147 {
148     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
149     uint32_t nr;
150
151     if ((nargs != 1) || (nret != 3)) {
152         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
153         return;
154     }
155     if (!ics) {
156         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
157         return;
158     }
159
160     nr = rtas_ld(args, 0);
161
162     if (!ics_valid_irq(ics, nr)) {
163         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
164         return;
165     }
166
167     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
168     rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
169     rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
170 }
171
172 static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
173                          uint32_t token,
174                          uint32_t nargs, target_ulong args,
175                          uint32_t nret, target_ulong rets)
176 {
177     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
178     uint32_t nr;
179
180     if ((nargs != 1) || (nret != 1)) {
181         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
182         return;
183     }
184     if (!ics) {
185         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
186         return;
187     }
188
189     nr = rtas_ld(args, 0);
190
191     if (!ics_valid_irq(ics, nr)) {
192         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
193         return;
194     }
195
196     ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
197                    ics->irqs[nr - ics->offset].priority);
198
199     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
200 }
201
202 static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
203                         uint32_t token,
204                         uint32_t nargs, target_ulong args,
205                         uint32_t nret, target_ulong rets)
206 {
207     ICSState *ics = QLIST_FIRST(&spapr->xics->ics);
208     uint32_t nr;
209
210     if ((nargs != 1) || (nret != 1)) {
211         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
212         return;
213     }
214     if (!ics) {
215         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
216         return;
217     }
218
219     nr = rtas_ld(args, 0);
220
221     if (!ics_valid_irq(ics, nr)) {
222         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
223         return;
224     }
225
226     ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
227                    ics->irqs[nr - ics->offset].saved_priority,
228                    ics->irqs[nr - ics->offset].saved_priority);
229
230     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
231 }
232
233 static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
234                                    Error **errp)
235 {
236     ICSState *ics = QLIST_FIRST(&xics->ics);
237
238     /* This needs to be deprecated ... */
239     xics->nr_irqs = nr_irqs;
240     if (ics) {
241         ics->nr_irqs = nr_irqs;
242     }
243 }
244
245 static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
246                                       Error **errp)
247 {
248     int i;
249
250     xics->nr_servers = nr_servers;
251
252     xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
253     for (i = 0; i < xics->nr_servers; i++) {
254         char buffer[32];
255         object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP);
256         snprintf(buffer, sizeof(buffer), "icp[%d]", i);
257         object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
258                                   errp);
259     }
260 }
261
262 static void xics_spapr_realize(DeviceState *dev, Error **errp)
263 {
264     XICSState *xics = XICS_SPAPR(dev);
265     ICSState *ics;
266     Error *error = NULL;
267     int i;
268
269     if (!xics->nr_servers) {
270         error_setg(errp, "Number of servers needs to be greater 0");
271         return;
272     }
273
274     /* Registration of global state belongs into realize */
275     spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
276     spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
277     spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
278     spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
279
280     spapr_register_hypercall(H_CPPR, h_cppr);
281     spapr_register_hypercall(H_IPI, h_ipi);
282     spapr_register_hypercall(H_XIRR, h_xirr);
283     spapr_register_hypercall(H_XIRR_X, h_xirr_x);
284     spapr_register_hypercall(H_EOI, h_eoi);
285     spapr_register_hypercall(H_IPOLL, h_ipoll);
286
287     QLIST_FOREACH(ics, &xics->ics, list) {
288         object_property_set_bool(OBJECT(ics), true, "realized", &error);
289         if (error) {
290             error_propagate(errp, error);
291             return;
292         }
293     }
294
295     for (i = 0; i < xics->nr_servers; i++) {
296         object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
297                                  &error);
298         if (error) {
299             error_propagate(errp, error);
300             return;
301         }
302     }
303 }
304
305 static void xics_spapr_initfn(Object *obj)
306 {
307     XICSState *xics = XICS_SPAPR(obj);
308     ICSState *ics;
309
310     ics = ICS(object_new(TYPE_ICS));
311     object_property_add_child(obj, "ics", OBJECT(ics), NULL);
312     ics->xics = xics;
313     QLIST_INSERT_HEAD(&xics->ics, ics, list);
314 }
315
316 static void xics_spapr_class_init(ObjectClass *oc, void *data)
317 {
318     DeviceClass *dc = DEVICE_CLASS(oc);
319     XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
320
321     dc->realize = xics_spapr_realize;
322     xsc->set_nr_irqs = xics_spapr_set_nr_irqs;
323     xsc->set_nr_servers = xics_spapr_set_nr_servers;
324 }
325
326 static const TypeInfo xics_spapr_info = {
327     .name          = TYPE_XICS_SPAPR,
328     .parent        = TYPE_XICS_COMMON,
329     .instance_size = sizeof(XICSState),
330     .class_size = sizeof(XICSStateClass),
331     .class_init    = xics_spapr_class_init,
332     .instance_init = xics_spapr_initfn,
333 };
334
335 #define ICS_IRQ_FREE(ics, srcno)   \
336     (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
337
338 static int ics_find_free_block(ICSState *ics, int num, int alignnum)
339 {
340     int first, i;
341
342     for (first = 0; first < ics->nr_irqs; first += alignnum) {
343         if (num > (ics->nr_irqs - first)) {
344             return -1;
345         }
346         for (i = first; i < first + num; ++i) {
347             if (!ICS_IRQ_FREE(ics, i)) {
348                 break;
349             }
350         }
351         if (i == (first + num)) {
352             return first;
353         }
354     }
355
356     return -1;
357 }
358
359 int xics_spapr_alloc(XICSState *xics, int irq_hint, bool lsi, Error **errp)
360 {
361     ICSState *ics = QLIST_FIRST(&xics->ics);
362     int irq;
363
364     if (!ics) {
365         return -1;
366     }
367     if (irq_hint) {
368         if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
369             error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
370             return -1;
371         }
372         irq = irq_hint;
373     } else {
374         irq = ics_find_free_block(ics, 1, 1);
375         if (irq < 0) {
376             error_setg(errp, "can't allocate IRQ: no IRQ left");
377             return -1;
378         }
379         irq += ics->offset;
380     }
381
382     ics_set_irq_type(ics, irq - ics->offset, lsi);
383     trace_xics_alloc(irq);
384
385     return irq;
386 }
387
388 /*
389  * Allocate block of consecutive IRQs, and return the number of the first IRQ in
390  * the block. If align==true, aligns the first IRQ number to num.
391  */
392 int xics_spapr_alloc_block(XICSState *xics, int num, bool lsi, bool align,
393                            Error **errp)
394 {
395     ICSState *ics = QLIST_FIRST(&xics->ics);
396     int i, first = -1;
397
398     if (!ics) {
399         return -1;
400     }
401
402     /*
403      * MSIMesage::data is used for storing VIRQ so
404      * it has to be aligned to num to support multiple
405      * MSI vectors. MSI-X is not affected by this.
406      * The hint is used for the first IRQ, the rest should
407      * be allocated continuously.
408      */
409     if (align) {
410         assert((num == 1) || (num == 2) || (num == 4) ||
411                (num == 8) || (num == 16) || (num == 32));
412         first = ics_find_free_block(ics, num, num);
413     } else {
414         first = ics_find_free_block(ics, num, 1);
415     }
416     if (first < 0) {
417         error_setg(errp, "can't find a free %d-IRQ block", num);
418         return -1;
419     }
420
421     if (first >= 0) {
422         for (i = first; i < first + num; ++i) {
423             ics_set_irq_type(ics, i, lsi);
424         }
425     }
426     first += ics->offset;
427
428     trace_xics_alloc_block(first, num, lsi, align);
429
430     return first;
431 }
432
433 static void ics_free(ICSState *ics, int srcno, int num)
434 {
435     int i;
436
437     for (i = srcno; i < srcno + num; ++i) {
438         if (ICS_IRQ_FREE(ics, i)) {
439             trace_xics_ics_free_warn(0, i + ics->offset);
440         }
441         memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
442     }
443 }
444
445 void xics_spapr_free(XICSState *xics, int irq, int num)
446 {
447     ICSState *ics = xics_find_source(xics, irq);
448
449     if (ics) {
450         trace_xics_ics_free(0, irq, num);
451         ics_free(ics, irq - ics->offset, num);
452     }
453 }
454
455 static void xics_spapr_register_types(void)
456 {
457     type_register_static(&xics_spapr_info);
458 }
459
460 type_init(xics_spapr_register_types)
This page took 0.049242 seconds and 4 git commands to generate.