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 "hw/ppc/fdt.h" 36 #include "qapi/visitor.h" 37 #include "qapi/error.h" 38 39 /* 40 * Guest interfaces 41 */ 42 43 static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, 44 target_ulong opcode, target_ulong *args) 45 { 46 CPUState *cs = CPU(cpu); 47 ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); 48 target_ulong cppr = args[0]; 49 50 icp_set_cppr(icp, cppr); 51 return H_SUCCESS; 52 } 53 54 static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, 55 target_ulong opcode, target_ulong *args) 56 { 57 target_ulong server = xics_get_cpu_index_by_dt_id(args[0]); 58 target_ulong mfrr = args[1]; 59 ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), server); 60 61 if (!icp) { 62 return H_PARAMETER; 63 } 64 65 icp_set_mfrr(icp, mfrr); 66 return H_SUCCESS; 67 } 68 69 static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, 70 target_ulong opcode, target_ulong *args) 71 { 72 CPUState *cs = CPU(cpu); 73 ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); 74 uint32_t xirr = icp_accept(icp); 75 76 args[0] = xirr; 77 return H_SUCCESS; 78 } 79 80 static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, 81 target_ulong opcode, target_ulong *args) 82 { 83 CPUState *cs = CPU(cpu); 84 ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); 85 uint32_t xirr = icp_accept(icp); 86 87 args[0] = xirr; 88 args[1] = cpu_get_host_ticks(); 89 return H_SUCCESS; 90 } 91 92 static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, 93 target_ulong opcode, target_ulong *args) 94 { 95 CPUState *cs = CPU(cpu); 96 ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); 97 target_ulong xirr = args[0]; 98 99 icp_eoi(icp, xirr); 100 return H_SUCCESS; 101 } 102 103 static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, 104 target_ulong opcode, target_ulong *args) 105 { 106 CPUState *cs = CPU(cpu); 107 ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); 108 uint32_t mfrr; 109 uint32_t xirr = icp_ipoll(icp, &mfrr); 110 111 args[0] = xirr; 112 args[1] = mfrr; 113 114 return H_SUCCESS; 115 } 116 117 static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, 118 uint32_t token, 119 uint32_t nargs, target_ulong args, 120 uint32_t nret, target_ulong rets) 121 { 122 ICSState *ics = spapr->ics; 123 uint32_t nr, srcno, server, priority; 124 125 if ((nargs != 3) || (nret != 1)) { 126 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 127 return; 128 } 129 if (!ics) { 130 rtas_st(rets, 0, RTAS_OUT_HW_ERROR); 131 return; 132 } 133 134 nr = rtas_ld(args, 0); 135 server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1)); 136 priority = rtas_ld(args, 2); 137 138 if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server) 139 || (priority > 0xff)) { 140 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 141 return; 142 } 143 144 srcno = nr - ics->offset; 145 ics_simple_write_xive(ics, srcno, server, priority, priority); 146 147 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 148 } 149 150 static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, 151 uint32_t token, 152 uint32_t nargs, target_ulong args, 153 uint32_t nret, target_ulong rets) 154 { 155 ICSState *ics = spapr->ics; 156 uint32_t nr, srcno; 157 158 if ((nargs != 1) || (nret != 3)) { 159 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 160 return; 161 } 162 if (!ics) { 163 rtas_st(rets, 0, RTAS_OUT_HW_ERROR); 164 return; 165 } 166 167 nr = rtas_ld(args, 0); 168 169 if (!ics_valid_irq(ics, nr)) { 170 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 171 return; 172 } 173 174 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 175 srcno = nr - ics->offset; 176 rtas_st(rets, 1, ics->irqs[srcno].server); 177 rtas_st(rets, 2, ics->irqs[srcno].priority); 178 } 179 180 static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, 181 uint32_t token, 182 uint32_t nargs, target_ulong args, 183 uint32_t nret, target_ulong rets) 184 { 185 ICSState *ics = spapr->ics; 186 uint32_t nr, srcno; 187 188 if ((nargs != 1) || (nret != 1)) { 189 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 190 return; 191 } 192 if (!ics) { 193 rtas_st(rets, 0, RTAS_OUT_HW_ERROR); 194 return; 195 } 196 197 nr = rtas_ld(args, 0); 198 199 if (!ics_valid_irq(ics, nr)) { 200 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 201 return; 202 } 203 204 srcno = nr - ics->offset; 205 ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, 0xff, 206 ics->irqs[srcno].priority); 207 208 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 209 } 210 211 static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, 212 uint32_t token, 213 uint32_t nargs, target_ulong args, 214 uint32_t nret, target_ulong rets) 215 { 216 ICSState *ics = spapr->ics; 217 uint32_t nr, srcno; 218 219 if ((nargs != 1) || (nret != 1)) { 220 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 221 return; 222 } 223 if (!ics) { 224 rtas_st(rets, 0, RTAS_OUT_HW_ERROR); 225 return; 226 } 227 228 nr = rtas_ld(args, 0); 229 230 if (!ics_valid_irq(ics, nr)) { 231 rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); 232 return; 233 } 234 235 srcno = nr - ics->offset; 236 ics_simple_write_xive(ics, srcno, ics->irqs[srcno].server, 237 ics->irqs[srcno].saved_priority, 238 ics->irqs[srcno].saved_priority); 239 240 rtas_st(rets, 0, RTAS_OUT_SUCCESS); 241 } 242 243 int xics_spapr_init(sPAPRMachineState *spapr, Error **errp) 244 { 245 /* Registration of global state belongs into realize */ 246 spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive); 247 spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive); 248 spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off); 249 spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on); 250 251 spapr_register_hypercall(H_CPPR, h_cppr); 252 spapr_register_hypercall(H_IPI, h_ipi); 253 spapr_register_hypercall(H_XIRR, h_xirr); 254 spapr_register_hypercall(H_XIRR_X, h_xirr_x); 255 spapr_register_hypercall(H_EOI, h_eoi); 256 spapr_register_hypercall(H_IPOLL, h_ipoll); 257 return 0; 258 } 259 260 #define ICS_IRQ_FREE(ics, srcno) \ 261 (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK))) 262 263 static int ics_find_free_block(ICSState *ics, int num, int alignnum) 264 { 265 int first, i; 266 267 for (first = 0; first < ics->nr_irqs; first += alignnum) { 268 if (num > (ics->nr_irqs - first)) { 269 return -1; 270 } 271 for (i = first; i < first + num; ++i) { 272 if (!ICS_IRQ_FREE(ics, i)) { 273 break; 274 } 275 } 276 if (i == (first + num)) { 277 return first; 278 } 279 } 280 281 return -1; 282 } 283 284 int spapr_ics_alloc(ICSState *ics, int irq_hint, bool lsi, Error **errp) 285 { 286 int irq; 287 288 if (!ics) { 289 return -1; 290 } 291 if (irq_hint) { 292 if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { 293 error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); 294 return -1; 295 } 296 irq = irq_hint; 297 } else { 298 irq = ics_find_free_block(ics, 1, 1); 299 if (irq < 0) { 300 error_setg(errp, "can't allocate IRQ: no IRQ left"); 301 return -1; 302 } 303 irq += ics->offset; 304 } 305 306 ics_set_irq_type(ics, irq - ics->offset, lsi); 307 trace_xics_alloc(irq); 308 309 return irq; 310 } 311 312 /* 313 * Allocate block of consecutive IRQs, and return the number of the first IRQ in 314 * the block. If align==true, aligns the first IRQ number to num. 315 */ 316 int spapr_ics_alloc_block(ICSState *ics, int num, bool lsi, 317 bool align, Error **errp) 318 { 319 int i, first = -1; 320 321 if (!ics) { 322 return -1; 323 } 324 325 /* 326 * MSIMesage::data is used for storing VIRQ so 327 * it has to be aligned to num to support multiple 328 * MSI vectors. MSI-X is not affected by this. 329 * The hint is used for the first IRQ, the rest should 330 * be allocated continuously. 331 */ 332 if (align) { 333 assert((num == 1) || (num == 2) || (num == 4) || 334 (num == 8) || (num == 16) || (num == 32)); 335 first = ics_find_free_block(ics, num, num); 336 } else { 337 first = ics_find_free_block(ics, num, 1); 338 } 339 if (first < 0) { 340 error_setg(errp, "can't find a free %d-IRQ block", num); 341 return -1; 342 } 343 344 if (first >= 0) { 345 for (i = first; i < first + num; ++i) { 346 ics_set_irq_type(ics, i, lsi); 347 } 348 } 349 first += ics->offset; 350 351 trace_xics_alloc_block(first, num, lsi, align); 352 353 return first; 354 } 355 356 static void ics_free(ICSState *ics, int srcno, int num) 357 { 358 int i; 359 360 for (i = srcno; i < srcno + num; ++i) { 361 if (ICS_IRQ_FREE(ics, i)) { 362 trace_xics_ics_free_warn(0, i + ics->offset); 363 } 364 memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); 365 } 366 } 367 368 void spapr_ics_free(ICSState *ics, int irq, int num) 369 { 370 if (ics_valid_irq(ics, irq)) { 371 trace_xics_ics_free(0, irq, num); 372 ics_free(ics, irq - ics->offset, num); 373 } 374 } 375 376 void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle) 377 { 378 uint32_t interrupt_server_ranges_prop[] = { 379 0, cpu_to_be32(nr_servers), 380 }; 381 int node; 382 383 _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); 384 385 _FDT(fdt_setprop_string(fdt, node, "device_type", 386 "PowerPC-External-Interrupt-Presentation")); 387 _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp")); 388 _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); 389 _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges", 390 interrupt_server_ranges_prop, 391 sizeof(interrupt_server_ranges_prop))); 392 _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); 393 _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle)); 394 _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle)); 395 } 396