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