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