qemu/hw/ppc/spapr_irq.c
<<
>>
Prefs
   1/*
   2 * QEMU PowerPC sPAPR IRQ interface
   3 *
   4 * Copyright (c) 2018, IBM Corporation.
   5 *
   6 * This code is licensed under the GPL version 2 or later. See the
   7 * COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qemu/log.h"
  12#include "qemu/error-report.h"
  13#include "qapi/error.h"
  14#include "hw/ppc/spapr.h"
  15#include "hw/ppc/xics.h"
  16#include "sysemu/kvm.h"
  17
  18#include "trace.h"
  19
  20void spapr_irq_msi_init(sPAPRMachineState *spapr, uint32_t nr_msis)
  21{
  22    spapr->irq_map_nr = nr_msis;
  23    spapr->irq_map = bitmap_new(spapr->irq_map_nr);
  24}
  25
  26int spapr_irq_msi_alloc(sPAPRMachineState *spapr, uint32_t num, bool align,
  27                        Error **errp)
  28{
  29    int irq;
  30
  31    /*
  32     * The 'align_mask' parameter of bitmap_find_next_zero_area()
  33     * should be one less than a power of 2; 0 means no
  34     * alignment. Adapt the 'align' value of the former allocator
  35     * to fit the requirements of bitmap_find_next_zero_area()
  36     */
  37    align -= 1;
  38
  39    irq = bitmap_find_next_zero_area(spapr->irq_map, spapr->irq_map_nr, 0, num,
  40                                     align);
  41    if (irq == spapr->irq_map_nr) {
  42        error_setg(errp, "can't find a free %d-IRQ block", num);
  43        return -1;
  44    }
  45
  46    bitmap_set(spapr->irq_map, irq, num);
  47
  48    return irq + SPAPR_IRQ_MSI;
  49}
  50
  51void spapr_irq_msi_free(sPAPRMachineState *spapr, int irq, uint32_t num)
  52{
  53    bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num);
  54}
  55
  56void spapr_irq_msi_reset(sPAPRMachineState *spapr)
  57{
  58    bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr);
  59}
  60
  61
  62/*
  63 * XICS IRQ backend.
  64 */
  65
  66static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
  67                                  const char *type_ics,
  68                                  int nr_irqs, Error **errp)
  69{
  70    Error *local_err = NULL;
  71    Object *obj;
  72
  73    obj = object_new(type_ics);
  74    object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
  75    object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr),
  76                                   &error_abort);
  77    object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err);
  78    if (local_err) {
  79        goto error;
  80    }
  81    object_property_set_bool(obj, true, "realized", &local_err);
  82    if (local_err) {
  83        goto error;
  84    }
  85
  86    return ICS_BASE(obj);
  87
  88error:
  89    error_propagate(errp, local_err);
  90    return NULL;
  91}
  92
  93static void spapr_irq_init_xics(sPAPRMachineState *spapr, Error **errp)
  94{
  95    MachineState *machine = MACHINE(spapr);
  96    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
  97    int nr_irqs = smc->irq->nr_irqs;
  98    Error *local_err = NULL;
  99
 100    /* Initialize the MSI IRQ allocator. */
 101    if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
 102        spapr_irq_msi_init(spapr, smc->irq->nr_msis);
 103    }
 104
 105    if (kvm_enabled()) {
 106        if (machine_kernel_irqchip_allowed(machine) &&
 107            !xics_kvm_init(spapr, &local_err)) {
 108            spapr->icp_type = TYPE_KVM_ICP;
 109            spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs,
 110                                          &local_err);
 111        }
 112        if (machine_kernel_irqchip_required(machine) && !spapr->ics) {
 113            error_prepend(&local_err,
 114                          "kernel_irqchip requested but unavailable: ");
 115            goto error;
 116        }
 117        error_free(local_err);
 118        local_err = NULL;
 119    }
 120
 121    if (!spapr->ics) {
 122        xics_spapr_init(spapr);
 123        spapr->icp_type = TYPE_ICP;
 124        spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs,
 125                                      &local_err);
 126    }
 127
 128error:
 129    error_propagate(errp, local_err);
 130}
 131
 132#define ICS_IRQ_FREE(ics, srcno)   \
 133    (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
 134
 135static int spapr_irq_claim_xics(sPAPRMachineState *spapr, int irq, bool lsi,
 136                                Error **errp)
 137{
 138    ICSState *ics = spapr->ics;
 139
 140    assert(ics);
 141
 142    if (!ics_valid_irq(ics, irq)) {
 143        error_setg(errp, "IRQ %d is invalid", irq);
 144        return -1;
 145    }
 146
 147    if (!ICS_IRQ_FREE(ics, irq - ics->offset)) {
 148        error_setg(errp, "IRQ %d is not free", irq);
 149        return -1;
 150    }
 151
 152    ics_set_irq_type(ics, irq - ics->offset, lsi);
 153    return 0;
 154}
 155
 156static void spapr_irq_free_xics(sPAPRMachineState *spapr, int irq, int num)
 157{
 158    ICSState *ics = spapr->ics;
 159    uint32_t srcno = irq - ics->offset;
 160    int i;
 161
 162    if (ics_valid_irq(ics, irq)) {
 163        trace_spapr_irq_free(0, irq, num);
 164        for (i = srcno; i < srcno + num; ++i) {
 165            if (ICS_IRQ_FREE(ics, i)) {
 166                trace_spapr_irq_free_warn(0, i);
 167            }
 168            memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
 169        }
 170    }
 171}
 172
 173static qemu_irq spapr_qirq_xics(sPAPRMachineState *spapr, int irq)
 174{
 175    ICSState *ics = spapr->ics;
 176    uint32_t srcno = irq - ics->offset;
 177
 178    if (ics_valid_irq(ics, irq)) {
 179        return ics->qirqs[srcno];
 180    }
 181
 182    return NULL;
 183}
 184
 185static void spapr_irq_print_info_xics(sPAPRMachineState *spapr, Monitor *mon)
 186{
 187    CPUState *cs;
 188
 189    CPU_FOREACH(cs) {
 190        PowerPCCPU *cpu = POWERPC_CPU(cs);
 191
 192        icp_pic_print_info(ICP(cpu->intc), mon);
 193    }
 194
 195    ics_pic_print_info(spapr->ics, mon);
 196}
 197
 198#define SPAPR_IRQ_XICS_NR_IRQS     0x1000
 199#define SPAPR_IRQ_XICS_NR_MSIS     \
 200    (XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
 201
 202sPAPRIrq spapr_irq_xics = {
 203    .nr_irqs     = SPAPR_IRQ_XICS_NR_IRQS,
 204    .nr_msis     = SPAPR_IRQ_XICS_NR_MSIS,
 205
 206    .init        = spapr_irq_init_xics,
 207    .claim       = spapr_irq_claim_xics,
 208    .free        = spapr_irq_free_xics,
 209    .qirq        = spapr_qirq_xics,
 210    .print_info  = spapr_irq_print_info_xics,
 211};
 212
 213/*
 214 * sPAPR IRQ frontend routines for devices
 215 */
 216
 217int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp)
 218{
 219    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
 220
 221    return smc->irq->claim(spapr, irq, lsi, errp);
 222}
 223
 224void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num)
 225{
 226    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
 227
 228    smc->irq->free(spapr, irq, num);
 229}
 230
 231qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq)
 232{
 233    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
 234
 235    return smc->irq->qirq(spapr, irq);
 236}
 237
 238/*
 239 * XICS legacy routines - to deprecate one day
 240 */
 241
 242static int ics_find_free_block(ICSState *ics, int num, int alignnum)
 243{
 244    int first, i;
 245
 246    for (first = 0; first < ics->nr_irqs; first += alignnum) {
 247        if (num > (ics->nr_irqs - first)) {
 248            return -1;
 249        }
 250        for (i = first; i < first + num; ++i) {
 251            if (!ICS_IRQ_FREE(ics, i)) {
 252                break;
 253            }
 254        }
 255        if (i == (first + num)) {
 256            return first;
 257        }
 258    }
 259
 260    return -1;
 261}
 262
 263int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp)
 264{
 265    ICSState *ics = spapr->ics;
 266    int first = -1;
 267
 268    assert(ics);
 269
 270    /*
 271     * MSIMesage::data is used for storing VIRQ so
 272     * it has to be aligned to num to support multiple
 273     * MSI vectors. MSI-X is not affected by this.
 274     * The hint is used for the first IRQ, the rest should
 275     * be allocated continuously.
 276     */
 277    if (align) {
 278        assert((num == 1) || (num == 2) || (num == 4) ||
 279               (num == 8) || (num == 16) || (num == 32));
 280        first = ics_find_free_block(ics, num, num);
 281    } else {
 282        first = ics_find_free_block(ics, num, 1);
 283    }
 284
 285    if (first < 0) {
 286        error_setg(errp, "can't find a free %d-IRQ block", num);
 287        return -1;
 288    }
 289
 290    return first + ics->offset;
 291}
 292
 293#define SPAPR_IRQ_XICS_LEGACY_NR_IRQS     0x400
 294
 295sPAPRIrq spapr_irq_xics_legacy = {
 296    .nr_irqs     = SPAPR_IRQ_XICS_LEGACY_NR_IRQS,
 297    .nr_msis     = SPAPR_IRQ_XICS_LEGACY_NR_IRQS,
 298
 299    .init        = spapr_irq_init_xics,
 300    .claim       = spapr_irq_claim_xics,
 301    .free        = spapr_irq_free_xics,
 302    .qirq        = spapr_qirq_xics,
 303    .print_info  = spapr_irq_print_info_xics,
 304};
 305