xref: /openbmc/qemu/hw/intc/xics_spapr.c (revision ec150c7e)
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/spapr_cpu_core.h"
35 #include "hw/ppc/xics.h"
36 #include "hw/ppc/xics_spapr.h"
37 #include "hw/ppc/fdt.h"
38 #include "qapi/visitor.h"
39 
40 /*
41  * Guest interfaces
42  */
43 
44 static bool check_emulated_xics(SpaprMachineState *spapr, const char *func)
45 {
46     if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT) ||
47         kvm_irqchip_in_kernel()) {
48         error_report("pseries: %s must only be called for emulated XICS",
49                      func);
50         return false;
51     }
52 
53     return true;
54 }
55 
56 #define CHECK_EMULATED_XICS_HCALL(spapr)               \
57     do {                                               \
58         if (!check_emulated_xics((spapr), __func__)) { \
59             return H_HARDWARE;                         \
60         }                                              \
61     } while (0)
62 
63 static target_ulong h_cppr(PowerPCCPU *cpu, SpaprMachineState *spapr,
64                            target_ulong opcode, target_ulong *args)
65 {
66     target_ulong cppr = args[0];
67 
68     CHECK_EMULATED_XICS_HCALL(spapr);
69 
70     icp_set_cppr(spapr_cpu_state(cpu)->icp, cppr);
71     return H_SUCCESS;
72 }
73 
74 static target_ulong h_ipi(PowerPCCPU *cpu, SpaprMachineState *spapr,
75                           target_ulong opcode, target_ulong *args)
76 {
77     target_ulong mfrr = args[1];
78     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]);
79 
80     CHECK_EMULATED_XICS_HCALL(spapr);
81 
82     if (!icp) {
83         return H_PARAMETER;
84     }
85 
86     icp_set_mfrr(icp, mfrr);
87     return H_SUCCESS;
88 }
89 
90 static target_ulong h_xirr(PowerPCCPU *cpu, SpaprMachineState *spapr,
91                            target_ulong opcode, target_ulong *args)
92 {
93     uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp);
94 
95     CHECK_EMULATED_XICS_HCALL(spapr);
96 
97     args[0] = xirr;
98     return H_SUCCESS;
99 }
100 
101 static target_ulong h_xirr_x(PowerPCCPU *cpu, SpaprMachineState *spapr,
102                              target_ulong opcode, target_ulong *args)
103 {
104     uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp);
105 
106     CHECK_EMULATED_XICS_HCALL(spapr);
107 
108     args[0] = xirr;
109     args[1] = cpu_get_host_ticks();
110     return H_SUCCESS;
111 }
112 
113 static target_ulong h_eoi(PowerPCCPU *cpu, SpaprMachineState *spapr,
114                           target_ulong opcode, target_ulong *args)
115 {
116     target_ulong xirr = args[0];
117 
118     CHECK_EMULATED_XICS_HCALL(spapr);
119 
120     icp_eoi(spapr_cpu_state(cpu)->icp, xirr);
121     return H_SUCCESS;
122 }
123 
124 static target_ulong h_ipoll(PowerPCCPU *cpu, SpaprMachineState *spapr,
125                             target_ulong opcode, target_ulong *args)
126 {
127     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]);
128     uint32_t mfrr;
129     uint32_t xirr;
130 
131     CHECK_EMULATED_XICS_HCALL(spapr);
132 
133     if (!icp) {
134         return H_PARAMETER;
135     }
136 
137     xirr = icp_ipoll(icp, &mfrr);
138 
139     args[0] = xirr;
140     args[1] = mfrr;
141 
142     return H_SUCCESS;
143 }
144 
145 #define CHECK_EMULATED_XICS_RTAS(spapr, rets)          \
146     do {                                               \
147         if (!check_emulated_xics((spapr), __func__)) { \
148             rtas_st((rets), 0, RTAS_OUT_HW_ERROR);     \
149             return;                                    \
150         }                                              \
151     } while (0)
152 
153 static void rtas_set_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
154                           uint32_t token,
155                           uint32_t nargs, target_ulong args,
156                           uint32_t nret, target_ulong rets)
157 {
158     ICSState *ics = spapr->ics;
159     uint32_t nr, srcno, server, priority;
160 
161     CHECK_EMULATED_XICS_RTAS(spapr, rets);
162 
163     if ((nargs != 3) || (nret != 1)) {
164         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
165         return;
166     }
167     if (!ics) {
168         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
169         return;
170     }
171 
172     nr = rtas_ld(args, 0);
173     server = rtas_ld(args, 1);
174     priority = rtas_ld(args, 2);
175 
176     if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server)
177         || (priority > 0xff)) {
178         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
179         return;
180     }
181 
182     srcno = nr - ics->offset;
183     ics_simple_write_xive(ics, srcno, server, priority, priority);
184 
185     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
186 }
187 
188 static void rtas_get_xive(PowerPCCPU *cpu, SpaprMachineState *spapr,
189                           uint32_t token,
190                           uint32_t nargs, target_ulong args,
191                           uint32_t nret, target_ulong rets)
192 {
193     ICSState *ics = spapr->ics;
194     uint32_t nr, srcno;
195 
196     CHECK_EMULATED_XICS_RTAS(spapr, rets);
197 
198     if ((nargs != 1) || (nret != 3)) {
199         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
200         return;
201     }
202     if (!ics) {
203         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
204         return;
205     }
206 
207     nr = rtas_ld(args, 0);
208 
209     if (!ics_valid_irq(ics, nr)) {
210         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
211         return;
212     }
213 
214     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
215     srcno = nr - ics->offset;
216     rtas_st(rets, 1, ics->irqs[srcno].server);
217     rtas_st(rets, 2, ics->irqs[srcno].priority);
218 }
219 
220 static void rtas_int_off(PowerPCCPU *cpu, SpaprMachineState *spapr,
221                          uint32_t token,
222                          uint32_t nargs, target_ulong args,
223                          uint32_t nret, target_ulong rets)
224 {
225     ICSState *ics = spapr->ics;
226     uint32_t nr, srcno;
227 
228     CHECK_EMULATED_XICS_RTAS(spapr, rets);
229 
230     if ((nargs != 1) || (nret != 1)) {
231         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
232         return;
233     }
234     if (!ics) {
235         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
236         return;
237     }
238 
239     nr = rtas_ld(args, 0);
240 
241     if (!ics_valid_irq(ics, nr)) {
242         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
243         return;
244     }
245 
246     srcno = nr - ics->offset;
247     ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, 0xff,
248                           ics->irqs[srcno].priority);
249 
250     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
251 }
252 
253 static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr,
254                         uint32_t token,
255                         uint32_t nargs, target_ulong args,
256                         uint32_t nret, target_ulong rets)
257 {
258     ICSState *ics = spapr->ics;
259     uint32_t nr, srcno;
260 
261     CHECK_EMULATED_XICS_RTAS(spapr, rets);
262 
263     if ((nargs != 1) || (nret != 1)) {
264         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
265         return;
266     }
267     if (!ics) {
268         rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
269         return;
270     }
271 
272     nr = rtas_ld(args, 0);
273 
274     if (!ics_valid_irq(ics, nr)) {
275         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
276         return;
277     }
278 
279     srcno = nr - ics->offset;
280     ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server,
281                           ics->irqs[srcno].saved_priority,
282                           ics->irqs[srcno].saved_priority);
283 
284     rtas_st(rets, 0, RTAS_OUT_SUCCESS);
285 }
286 
287 void xics_spapr_init(SpaprMachineState *spapr)
288 {
289     spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
290     spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
291     spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
292     spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
293 
294     spapr_register_hypercall(H_CPPR, h_cppr);
295     spapr_register_hypercall(H_IPI, h_ipi);
296     spapr_register_hypercall(H_XIRR, h_xirr);
297     spapr_register_hypercall(H_XIRR_X, h_xirr_x);
298     spapr_register_hypercall(H_EOI, h_eoi);
299     spapr_register_hypercall(H_IPOLL, h_ipoll);
300 }
301 
302 void spapr_dt_xics(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt,
303                    uint32_t phandle)
304 {
305     uint32_t interrupt_server_ranges_prop[] = {
306         0, cpu_to_be32(nr_servers),
307     };
308     int node;
309 
310     _FDT(node = fdt_add_subnode(fdt, 0, XICS_NODENAME));
311 
312     _FDT(fdt_setprop_string(fdt, node, "device_type",
313                             "PowerPC-External-Interrupt-Presentation"));
314     _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp"));
315     _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0));
316     _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges",
317                      interrupt_server_ranges_prop,
318                      sizeof(interrupt_server_ranges_prop)));
319     _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2));
320     _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle));
321     _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle));
322 }
323