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 static void xics_spapr_realize(DeviceState *dev, 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 } 258 259 static void xics_spapr_class_init(ObjectClass *oc, void *data) 260 { 261 DeviceClass *dc = DEVICE_CLASS(oc); 262 263 dc->realize = xics_spapr_realize; 264 } 265 266 static const TypeInfo xics_spapr_info = { 267 .name = TYPE_XICS_SPAPR, 268 .parent = TYPE_XICS_COMMON, 269 .instance_size = sizeof(XICSState), 270 .class_size = sizeof(XICSStateClass), 271 .class_init = xics_spapr_class_init, 272 }; 273 274 #define ICS_IRQ_FREE(ics, srcno) \ 275 (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK))) 276 277 static int ics_find_free_block(ICSState *ics, int num, int alignnum) 278 { 279 int first, i; 280 281 for (first = 0; first < ics->nr_irqs; first += alignnum) { 282 if (num > (ics->nr_irqs - first)) { 283 return -1; 284 } 285 for (i = first; i < first + num; ++i) { 286 if (!ICS_IRQ_FREE(ics, i)) { 287 break; 288 } 289 } 290 if (i == (first + num)) { 291 return first; 292 } 293 } 294 295 return -1; 296 } 297 298 int spapr_ics_alloc(ICSState *ics, int irq_hint, bool lsi, Error **errp) 299 { 300 int irq; 301 302 if (!ics) { 303 return -1; 304 } 305 if (irq_hint) { 306 if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { 307 error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); 308 return -1; 309 } 310 irq = irq_hint; 311 } else { 312 irq = ics_find_free_block(ics, 1, 1); 313 if (irq < 0) { 314 error_setg(errp, "can't allocate IRQ: no IRQ left"); 315 return -1; 316 } 317 irq += ics->offset; 318 } 319 320 ics_set_irq_type(ics, irq - ics->offset, lsi); 321 trace_xics_alloc(irq); 322 323 return irq; 324 } 325 326 /* 327 * Allocate block of consecutive IRQs, and return the number of the first IRQ in 328 * the block. If align==true, aligns the first IRQ number to num. 329 */ 330 int spapr_ics_alloc_block(ICSState *ics, int num, bool lsi, 331 bool align, Error **errp) 332 { 333 int i, first = -1; 334 335 if (!ics) { 336 return -1; 337 } 338 339 /* 340 * MSIMesage::data is used for storing VIRQ so 341 * it has to be aligned to num to support multiple 342 * MSI vectors. MSI-X is not affected by this. 343 * The hint is used for the first IRQ, the rest should 344 * be allocated continuously. 345 */ 346 if (align) { 347 assert((num == 1) || (num == 2) || (num == 4) || 348 (num == 8) || (num == 16) || (num == 32)); 349 first = ics_find_free_block(ics, num, num); 350 } else { 351 first = ics_find_free_block(ics, num, 1); 352 } 353 if (first < 0) { 354 error_setg(errp, "can't find a free %d-IRQ block", num); 355 return -1; 356 } 357 358 if (first >= 0) { 359 for (i = first; i < first + num; ++i) { 360 ics_set_irq_type(ics, i, lsi); 361 } 362 } 363 first += ics->offset; 364 365 trace_xics_alloc_block(first, num, lsi, align); 366 367 return first; 368 } 369 370 static void ics_free(ICSState *ics, int srcno, int num) 371 { 372 int i; 373 374 for (i = srcno; i < srcno + num; ++i) { 375 if (ICS_IRQ_FREE(ics, i)) { 376 trace_xics_ics_free_warn(0, i + ics->offset); 377 } 378 memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); 379 } 380 } 381 382 void spapr_ics_free(ICSState *ics, int irq, int num) 383 { 384 if (ics_valid_irq(ics, irq)) { 385 trace_xics_ics_free(0, irq, num); 386 ics_free(ics, irq - ics->offset, num); 387 } 388 } 389 390 void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle) 391 { 392 uint32_t interrupt_server_ranges_prop[] = { 393 0, cpu_to_be32(xics->nr_servers), 394 }; 395 int node; 396 397 _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); 398 399 _FDT(fdt_setprop_string(fdt, node, "device_type", 400 "PowerPC-External-Interrupt-Presentation")); 401 _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp")); 402 _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); 403 _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges", 404 interrupt_server_ranges_prop, 405 sizeof(interrupt_server_ranges_prop))); 406 _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); 407 _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle)); 408 _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle)); 409 } 410 411 static void xics_spapr_register_types(void) 412 { 413 type_register_static(&xics_spapr_info); 414 } 415 416 type_init(xics_spapr_register_types) 417