Message ID | 20181209194610.29727-6-clg@kaod.org |
---|---|
State | New |
Headers | show |
Series | ppc: support for the XIVE interrupt controller (POWER9) | expand |
On Sun, Dec 09, 2018 at 08:45:56PM +0100, Cédric Le Goater wrote: > sPAPRXive models the XIVE interrupt controller of the sPAPR machine. > It inherits from the XiveRouter and provisions storage for the routing > tables : > > - Event Assignment Structure (EAS) > - Event Notification Descriptor (END) > > The sPAPRXive model incorporates an internal XiveSource for the IPIs > and for the interrupts of the virtual devices of the guest. This model > is consistent with XIVE architecture which also incorporates an > internal IVSE for IPIs and accelerator interrupts in the IVRE > sub-engine. > > The sPAPRXive model exports two memory regions, one for the ESB > trigger and management pages used to control the sources and one for > the TIMA pages. They are mapped by default at the addresses found on > chip 0 of a baremetal system. This is also consistent with the XIVE > architecture which defines a Virtualization Controller BAR for the > internal IVSE ESB pages and a Thread Managment BAR for the TIMA. > > Signed-off-by: Cédric Le Goater <clg@kaod.org> > Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Applied. > --- > default-configs/ppc64-softmmu.mak | 1 + > include/hw/ppc/spapr_xive.h | 45 ++++ > hw/intc/spapr_xive.c | 366 ++++++++++++++++++++++++++++++ > hw/intc/Makefile.objs | 1 + > 4 files changed, 413 insertions(+) > create mode 100644 include/hw/ppc/spapr_xive.h > create mode 100644 hw/intc/spapr_xive.c > > diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak > index 2d1e7c5c4668..7f34ad0528ed 100644 > --- a/default-configs/ppc64-softmmu.mak > +++ b/default-configs/ppc64-softmmu.mak > @@ -17,6 +17,7 @@ CONFIG_XICS=$(CONFIG_PSERIES) > CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) > CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM)) > CONFIG_XIVE=$(CONFIG_PSERIES) > +CONFIG_XIVE_SPAPR=$(CONFIG_PSERIES) > CONFIG_MEM_DEVICE=y > CONFIG_DIMM=y > CONFIG_SPAPR_RNG=y > diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h > new file mode 100644 > index 000000000000..f087959b9924 > --- /dev/null > +++ b/include/hw/ppc/spapr_xive.h > @@ -0,0 +1,45 @@ > +/* > + * QEMU PowerPC sPAPR XIVE interrupt controller model > + * > + * Copyright (c) 2017-2018, IBM Corporation. > + * > + * This code is licensed under the GPL version 2 or later. See the > + * COPYING file in the top-level directory. > + */ > + > +#ifndef PPC_SPAPR_XIVE_H > +#define PPC_SPAPR_XIVE_H > + > +#include "hw/ppc/xive.h" > + > +#define TYPE_SPAPR_XIVE "spapr-xive" > +#define SPAPR_XIVE(obj) OBJECT_CHECK(sPAPRXive, (obj), TYPE_SPAPR_XIVE) > + > +typedef struct sPAPRXive { > + XiveRouter parent; > + > + /* Internal interrupt source for IPIs and virtual devices */ > + XiveSource source; > + hwaddr vc_base; > + > + /* END ESB MMIOs */ > + XiveENDSource end_source; > + hwaddr end_base; > + > + /* Routing table */ > + XiveEAS *eat; > + uint32_t nr_irqs; > + XiveEND *endt; > + uint32_t nr_ends; > + > + /* TIMA mapping address */ > + hwaddr tm_base; > + MemoryRegion tm_mmio; > +} sPAPRXive; > + > +bool spapr_xive_irq_claim(sPAPRXive *xive, uint32_t lisn, bool lsi); > +bool spapr_xive_irq_free(sPAPRXive *xive, uint32_t lisn); > +void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon); > +qemu_irq spapr_xive_qirq(sPAPRXive *xive, uint32_t lisn); > + > +#endif /* PPC_SPAPR_XIVE_H */ > diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c > new file mode 100644 > index 000000000000..eef5830d45c6 > --- /dev/null > +++ b/hw/intc/spapr_xive.c > @@ -0,0 +1,366 @@ > +/* > + * QEMU PowerPC sPAPR XIVE interrupt controller model > + * > + * Copyright (c) 2017-2018, IBM Corporation. > + * > + * This code is licensed under the GPL version 2 or later. See the > + * COPYING file in the top-level directory. > + */ > + > +#include "qemu/osdep.h" > +#include "qemu/log.h" > +#include "qapi/error.h" > +#include "qemu/error-report.h" > +#include "target/ppc/cpu.h" > +#include "sysemu/cpus.h" > +#include "monitor/monitor.h" > +#include "hw/ppc/spapr.h" > +#include "hw/ppc/spapr_xive.h" > +#include "hw/ppc/xive.h" > +#include "hw/ppc/xive_regs.h" > + > +/* > + * XIVE Virtualization Controller BAR and Thread Managment BAR that we > + * use for the ESB pages and the TIMA pages > + */ > +#define SPAPR_XIVE_VC_BASE 0x0006010000000000ull > +#define SPAPR_XIVE_TM_BASE 0x0006030203180000ull > + > +/* > + * On sPAPR machines, use a simplified output for the XIVE END > + * structure dumping only the information related to the OS EQ. > + */ > +static void spapr_xive_end_pic_print_info(sPAPRXive *xive, XiveEND *end, > + Monitor *mon) > +{ > + uint32_t qindex = GETFIELD_BE32(END_W1_PAGE_OFF, end->w1); > + uint32_t qgen = GETFIELD_BE32(END_W1_GENERATION, end->w1); > + uint32_t qsize = GETFIELD_BE32(END_W0_QSIZE, end->w0); > + uint32_t qentries = 1 << (qsize + 10); > + uint32_t nvt = GETFIELD_BE32(END_W6_NVT_INDEX, end->w6); > + uint8_t priority = GETFIELD_BE32(END_W7_F0_PRIORITY, end->w7); > + > + monitor_printf(mon, "%3d/%d % 6d/%5d ^%d", nvt, > + priority, qindex, qentries, qgen); > + > + xive_end_queue_pic_print_info(end, 6, mon); > + monitor_printf(mon, "]"); > +} > + > +void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon) > +{ > + XiveSource *xsrc = &xive->source; > + int i; > + > + monitor_printf(mon, " LSIN PQ EISN CPU/PRIO EQ\n"); > + > + for (i = 0; i < xive->nr_irqs; i++) { > + uint8_t pq = xive_source_esb_get(xsrc, i); > + XiveEAS *eas = &xive->eat[i]; > + > + if (!xive_eas_is_valid(eas)) { > + continue; > + } > + > + monitor_printf(mon, " %08x %s %c%c%c %s %08x ", i, > + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", > + pq & XIVE_ESB_VAL_P ? 'P' : '-', > + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', > + xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ', > + xive_eas_is_masked(eas) ? "M" : " ", > + (int) GETFIELD_BE64(EAS_END_DATA, eas->w)); > + > + if (!xive_eas_is_masked(eas)) { > + uint32_t end_idx = GETFIELD_BE64(EAS_END_INDEX, eas->w); > + XiveEND *end; > + > + assert(end_idx < xive->nr_ends); > + end = &xive->endt[end_idx]; > + > + if (xive_end_is_valid(end)) { > + spapr_xive_end_pic_print_info(xive, end, mon); > + } > + } > + monitor_printf(mon, "\n"); > + } > +} > + > +static void spapr_xive_map_mmio(sPAPRXive *xive) > +{ > + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base); > + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base); > + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base); > +} > + > +static void spapr_xive_end_reset(XiveEND *end) > +{ > + memset(end, 0, sizeof(*end)); > + > + /* switch off the escalation and notification ESBs */ > + end->w1 = cpu_to_be32(END_W1_ESe_Q | END_W1_ESn_Q); > +} > + > +static void spapr_xive_reset(void *dev) > +{ > + sPAPRXive *xive = SPAPR_XIVE(dev); > + int i; > + > + /* > + * The XiveSource has its own reset handler, which mask off all > + * IRQs (!P|Q) > + */ > + > + /* Mask all valid EASs in the IRQ number space. */ > + for (i = 0; i < xive->nr_irqs; i++) { > + XiveEAS *eas = &xive->eat[i]; > + if (xive_eas_is_valid(eas)) { > + eas->w = cpu_to_be64(EAS_VALID | EAS_MASKED); > + } else { > + eas->w = 0; > + } > + } > + > + /* Clear all ENDs */ > + for (i = 0; i < xive->nr_ends; i++) { > + spapr_xive_end_reset(&xive->endt[i]); > + } > +} > + > +static void spapr_xive_instance_init(Object *obj) > +{ > + sPAPRXive *xive = SPAPR_XIVE(obj); > + > + object_initialize(&xive->source, sizeof(xive->source), TYPE_XIVE_SOURCE); > + object_property_add_child(obj, "source", OBJECT(&xive->source), NULL); > + > + object_initialize(&xive->end_source, sizeof(xive->end_source), > + TYPE_XIVE_END_SOURCE); > + object_property_add_child(obj, "end_source", OBJECT(&xive->end_source), > + NULL); > +} > + > +static void spapr_xive_realize(DeviceState *dev, Error **errp) > +{ > + sPAPRXive *xive = SPAPR_XIVE(dev); > + XiveSource *xsrc = &xive->source; > + XiveENDSource *end_xsrc = &xive->end_source; > + Error *local_err = NULL; > + > + if (!xive->nr_irqs) { > + error_setg(errp, "Number of interrupt needs to be greater 0"); > + return; > + } > + > + if (!xive->nr_ends) { > + error_setg(errp, "Number of interrupt needs to be greater 0"); > + return; > + } > + > + /* > + * Initialize the internal sources, for IPIs and virtual devices. > + */ > + object_property_set_int(OBJECT(xsrc), xive->nr_irqs, "nr-irqs", > + &error_fatal); > + object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive), > + &error_fatal); > + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); > + if (local_err) { > + error_propagate(errp, local_err); > + return; > + } > + > + /* > + * Initialize the END ESB source > + */ > + object_property_set_int(OBJECT(end_xsrc), xive->nr_irqs, "nr-ends", > + &error_fatal); > + object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive), > + &error_fatal); > + object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err); > + if (local_err) { > + error_propagate(errp, local_err); > + return; > + } > + > + /* Set the mapping address of the END ESB pages after the source ESBs */ > + xive->end_base = xive->vc_base + (1ull << xsrc->esb_shift) * xsrc->nr_irqs; > + > + /* > + * Allocate the routing tables > + */ > + xive->eat = g_new0(XiveEAS, xive->nr_irqs); > + xive->endt = g_new0(XiveEND, xive->nr_ends); > + > + /* TIMA initialization */ > + memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive, > + "xive.tima", 4ull << TM_SHIFT); > + > + /* Define all XIVE MMIO regions on SysBus */ > + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio); > + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio); > + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio); > + > + /* Map all regions */ > + spapr_xive_map_mmio(xive); > + > + qemu_register_reset(spapr_xive_reset, dev); > +} > + > +static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk, > + uint32_t eas_idx, XiveEAS *eas) > +{ > + sPAPRXive *xive = SPAPR_XIVE(xrtr); > + > + if (eas_idx >= xive->nr_irqs) { > + return -1; > + } > + > + *eas = xive->eat[eas_idx]; > + return 0; > +} > + > +static int spapr_xive_get_end(XiveRouter *xrtr, > + uint8_t end_blk, uint32_t end_idx, XiveEND *end) > +{ > + sPAPRXive *xive = SPAPR_XIVE(xrtr); > + > + if (end_idx >= xive->nr_ends) { > + return -1; > + } > + > + memcpy(end, &xive->endt[end_idx], sizeof(XiveEND)); > + return 0; > +} > + > +static int spapr_xive_write_end(XiveRouter *xrtr, uint8_t end_blk, > + uint32_t end_idx, XiveEND *end, > + uint8_t word_number) > +{ > + sPAPRXive *xive = SPAPR_XIVE(xrtr); > + > + if (end_idx >= xive->nr_ends) { > + return -1; > + } > + > + memcpy(&xive->endt[end_idx], end, sizeof(XiveEND)); > + return 0; > +} > + > +static const VMStateDescription vmstate_spapr_xive_end = { > + .name = TYPE_SPAPR_XIVE "/end", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField []) { > + VMSTATE_UINT32(w0, XiveEND), > + VMSTATE_UINT32(w1, XiveEND), > + VMSTATE_UINT32(w2, XiveEND), > + VMSTATE_UINT32(w3, XiveEND), > + VMSTATE_UINT32(w4, XiveEND), > + VMSTATE_UINT32(w5, XiveEND), > + VMSTATE_UINT32(w6, XiveEND), > + VMSTATE_UINT32(w7, XiveEND), > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +static const VMStateDescription vmstate_spapr_xive_eas = { > + .name = TYPE_SPAPR_XIVE "/eas", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField []) { > + VMSTATE_UINT64(w, XiveEAS), > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +static const VMStateDescription vmstate_spapr_xive = { > + .name = TYPE_SPAPR_XIVE, > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32_EQUAL(nr_irqs, sPAPRXive, NULL), > + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(eat, sPAPRXive, nr_irqs, > + vmstate_spapr_xive_eas, XiveEAS), > + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(endt, sPAPRXive, nr_ends, > + vmstate_spapr_xive_end, XiveEND), > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +static Property spapr_xive_properties[] = { > + DEFINE_PROP_UINT32("nr-irqs", sPAPRXive, nr_irqs, 0), > + DEFINE_PROP_UINT32("nr-ends", sPAPRXive, nr_ends, 0), > + DEFINE_PROP_UINT64("vc-base", sPAPRXive, vc_base, SPAPR_XIVE_VC_BASE), > + DEFINE_PROP_UINT64("tm-base", sPAPRXive, tm_base, SPAPR_XIVE_TM_BASE), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void spapr_xive_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); > + > + dc->desc = "sPAPR XIVE Interrupt Controller"; > + dc->props = spapr_xive_properties; > + dc->realize = spapr_xive_realize; > + dc->vmsd = &vmstate_spapr_xive; > + > + xrc->get_eas = spapr_xive_get_eas; > + xrc->get_end = spapr_xive_get_end; > + xrc->write_end = spapr_xive_write_end; > +} > + > +static const TypeInfo spapr_xive_info = { > + .name = TYPE_SPAPR_XIVE, > + .parent = TYPE_XIVE_ROUTER, > + .instance_init = spapr_xive_instance_init, > + .instance_size = sizeof(sPAPRXive), > + .class_init = spapr_xive_class_init, > +}; > + > +static void spapr_xive_register_types(void) > +{ > + type_register_static(&spapr_xive_info); > +} > + > +type_init(spapr_xive_register_types) > + > +bool spapr_xive_irq_claim(sPAPRXive *xive, uint32_t lisn, bool lsi) > +{ > + XiveSource *xsrc = &xive->source; > + > + if (lisn >= xive->nr_irqs) { > + return false; > + } > + > + xive->eat[lisn].w |= cpu_to_be64(EAS_VALID); > + xive_source_irq_set(xsrc, lisn, lsi); > + return true; > +} > + > +bool spapr_xive_irq_free(sPAPRXive *xive, uint32_t lisn) > +{ > + XiveSource *xsrc = &xive->source; > + > + if (lisn >= xive->nr_irqs) { > + return false; > + } > + > + xive->eat[lisn].w &= cpu_to_be64(~EAS_VALID); > + xive_source_irq_set(xsrc, lisn, false); > + return true; > +} > + > +qemu_irq spapr_xive_qirq(sPAPRXive *xive, uint32_t lisn) > +{ > + XiveSource *xsrc = &xive->source; > + > + if (lisn >= xive->nr_irqs) { > + return NULL; > + } > + > + /* The sPAPR machine/device should have claimed the IRQ before */ > + assert(xive_eas_is_valid(&xive->eat[lisn])); > + > + return xive_source_qirq(xsrc, lisn); > +} > diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs > index 72a46ed91c31..301a8e972d91 100644 > --- a/hw/intc/Makefile.objs > +++ b/hw/intc/Makefile.objs > @@ -38,6 +38,7 @@ obj-$(CONFIG_XICS) += xics.o > obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o > obj-$(CONFIG_XICS_KVM) += xics_kvm.o > obj-$(CONFIG_XIVE) += xive.o > +obj-$(CONFIG_XIVE_SPAPR) += spapr_xive.o > obj-$(CONFIG_POWERNV) += xics_pnv.o > obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o > obj-$(CONFIG_S390_FLIC) += s390_flic.o
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 2d1e7c5c4668..7f34ad0528ed 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -17,6 +17,7 @@ CONFIG_XICS=$(CONFIG_PSERIES) CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM)) CONFIG_XIVE=$(CONFIG_PSERIES) +CONFIG_XIVE_SPAPR=$(CONFIG_PSERIES) CONFIG_MEM_DEVICE=y CONFIG_DIMM=y CONFIG_SPAPR_RNG=y diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h new file mode 100644 index 000000000000..f087959b9924 --- /dev/null +++ b/include/hw/ppc/spapr_xive.h @@ -0,0 +1,45 @@ +/* + * QEMU PowerPC sPAPR XIVE interrupt controller model + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_SPAPR_XIVE_H +#define PPC_SPAPR_XIVE_H + +#include "hw/ppc/xive.h" + +#define TYPE_SPAPR_XIVE "spapr-xive" +#define SPAPR_XIVE(obj) OBJECT_CHECK(sPAPRXive, (obj), TYPE_SPAPR_XIVE) + +typedef struct sPAPRXive { + XiveRouter parent; + + /* Internal interrupt source for IPIs and virtual devices */ + XiveSource source; + hwaddr vc_base; + + /* END ESB MMIOs */ + XiveENDSource end_source; + hwaddr end_base; + + /* Routing table */ + XiveEAS *eat; + uint32_t nr_irqs; + XiveEND *endt; + uint32_t nr_ends; + + /* TIMA mapping address */ + hwaddr tm_base; + MemoryRegion tm_mmio; +} sPAPRXive; + +bool spapr_xive_irq_claim(sPAPRXive *xive, uint32_t lisn, bool lsi); +bool spapr_xive_irq_free(sPAPRXive *xive, uint32_t lisn); +void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon); +qemu_irq spapr_xive_qirq(sPAPRXive *xive, uint32_t lisn); + +#endif /* PPC_SPAPR_XIVE_H */ diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c new file mode 100644 index 000000000000..eef5830d45c6 --- /dev/null +++ b/hw/intc/spapr_xive.c @@ -0,0 +1,366 @@ +/* + * QEMU PowerPC sPAPR XIVE interrupt controller model + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "target/ppc/cpu.h" +#include "sysemu/cpus.h" +#include "monitor/monitor.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_xive.h" +#include "hw/ppc/xive.h" +#include "hw/ppc/xive_regs.h" + +/* + * XIVE Virtualization Controller BAR and Thread Managment BAR that we + * use for the ESB pages and the TIMA pages + */ +#define SPAPR_XIVE_VC_BASE 0x0006010000000000ull +#define SPAPR_XIVE_TM_BASE 0x0006030203180000ull + +/* + * On sPAPR machines, use a simplified output for the XIVE END + * structure dumping only the information related to the OS EQ. + */ +static void spapr_xive_end_pic_print_info(sPAPRXive *xive, XiveEND *end, + Monitor *mon) +{ + uint32_t qindex = GETFIELD_BE32(END_W1_PAGE_OFF, end->w1); + uint32_t qgen = GETFIELD_BE32(END_W1_GENERATION, end->w1); + uint32_t qsize = GETFIELD_BE32(END_W0_QSIZE, end->w0); + uint32_t qentries = 1 << (qsize + 10); + uint32_t nvt = GETFIELD_BE32(END_W6_NVT_INDEX, end->w6); + uint8_t priority = GETFIELD_BE32(END_W7_F0_PRIORITY, end->w7); + + monitor_printf(mon, "%3d/%d % 6d/%5d ^%d", nvt, + priority, qindex, qentries, qgen); + + xive_end_queue_pic_print_info(end, 6, mon); + monitor_printf(mon, "]"); +} + +void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon) +{ + XiveSource *xsrc = &xive->source; + int i; + + monitor_printf(mon, " LSIN PQ EISN CPU/PRIO EQ\n"); + + for (i = 0; i < xive->nr_irqs; i++) { + uint8_t pq = xive_source_esb_get(xsrc, i); + XiveEAS *eas = &xive->eat[i]; + + if (!xive_eas_is_valid(eas)) { + continue; + } + + monitor_printf(mon, " %08x %s %c%c%c %s %08x ", i, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ', + xive_eas_is_masked(eas) ? "M" : " ", + (int) GETFIELD_BE64(EAS_END_DATA, eas->w)); + + if (!xive_eas_is_masked(eas)) { + uint32_t end_idx = GETFIELD_BE64(EAS_END_INDEX, eas->w); + XiveEND *end; + + assert(end_idx < xive->nr_ends); + end = &xive->endt[end_idx]; + + if (xive_end_is_valid(end)) { + spapr_xive_end_pic_print_info(xive, end, mon); + } + } + monitor_printf(mon, "\n"); + } +} + +static void spapr_xive_map_mmio(sPAPRXive *xive) +{ + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base); + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base); + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base); +} + +static void spapr_xive_end_reset(XiveEND *end) +{ + memset(end, 0, sizeof(*end)); + + /* switch off the escalation and notification ESBs */ + end->w1 = cpu_to_be32(END_W1_ESe_Q | END_W1_ESn_Q); +} + +static void spapr_xive_reset(void *dev) +{ + sPAPRXive *xive = SPAPR_XIVE(dev); + int i; + + /* + * The XiveSource has its own reset handler, which mask off all + * IRQs (!P|Q) + */ + + /* Mask all valid EASs in the IRQ number space. */ + for (i = 0; i < xive->nr_irqs; i++) { + XiveEAS *eas = &xive->eat[i]; + if (xive_eas_is_valid(eas)) { + eas->w = cpu_to_be64(EAS_VALID | EAS_MASKED); + } else { + eas->w = 0; + } + } + + /* Clear all ENDs */ + for (i = 0; i < xive->nr_ends; i++) { + spapr_xive_end_reset(&xive->endt[i]); + } +} + +static void spapr_xive_instance_init(Object *obj) +{ + sPAPRXive *xive = SPAPR_XIVE(obj); + + object_initialize(&xive->source, sizeof(xive->source), TYPE_XIVE_SOURCE); + object_property_add_child(obj, "source", OBJECT(&xive->source), NULL); + + object_initialize(&xive->end_source, sizeof(xive->end_source), + TYPE_XIVE_END_SOURCE); + object_property_add_child(obj, "end_source", OBJECT(&xive->end_source), + NULL); +} + +static void spapr_xive_realize(DeviceState *dev, Error **errp) +{ + sPAPRXive *xive = SPAPR_XIVE(dev); + XiveSource *xsrc = &xive->source; + XiveENDSource *end_xsrc = &xive->end_source; + Error *local_err = NULL; + + if (!xive->nr_irqs) { + error_setg(errp, "Number of interrupt needs to be greater 0"); + return; + } + + if (!xive->nr_ends) { + error_setg(errp, "Number of interrupt needs to be greater 0"); + return; + } + + /* + * Initialize the internal sources, for IPIs and virtual devices. + */ + object_property_set_int(OBJECT(xsrc), xive->nr_irqs, "nr-irqs", + &error_fatal); + object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive), + &error_fatal); + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * Initialize the END ESB source + */ + object_property_set_int(OBJECT(end_xsrc), xive->nr_irqs, "nr-ends", + &error_fatal); + object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive), + &error_fatal); + object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Set the mapping address of the END ESB pages after the source ESBs */ + xive->end_base = xive->vc_base + (1ull << xsrc->esb_shift) * xsrc->nr_irqs; + + /* + * Allocate the routing tables + */ + xive->eat = g_new0(XiveEAS, xive->nr_irqs); + xive->endt = g_new0(XiveEND, xive->nr_ends); + + /* TIMA initialization */ + memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive, + "xive.tima", 4ull << TM_SHIFT); + + /* Define all XIVE MMIO regions on SysBus */ + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio); + + /* Map all regions */ + spapr_xive_map_mmio(xive); + + qemu_register_reset(spapr_xive_reset, dev); +} + +static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk, + uint32_t eas_idx, XiveEAS *eas) +{ + sPAPRXive *xive = SPAPR_XIVE(xrtr); + + if (eas_idx >= xive->nr_irqs) { + return -1; + } + + *eas = xive->eat[eas_idx]; + return 0; +} + +static int spapr_xive_get_end(XiveRouter *xrtr, + uint8_t end_blk, uint32_t end_idx, XiveEND *end) +{ + sPAPRXive *xive = SPAPR_XIVE(xrtr); + + if (end_idx >= xive->nr_ends) { + return -1; + } + + memcpy(end, &xive->endt[end_idx], sizeof(XiveEND)); + return 0; +} + +static int spapr_xive_write_end(XiveRouter *xrtr, uint8_t end_blk, + uint32_t end_idx, XiveEND *end, + uint8_t word_number) +{ + sPAPRXive *xive = SPAPR_XIVE(xrtr); + + if (end_idx >= xive->nr_ends) { + return -1; + } + + memcpy(&xive->endt[end_idx], end, sizeof(XiveEND)); + return 0; +} + +static const VMStateDescription vmstate_spapr_xive_end = { + .name = TYPE_SPAPR_XIVE "/end", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(w0, XiveEND), + VMSTATE_UINT32(w1, XiveEND), + VMSTATE_UINT32(w2, XiveEND), + VMSTATE_UINT32(w3, XiveEND), + VMSTATE_UINT32(w4, XiveEND), + VMSTATE_UINT32(w5, XiveEND), + VMSTATE_UINT32(w6, XiveEND), + VMSTATE_UINT32(w7, XiveEND), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_spapr_xive_eas = { + .name = TYPE_SPAPR_XIVE "/eas", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT64(w, XiveEAS), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_spapr_xive = { + .name = TYPE_SPAPR_XIVE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_EQUAL(nr_irqs, sPAPRXive, NULL), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(eat, sPAPRXive, nr_irqs, + vmstate_spapr_xive_eas, XiveEAS), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(endt, sPAPRXive, nr_ends, + vmstate_spapr_xive_end, XiveEND), + VMSTATE_END_OF_LIST() + }, +}; + +static Property spapr_xive_properties[] = { + DEFINE_PROP_UINT32("nr-irqs", sPAPRXive, nr_irqs, 0), + DEFINE_PROP_UINT32("nr-ends", sPAPRXive, nr_ends, 0), + DEFINE_PROP_UINT64("vc-base", sPAPRXive, vc_base, SPAPR_XIVE_VC_BASE), + DEFINE_PROP_UINT64("tm-base", sPAPRXive, tm_base, SPAPR_XIVE_TM_BASE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_xive_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); + + dc->desc = "sPAPR XIVE Interrupt Controller"; + dc->props = spapr_xive_properties; + dc->realize = spapr_xive_realize; + dc->vmsd = &vmstate_spapr_xive; + + xrc->get_eas = spapr_xive_get_eas; + xrc->get_end = spapr_xive_get_end; + xrc->write_end = spapr_xive_write_end; +} + +static const TypeInfo spapr_xive_info = { + .name = TYPE_SPAPR_XIVE, + .parent = TYPE_XIVE_ROUTER, + .instance_init = spapr_xive_instance_init, + .instance_size = sizeof(sPAPRXive), + .class_init = spapr_xive_class_init, +}; + +static void spapr_xive_register_types(void) +{ + type_register_static(&spapr_xive_info); +} + +type_init(spapr_xive_register_types) + +bool spapr_xive_irq_claim(sPAPRXive *xive, uint32_t lisn, bool lsi) +{ + XiveSource *xsrc = &xive->source; + + if (lisn >= xive->nr_irqs) { + return false; + } + + xive->eat[lisn].w |= cpu_to_be64(EAS_VALID); + xive_source_irq_set(xsrc, lisn, lsi); + return true; +} + +bool spapr_xive_irq_free(sPAPRXive *xive, uint32_t lisn) +{ + XiveSource *xsrc = &xive->source; + + if (lisn >= xive->nr_irqs) { + return false; + } + + xive->eat[lisn].w &= cpu_to_be64(~EAS_VALID); + xive_source_irq_set(xsrc, lisn, false); + return true; +} + +qemu_irq spapr_xive_qirq(sPAPRXive *xive, uint32_t lisn) +{ + XiveSource *xsrc = &xive->source; + + if (lisn >= xive->nr_irqs) { + return NULL; + } + + /* The sPAPR machine/device should have claimed the IRQ before */ + assert(xive_eas_is_valid(&xive->eat[lisn])); + + return xive_source_qirq(xsrc, lisn); +} diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 72a46ed91c31..301a8e972d91 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -38,6 +38,7 @@ obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_XIVE) += xive.o +obj-$(CONFIG_XIVE_SPAPR) += spapr_xive.o obj-$(CONFIG_POWERNV) += xics_pnv.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o