Skip to contentSkip to conversation

ppc: support for the XIVE interrupt controller (POWER9)

PATCH Superseded Cédric Le Goater 3 participants 100 messages Apr 19, 2018 – May 23, 2018 Page 1 of 2

v3 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

v3 cover ppc: support for the XIVE interrupt controller (POWER9)

Hello,

The POWER9 processor comes with a new interrupt controller, called XIVE, which introduces a large number of new features, for virtualization in particular.

* XIVE interrupt controller

It is composed of three sub-engines :

- Interrupt Virtualization Source Engine (IVSE). These are in PHBs, in the main controller for the IPIS and in the PSI host bridge. They are configured to feed the IVRE with events.

- Interrupt Virtualization Routing Engine (IVRE). Its job is to match an event source with a Notification Virtualization Target (NVT), a priority and an Event Queue (EQ) to determine if a Virtual Processor can handle the event.

- Interrupt Virtualization Presentation Engine (IVPE). It maintains the interrupt state of each hardware thread and present the notification as an external exception.

Each of the engines uses a set of internal tables to redirect exceptions from event sources to CPU threads. Interrupt sources have a 2-bit state machine, the Event State Buffer (ESB), that allows events to be triggered. If the event is let through, the IVRE looks up in the Interrupt Virtualization Entry (IVE) table for the Event Queue Descriptor configured for the source. Each Event Queue Descriptor defines a notification path to a CPU and an in-memory queue in which will be recorded an event identifier for the OS to pull.

On a POWER9 sPAPR machine, the Client Architecture Support (CAS) negotiation process determines whether the guest operates with a interrupt controller using the XICS legacy model, as found on POWER8, or in XIVE exploitation mode. On a POWER9 PowerNV machine, the XIVE interrupt controller is a must have.

* XIVE for sPAPR

Here are the high level ideas of the current design to add support for XIVE :

- introduce a persistent sPAPRXive object under the sPAPR machine for newer machines and let the CAS negotiation process decide whether it should be used or not. Use the 'ov5_cas' attribute for this purpose.

- introduce a persistent XIVE interrupt presenter under the sPAPR core and switch ICP after CAS. Each core has now two ICPs, one active through the 'intc' pointer and another one among its children ready to be used if the guest requires it.

- move the XIVE EQs under the cores to simplify the XIVE model

- allocate the CPU IPIs at the beginning of the IRQ number space to be compatible with XICS (which starts at 4096) and also to simplify the model. This means that the XIVE model covers the whole IRQ number space. There are no offset like in XICS splitting the IRQ number space.

* sPAPR patchset layout

It first defines new models for XIVE, which will be shared between the machines or with KVM for sPAPR :

- XiveSource holding the PQ bits and the ESB MMIO region used to control them.

- XiveNVT holding the CPU interrupt state and the EQ state. it models the XIVE interrupt presenter engine.

- sPAPRXive modeling the XIVE interrupt controller for sPAPR machines, holding the internal routing table, a single XIVE source for the IPIs and other interrupts and the TIMA MMIO regions used by the XiveNVT to do interrupt management.

We do not model the IVRE, but this is not a problem to introduce it if needed. Maybe for migration. To be discussed.

Then, the notification process and the interrupt delivery to the CPU is described. Support for sPAPR is completed with the integration of the sPAPRXive object in the machine, the definition of the new XIVE hcalls, the device tree layout, and the necessary adjustments to support the CAS negotiation.

Follows the support for KVM with a set of specific XIVE models, very much like XICS does. But, the interrupt mode is still chosen at the init of the machine and the reset does change the KVM interrupt device. A couple of patches try to fix this limitation with a proposal to support resets of KVM devices. Some issues in the MMU migration which still need to be addressed.

* PowerNV extension

It seemed interesting to include the models for PowerNV as a way to validate that the concept are valid.

The patchset finishes with RFCs of models for the XIVE interrupt controller and for the PSI bridge device for the POWER9 PowerNV. PSI provides a good example of the usage of the notify() handler of the XiveFabric interface, linking the PSI XiveSource to its owning device.

* Coverage

At this stage, XIVE support in QEMU covers :

- TCG & KVM kernel_irqchip=off/on
- CPU hotplug
- support for older machines
- migration under TCG
- migration under KVM, including kernel_irqchip=off <-> kernel_irqchip=on

* Caveats

Migration still needs some care to make sure all HW states are captured correctly. Extra quiescence points are possibly needed, to turn off/on the XIVE configuration under KVM.

KVM device reset works well enough but has consequences on MMU migration. Probably an ordering problem.

* Github

QEMU:

https://github.com/legoater/qemu/commits/xive

Linux/KVM (to be sent later on):

https://github.com/legoater/linux/commits/xive

Thanks,

C.

Changes since v2 :

- added support for Store EOI
- added support for two page ESB MMIO setting like on KVM
- introduced the XiveFabric interface
- introduced spapr_xive_mmio_unmap()
- KVM support

Cédric Le Goater (35):
ppc/xive: introduce a XIVE interrupt source model ppc/xive: add support for the LSI interrupt sources ppc/xive: introduce the XiveFabric interface spapr/xive: introduce a XIVE interrupt controller for sPAPR spapr/xive: add a single source block to the sPAPR XIVE model spapr/xive: introduce a XIVE interrupt presenter model spapr/xive: introduce the XIVE Event Queues
spapr: push the XIVE EQ data in OS event queue
spapr: notify the CPU when the XIVE interrupt priority is more
privileged
spapr: add support for the SET_OS_PENDING command (XIVE)
spapr: introduce a 'xive_exploitation' option to enable XIVE
spapr: add a sPAPRXive object to the machine
spapr: add hcalls support for the XIVE exploitation interrupt mode
spapr: add device tree support for the XIVE exploitation mode
sysbus: add a sysbus_mmio_unmap() helper
spapr: introduce a helper to map the XIVE memory regions
spapr: add XIVE support to spapr_qirq()
spapr: introduce a spapr_icp_create() helper
spapr: toggle the ICP depending on the selected interrupt mode
spapr: add support to dump XIVE information
spapr: advertise XIVE exploitation mode in CAS
spapr: add classes for the XIVE models
target/ppc/kvm: add Linux KVM definitions for XIVE spapr/xive: add common realize routine for KVM spapr/xive: add KVM support
spapr/xive: add a XIVE KVM device to the machine
migration: discard non-migratable RAMBlocks
intc: introduce a CPUIntc interface
spapr/xive,xics: use the CPU_INTC handlers to reset KVM spapr/xive,xics: reset KVM at machine reset
spapr/xive: raise migration priority of the machine ppc/pnv: introduce a pnv_icp_create() helper
ppc: externalize ppc_get_vcpu_by_pir()
ppc/pnv: add XIVE support
ppc/pnv: add a PSI bridge model for POWER9 processor

 default-configs/ppc64-softmmu.mak |    3 +
 exec.c                            |   10 +
 hw/core/sysbus.c                  |   10 +
 hw/intc/Makefile.objs             |    5 +-
 hw/intc/intc.c                    |   26 +
 hw/intc/pnv_xive.c                | 1234 +++++++++++++++++++++++++++++++++++++
 hw/intc/pnv_xive_regs.h           |  314 ++++++++++
 hw/intc/spapr_xive.c              |  324 ++++++++++
 hw/intc/spapr_xive_hcall.c        |  923 +++++++++++++++++++++++++++
 hw/intc/spapr_xive_kvm.c          |  655 ++++++++++++++++++++
 hw/intc/xics.c                    |    4 +
 hw/intc/xics_kvm.c                |  108 +++-
 hw/intc/xive.c                    | 1200 ++++++++++++++++++++++++++++++++++++
 hw/ppc/pnv.c                      |   93 +--
 hw/ppc/pnv_core.c                 |    2 +-
 hw/ppc/pnv_psi.c                  |  399 +++++++++++-
 hw/ppc/ppc.c                      |   16 +
 hw/ppc/spapr.c                    |  264 +++++++-
 hw/ppc/spapr_cpu_core.c           |   55 +-
 hw/ppc/spapr_hcall.c              |    6 +
 hw/ppc/spapr_rtas.c               |    2 -
 include/exec/cpu-common.h         |    1 +
 include/hw/intc/intc.h            |   21 +
 include/hw/ppc/pnv.h              |   37 +-
 include/hw/ppc/pnv_psi.h          |   50 +-
 include/hw/ppc/pnv_xive.h         |   89 +++
 include/hw/ppc/pnv_xscom.h        |    5 +
 include/hw/ppc/ppc.h              |    1 +
 include/hw/ppc/spapr.h            |   21 +-
 include/hw/ppc/spapr_cpu_core.h   |    2 +
 include/hw/ppc/spapr_xive.h       |   93 +++
 include/hw/ppc/xics.h             |    1 +
 include/hw/ppc/xive.h             |  269 ++++++++
 include/hw/ppc/xive_regs.h        |  187 ++++++
 include/hw/sysbus.h               |    1 +
 include/migration/vmstate.h       |    2 +
 linux-headers/asm-powerpc/kvm.h   |   18 +
 linux-headers/linux/kvm.h         |    5 +
 migration/ram.c                   |   42 +-
 target/ppc/kvm.c                  |    7 +
 target/ppc/kvm_ppc.h              |    6 +
 41 files changed, 6414 insertions(+), 97 deletions(-)
 create mode 100644 hw/intc/pnv_xive.c
 create mode 100644 hw/intc/pnv_xive_regs.h
 create mode 100644 hw/intc/spapr_xive.c
 create mode 100644 hw/intc/spapr_xive_hcall.c
 create mode 100644 hw/intc/spapr_xive_kvm.c
 create mode 100644 hw/intc/xive.c
 create mode 100644 include/hw/ppc/pnv_xive.h
 create mode 100644 include/hw/ppc/spapr_xive.h
 create mode 100644 include/hw/ppc/xive.h
 create mode 100644 include/hw/ppc/xive_regs.h
Signature · 1 line
2.13.6
#1 lore raw

PATCH v3 1/35 ppc/xive: introduce a XIVE interrupt source model

Each XIVE interrupt source is associated with a two bit state machine called an Event State Buffer (ESB) : the first bit "P" means that an interrupt is "pending" and waiting for an EOI and the bit "Q" (queued) means a new interrupt was triggered while another was still pending.

When an event is triggered, the associated interrupt state bits are fetched and modified and forwarded to the virtualization engine of the controller doing the routing. These can also be controlled by MMIO, to trigger events or turn off the sources for instance. See code for more details on the states and transitions.

On a sPAPR machine, the OS will obtain the address of the MMIO page of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used.

The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on.

---
Changes since v2:

- added support for Store EOI
- added support for two page MMIO setting like on KVM

 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xive.c                    | 335 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xive.h             | 130 +++++++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 hw/intc/xive.c
 create mode 100644 include/hw/ppc/xive.h
default-configs/ppc64-softmmu.mak +1 -0
index b94af6c7c62a..c6d13e757977 100644
@@ -16,4 +16,5 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_XICS=$(CONFIG_PSERIES)
 CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
+CONFIG_XIVE=$(CONFIG_PSERIES)
 CONFIG_MEM_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 0e9963f5eecc..72a46ed91c31 100644
@@ -37,6 +37,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
 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_POWERNV) += xics_pnv.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xive.c new file +335 -0

335 lines not on this page · raw message

include/hw/ppc/xive.h new file +130 -0

130 lines not on this page · raw message

Signature · 1 line
2.13.6
#2 lore raw

Re: [Qemu-devel] [PATCH v3 01/35] ppc/xive: introduce a XIVE interrupt source model

Quoted 19 lines · Cédric Le Goater

Each XIVE interrupt source is associated with a two bit state machine called an Event State Buffer (ESB) : the first bit "P" means that an interrupt is "pending" and waiting for an EOI and the bit "Q" (queued) means a new interrupt was triggered while another was still pending.

When an event is triggered, the associated interrupt state bits are fetched and modified and forwarded to the virtualization engine of the controller doing the routing. These can also be controlled by MMIO, to trigger events or turn off the sources for instance. See code for more details on the states and transitions.

On a sPAPR machine, the OS will obtain the address of the MMIO page of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used.

The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on.

Quoted 6 lines

---
Changes since v2:

- added support for Store EOI
- added support for two page MMIO setting like on KVM

Looks generally sane to me, though I have a few queries.

Quoted 135 lines
 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xive.c                    | 335 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xive.h             | 130 +++++++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 hw/intc/xive.c
 create mode 100644 include/hw/ppc/xive.h
default-configs/ppc64-softmmu.mak +1 -0
index b94af6c7c62a..c6d13e757977 100644
@@ -16,4 +16,5 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_XICS=$(CONFIG_PSERIES)
 CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
+CONFIG_XIVE=$(CONFIG_PSERIES)
 CONFIG_MEM_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 0e9963f5eecc..72a46ed91c31 100644
@@ -37,6 +37,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
 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_POWERNV) += xics_pnv.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xive.c new file +98 -0
new file mode 100644
index 000000000000..c70578759d02
@@ -0,0 +1,335 @@
+/*
+ * QEMU PowerPC 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/dma.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/xive.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+uint8_t xive_source_pq_get(XiveSource *xsrc, uint32_t srcno)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+
+    assert(byte < xsrc->sbe_size);
+
+    return (xsrc->sbe[byte] >> bit) & 0x3;
+}
+
+uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+    uint8_t old, new;
+
+    assert(byte < xsrc->sbe_size);
+
+    old = xsrc->sbe[byte];
+
+    new = xsrc->sbe[byte] & ~(0x3 << bit);
+    new |= (pq & 0x3) << bit;
+
+    xsrc->sbe[byte] = new;
+
+    return (old >> bit) & 0x3;
+}
+
+static bool xive_source_pq_eoi(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*
+ * Returns whether the event notification should be forwarded.
+ */
+static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*

hw/intc/xive.c line 108

Quoted 10 lines
hw/intc/xive.c new file +10 -0
+ * Forward the source event notification to the associated XiveFabric,
+ * the device owning the sources.
+ */
+static void xive_source_notify(XiveSource *xsrc, int srcno)
+{
+
+}
+
+/* In a two pages ESB MMIO setting, even page is the trigger page, odd
+ * page is for management */

Can I understand from this that the result from this function is only meaningful in 2-pages mode?

hw/intc/xive.c line 111

Quoted 3 lines
hw/intc/xive.c new file +3 -0
+static inline bool xive_source_is_trigger_page(hwaddr addr)
+{
+    return !((addr >> 16) & 1);

Later on you seem to have both 4k and 64k variants list, but here you hardcode 64k. Is that a problem?

hw/intc/xive.c line 117

Quoted 6 lines
hw/intc/xive.c new file +6 -0
+}
+
+static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;

You ignore the low bits of the address entirely, so effective you have a 256 byte range that's all aliases of the same register. Is that intentional?

Quoted 7 lines
hw/intc/xive.c new file +7 -0
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    uint64_t ret = -1;
+
+    if (xive_source_esb_2page(xsrc) && xive_source_is_trigger_page(addr)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "XIVE: invalid load on IRQ %d trigger page at "
+                      "0x%"HWADDR_PRIx"\n", srcno, addr);

hw/intc/xive.c line 134

Quoted 10 lines
hw/intc/xive.c new file +10 -0
+        return -1;
+    }
+
+    switch (offset) {
+    case XIVE_ESB_LOAD_EOI:
+        /*
+         * Load EOI is not the default source setting under QEMU, but
+         * this is what HW uses currently.
+         */
+        ret = xive_source_pq_eoi(xsrc, srcno);

You're implicitly casting a bool return value into a u64 here, is that intentional?

Quoted 53 lines
hw/intc/xive.c new file +53 -0
+
+        break;
+
+    case XIVE_ESB_GET:
+        ret = xive_source_pq_get(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_SET_PQ_00:
+    case XIVE_ESB_SET_PQ_01:
+    case XIVE_ESB_SET_PQ_10:
+    case XIVE_ESB_SET_PQ_11:
+        ret = xive_source_pq_set(xsrc, srcno, (offset >> 8) & 0x3);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB addr %d\n", offset);
+    }
+
+    return ret;
+}
+
+static void xive_source_esb_write(void *opaque, hwaddr addr,
+                                 uint64_t value, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    bool notify = false;
+
+    switch (offset) {
+    case 0:
+        notify = xive_source_pq_trigger(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_STORE_EOI:
+        if (xive_source_is_trigger_page(addr)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid store on IRQ %d trigger page at "
+                          "0x%"HWADDR_PRIx"\n", srcno, addr);
+            return;
+        }
+
+        if (!(xsrc->esb_flags & XIVE_SRC_STORE_EOI)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid Store EOI for IRQ %d\n", srcno);
+            return;
+        }
+
+        /* If the Q bit is set, we should forward a new source event
+         * notification
+         */
+        notify = xive_source_pq_eoi(xsrc, srcno);
+        break;
+

hw/intc/xive.c line 197

Quoted 10 lines
hw/intc/xive.c new file +10 -0
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %d\n",
+                      offset);
+        return;
+    }
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }

EOI via this path calls notify, but the one via the read path doesn't. Is that correct?

Quoted 100 lines
hw/intc/xive.c new file +100 -0
+}
+
+static const MemoryRegionOps xive_source_esb_ops = {
+    .read = xive_source_esb_read,
+    .write = xive_source_esb_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+};
+
+static void xive_source_set_irq(void *opaque, int srcno, int val)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    bool notify = false;
+
+    if (val) {
+        notify = xive_source_pq_trigger(xsrc, srcno);
+    }
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }
+}
+
+void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "XIVE Source %6x ..%6x\n",
+                   xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        uint8_t pq = xive_source_pq_get(xsrc, i);
+        uint32_t lisn = i  + xsrc->offset;
+
+        if (pq == XIVE_ESB_OFF) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %c%c\n", lisn,
+                       pq & XIVE_ESB_VAL_P ? 'P' : '-',
+                       pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
+    }
+}
+
+static void xive_source_reset(DeviceState *dev)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    /* SBEs are initialized to 0b01 which corresponds to "ints off" */
+    memset(xsrc->sbe, 0x55, xsrc->sbe_size);
+}
+
+static void xive_source_realize(DeviceState *dev, Error **errp)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    if (!xsrc->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater than 0");
+        return;
+    }
+
+    if (xsrc->esb_shift != XIVE_ESB_4K &&
+        xsrc->esb_shift != XIVE_ESB_4K_2PAGE &&
+        xsrc->esb_shift != XIVE_ESB_64K &&
+        xsrc->esb_shift != XIVE_ESB_64K_2PAGE) {
+        error_setg(errp, "Invalid ESB shift setting");
+        return;
+    }
+
+    xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
+                                     xsrc->nr_irqs);
+
+    /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
+    xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
+    xsrc->sbe = g_malloc0(xsrc->sbe_size);
+
+    /* TODO: H_INT_ESB support, which removing the ESB MMIOs */
+
+    memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
+                          &xive_source_esb_ops, xsrc, "xive.esb",
+                          (1ull << xsrc->esb_shift) * xsrc->nr_irqs);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xsrc->esb_mmio);
+}
+
+static const VMStateDescription vmstate_xive_source = {
+    .name = TYPE_XIVE_SOURCE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL),
+        VMSTATE_VBUFFER_UINT32(sbe, XiveSource, 1, NULL, sbe_size),
+        VMSTATE_END_OF_LIST()
+    },

hw/intc/xive.c line 307

Quoted 10 lines
hw/intc/xive.c new file +10 -0
+};
+
+/*
+ * The default XIVE interrupt source setting for ESB MMIO is two 64k
+ * pages without Store EOI. This is in sync with KVM.
+ */
+static Property xive_source_properties[] = {
+    DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
+    DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
+    DEFINE_PROP_UINT64("bar", XiveSource, esb_base, 0),

Isn't this redundant with however the base address is handled through the SysBusDevice stuff (I forget the details)?

Quoted 78 lines
hw/intc/xive.c new file +28 -0
+    DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xive_source_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_source_realize;
+    dc->reset = xive_source_reset;
+    dc->props = xive_source_properties;
+    dc->desc = "XIVE interrupt source";
+    dc->vmsd = &vmstate_xive_source;
+}
+
+static const TypeInfo xive_source_info = {
+    .name          = TYPE_XIVE_SOURCE,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XiveSource),
+    .class_init    = xive_source_class_init,
+};
+
+static void xive_register_types(void)
+{
+    type_register_static(&xive_source_info);
+}
+
+type_init(xive_register_types)
include/hw/ppc/xive.h new file +44 -0
new file mode 100644
index 000000000000..d92a50519edf
@@ -0,0 +1,130 @@
+/*
+ * QEMU PowerPC 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_XIVE_H
+#define PPC_XIVE_H
+
+#include "hw/sysbus.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+#define TYPE_XIVE_SOURCE "xive-source"
+#define XIVE_SOURCE(obj) OBJECT_CHECK(XiveSource, (obj), TYPE_XIVE_SOURCE)
+
+/*
+ * XIVE Source Interrupt source characteristics, which define how the
+ * ESB are controlled.
+ */
+#define XIVE_SRC_H_INT_ESB     0x1 /* ESB managed with hcall H_INT_ESB */
+#define XIVE_SRC_STORE_EOI     0x4 /* Store EOI supported */
+
+typedef struct XiveSource {
+    SysBusDevice parent;
+
+    /* IRQs */
+    uint32_t     nr_irqs;
+    uint32_t     offset;
+    qemu_irq     *qirqs;
+
+    /* PQ bits */
+    uint8_t      *sbe;
+    uint32_t     sbe_size;
+
+    /* ESB memory region */
+    uint64_t     esb_flags;
+    hwaddr       esb_base;
+    uint32_t     esb_shift;

include/hw/ppc/xive.h line 54

Quoted 10 lines
include/hw/ppc/xive.h new file +10 -0
+    MemoryRegion esb_mmio;
+} XiveSource;
+
+/*
+ * ESB MMIO setting. Can be one page, for both source triggering and
+ * source management, or two different pages. See below for magic
+ * values.
+ */
+#define XIVE_ESB_4K          12 /* PSI HB */
+#define XIVE_ESB_4K_2PAGE    17

Should this be 13 instead of 17?

include/hw/ppc/xive.h line 56

Quoted 2 lines
include/hw/ppc/xive.h new file +2 -0
+#define XIVE_ESB_64K         16
+#define XIVE_ESB_64K_2PAGE   17

(Also, who the hell comes up with a brand new PIC and decides to have *4* different interface variants. But that's not your problem, I realise).

Quoted 74 lines
include/hw/ppc/xive.h new file +74 -0
+
+static inline bool xive_source_esb_2page(XiveSource *xsrc)
+{
+    return xsrc->esb_shift == XIVE_ESB_64K_2PAGE;
+}
+
+static inline hwaddr xive_source_esb_base(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->esb_base + (1ull << xsrc->esb_shift) * srcno;
+}
+
+/* The trigger page is always the first/even page */
+#define xive_source_esb_trigger xive_source_esb_base
+
+/* In a two pages ESB MMIO setting, the odd page is for management */
+static inline hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)
+{
+    hwaddr addr = xive_source_esb_base(xsrc, srcno);
+
+    if (xive_source_esb_2page(xsrc)) {
+        addr += (1 << (xsrc->esb_shift - 1));
+    }
+
+    return addr;
+}
+
+/*
+ * Each interrupt source has a 2-bit state machine called ESB which
+ * can be controlled by MMIO. It's made of 2 bits, P and Q. P
+ * indicates that an interrupt is pending (has been sent to a queue
+ * and is waiting for an EOI). Q indicates that the interrupt has been
+ * triggered while pending.
+ *
+ * This acts as a coalescing mechanism in order to guarantee
+ * that a given interrupt only occurs at most once in a queue.
+ *
+ * When doing an EOI, the Q bit will indicate if the interrupt
+ * needs to be re-triggered.
+ */
+#define XIVE_ESB_VAL_P        0x2
+#define XIVE_ESB_VAL_Q        0x1
+
+#define XIVE_ESB_RESET        0x0
+#define XIVE_ESB_PENDING      XIVE_ESB_VAL_P
+#define XIVE_ESB_QUEUED       (XIVE_ESB_VAL_P | XIVE_ESB_VAL_Q)
+#define XIVE_ESB_OFF          XIVE_ESB_VAL_Q
+
+/*
+ * "magic" Event State Buffer (ESB) MMIO offsets.
+ *
+ * The following offsets into the ESB MMIO allow to read or
+ * manipulate the PQ bits. They must be used with an 8-bytes
+ * load instruction. They all return the previous state of the
+ * interrupt (atomically).
+ *
+ * Additionally, some ESB pages support doing an EOI via a
+ * store at 0 and some ESBs support doing a trigger via a
+ * separate trigger page.
+ */
+#define XIVE_ESB_STORE_EOI      0x400 /* Store */
+#define XIVE_ESB_LOAD_EOI       0x000 /* Load */
+#define XIVE_ESB_GET            0x800 /* Load */
+#define XIVE_ESB_SET_PQ_00      0xc00 /* Load */
+#define XIVE_ESB_SET_PQ_01      0xd00 /* Load */
+#define XIVE_ESB_SET_PQ_10      0xe00 /* Load */
+#define XIVE_ESB_SET_PQ_11      0xf00 /* Load */
+
+uint8_t xive_source_pq_get(XiveSource *xsrc, uint32_t srcno);
+uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
+
+void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
+
+#endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#3 lore raw

Re: [Qemu-devel] [PATCH v3 01/35] ppc/xive: introduce a XIVE interrupt source model

Quoted 169 lines · David Gibson
Quoted 19 lines · Cédric Le Goater

Each XIVE interrupt source is associated with a two bit state machine called an Event State Buffer (ESB) : the first bit "P" means that an interrupt is "pending" and waiting for an EOI and the bit "Q" (queued) means a new interrupt was triggered while another was still pending.

When an event is triggered, the associated interrupt state bits are fetched and modified and forwarded to the virtualization engine of the controller doing the routing. These can also be controlled by MMIO, to trigger events or turn off the sources for instance. See code for more details on the states and transitions.

On a sPAPR machine, the OS will obtain the address of the MMIO page of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used.

The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on.

Quoted 6 lines

---
Changes since v2:

- added support for Store EOI
- added support for two page MMIO setting like on KVM

Looks generally sane to me, though I have a few queries.

Quoted 140 lines
 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xive.c                    | 335 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xive.h             | 130 +++++++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 hw/intc/xive.c
 create mode 100644 include/hw/ppc/xive.h
default-configs/ppc64-softmmu.mak +1 -0
index b94af6c7c62a..c6d13e757977 100644
@@ -16,4 +16,5 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_XICS=$(CONFIG_PSERIES)
 CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
+CONFIG_XIVE=$(CONFIG_PSERIES)
 CONFIG_MEM_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 0e9963f5eecc..72a46ed91c31 100644
@@ -37,6 +37,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
 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_POWERNV) += xics_pnv.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xive.c new file +103 -0
new file mode 100644
index 000000000000..c70578759d02
@@ -0,0 +1,335 @@
+/*
+ * QEMU PowerPC 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/dma.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/xive.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+uint8_t xive_source_pq_get(XiveSource *xsrc, uint32_t srcno)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+
+    assert(byte < xsrc->sbe_size);
+
+    return (xsrc->sbe[byte] >> bit) & 0x3;
+}
+
+uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+    uint8_t old, new;
+
+    assert(byte < xsrc->sbe_size);
+
+    old = xsrc->sbe[byte];
+
+    new = xsrc->sbe[byte] & ~(0x3 << bit);
+    new |= (pq & 0x3) << bit;
+
+    xsrc->sbe[byte] = new;
+
+    return (old >> bit) & 0x3;
+}
+
+static bool xive_source_pq_eoi(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*
+ * Returns whether the event notification should be forwarded.
+ */
+static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*
+ * Forward the source event notification to the associated XiveFabric,
+ * the device owning the sources.
+ */
+static void xive_source_notify(XiveSource *xsrc, int srcno)
+{
Quoted 8 lines

hw/intc/xive.c line 108

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+
+}
+
+/* In a two pages ESB MMIO setting, even page is the trigger page, odd
+ * page is for management */

Can I understand from this that the result from this function is only meaningful in 2-pages mode?

yes. May be I should rename it to xive_source_is_even_page() ?

Quoted 6 lines

hw/intc/xive.c line 111

Quoted 3 lines
hw/intc/xive.c new file +3 -0
+static inline bool xive_source_is_trigger_page(hwaddr addr)
+{
+    return !((addr >> 16) & 1);

Later on you seem to have both 4k and 64k variants list, but here you hardcode 64k. Is that a problem?

oups. This should be :

	(addr >> (xsrc->esb_shift - 1))

I did the tests with the spapr guest which uses 64k ESB MMIO pages. The check is only significant in a 2 pages setting.

Quoted 10 lines

hw/intc/xive.c line 117

Quoted 6 lines
hw/intc/xive.c new file +6 -0
+}
+
+static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;

You ignore the low bits of the address entirely, so effective you have a 256 byte range that's all aliases of the same register. Is that intentional?

yes but it's not entirely correct. The exact ranges are :

Load Store

0x000 .. 0x3FF EOI and return 0|1 Trigger
0x400 .. 0x7FF EOI and return 0|1 EOI
0x800 .. 0xBFF return PQ undefined
0xC00 .. 0xCFF return PQ and PQ=00 PQ=00
0xD00 .. 0xDFF return PQ and PQ=01 PQ=01
0xE00 .. 0xDFF return PQ and PQ=10 PQ=10
0xF00 .. 0xDFF return PQ and PQ=11 PQ=11

There is room for some improvements.

The trigger page in a two pages ESB MMIO settings only triggers on stores.

Quoted 12 lines
Quoted 12 lines
hw/intc/xive.c new file +12 -0
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    uint64_t ret = -1;
+
+    if (xive_source_esb_2page(xsrc) && xive_source_is_trigger_page(addr)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "XIVE: invalid load on IRQ %d trigger page at "
+                      "0x%"HWADDR_PRIx"\n", srcno, addr);
+        return -1;
+    }
+
+    switch (offset) {
+    case XIVE_ESB_LOAD_EOI:
Quoted 8 lines

hw/intc/xive.c line 134

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+        /*
+         * Load EOI is not the default source setting under QEMU, but
+         * this is what HW uses currently.
+         */
+        ret = xive_source_pq_eoi(xsrc, srcno);

You're implicitly casting a bool return value into a u64 here, is that intentional?

yes. is that bad ? This is what the load is supposed to return in the transition algo.

Quoted 58 lines
Quoted 58 lines
hw/intc/xive.c new file +58 -0
+
+        break;
+
+    case XIVE_ESB_GET:
+        ret = xive_source_pq_get(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_SET_PQ_00:
+    case XIVE_ESB_SET_PQ_01:
+    case XIVE_ESB_SET_PQ_10:
+    case XIVE_ESB_SET_PQ_11:
+        ret = xive_source_pq_set(xsrc, srcno, (offset >> 8) & 0x3);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB addr %d\n", offset);
+    }
+
+    return ret;
+}
+
+static void xive_source_esb_write(void *opaque, hwaddr addr,
+                                 uint64_t value, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    bool notify = false;
+
+    switch (offset) {
+    case 0:
+        notify = xive_source_pq_trigger(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_STORE_EOI:
+        if (xive_source_is_trigger_page(addr)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid store on IRQ %d trigger page at "
+                          "0x%"HWADDR_PRIx"\n", srcno, addr);
+            return;
+        }
+
+        if (!(xsrc->esb_flags & XIVE_SRC_STORE_EOI)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid Store EOI for IRQ %d\n", srcno);
+            return;
+        }
+
+        /* If the Q bit is set, we should forward a new source event
+         * notification
+         */
+        notify = xive_source_pq_eoi(xsrc, srcno);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %d\n",
+                      offset);
+        return;
+    }
Quoted 8 lines

hw/intc/xive.c line 197

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }

EOI via this path calls notify, but the one via the read path doesn't. Is that correct?

No. I have given attention to the one page ESB MMIO setting + Store EOI in the emulated mode and not enough to the two pages ESB MMIO setting. This is a late change to be compatible with KVM. I will fix.

Quoted 106 lines
Quoted 106 lines
hw/intc/xive.c new file +105 -0
+}
+
+static const MemoryRegionOps xive_source_esb_ops = {
+    .read = xive_source_esb_read,
+    .write = xive_source_esb_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+};
+
+static void xive_source_set_irq(void *opaque, int srcno, int val)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    bool notify = false;
+
+    if (val) {
+        notify = xive_source_pq_trigger(xsrc, srcno);
+    }
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }
+}
+
+void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "XIVE Source %6x ..%6x\n",
+                   xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        uint8_t pq = xive_source_pq_get(xsrc, i);
+        uint32_t lisn = i  + xsrc->offset;
+
+        if (pq == XIVE_ESB_OFF) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %c%c\n", lisn,
+                       pq & XIVE_ESB_VAL_P ? 'P' : '-',
+                       pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
+    }
+}
+
+static void xive_source_reset(DeviceState *dev)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    /* SBEs are initialized to 0b01 which corresponds to "ints off" */
+    memset(xsrc->sbe, 0x55, xsrc->sbe_size);
+}
+
+static void xive_source_realize(DeviceState *dev, Error **errp)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    if (!xsrc->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater than 0");
+        return;
+    }
+
+    if (xsrc->esb_shift != XIVE_ESB_4K &&
+        xsrc->esb_shift != XIVE_ESB_4K_2PAGE &&
+        xsrc->esb_shift != XIVE_ESB_64K &&
+        xsrc->esb_shift != XIVE_ESB_64K_2PAGE) {
+        error_setg(errp, "Invalid ESB shift setting");
+        return;
+    }
+
+    xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
+                                     xsrc->nr_irqs);
+
+    /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
+    xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
+    xsrc->sbe = g_malloc0(xsrc->sbe_size);
+
+    /* TODO: H_INT_ESB support, which removing the ESB MMIOs */
+
+    memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
+                          &xive_source_esb_ops, xsrc, "xive.esb",
+                          (1ull << xsrc->esb_shift) * xsrc->nr_irqs);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xsrc->esb_mmio);
+}
+
+static const VMStateDescription vmstate_xive_source = {
+    .name = TYPE_XIVE_SOURCE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL),
+        VMSTATE_VBUFFER_UINT32(sbe, XiveSource, 1, NULL, sbe_size),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+/*
+ * The default XIVE interrupt source setting for ESB MMIO is two 64k
+ * pages without Store EOI. This is in sync with KVM.
Quoted 8 lines

hw/intc/xive.c line 307

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+ */
+static Property xive_source_properties[] = {
+    DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
+    DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
+    DEFINE_PROP_UINT64("bar", XiveSource, esb_base, 0),

Isn't this redundant with however the base address is handled through the SysBusDevice stuff (I forget the details)?

Storing the ESB MMIO base address under the XiveSource object is convenient later on in the h_int_get_source_info hcall which make use of the helpers :

hwaddr xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno) hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)

But it is only used in that place. So we could just store the ESB MMIO base address under the sPAPRXive controller. This makes some sense in the design, as we have to inform KVM of this address with a KVM device ioctl.

Quoted 82 lines
Quoted 82 lines
hw/intc/xive.c new file +28 -0
+    DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xive_source_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_source_realize;
+    dc->reset = xive_source_reset;
+    dc->props = xive_source_properties;
+    dc->desc = "XIVE interrupt source";
+    dc->vmsd = &vmstate_xive_source;
+}
+
+static const TypeInfo xive_source_info = {
+    .name          = TYPE_XIVE_SOURCE,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XiveSource),
+    .class_init    = xive_source_class_init,
+};
+
+static void xive_register_types(void)
+{
+    type_register_static(&xive_source_info);
+}
+
+type_init(xive_register_types)
include/hw/ppc/xive.h new file +48 -0
new file mode 100644
index 000000000000..d92a50519edf
@@ -0,0 +1,130 @@
+/*
+ * QEMU PowerPC 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_XIVE_H
+#define PPC_XIVE_H
+
+#include "hw/sysbus.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+#define TYPE_XIVE_SOURCE "xive-source"
+#define XIVE_SOURCE(obj) OBJECT_CHECK(XiveSource, (obj), TYPE_XIVE_SOURCE)
+
+/*
+ * XIVE Source Interrupt source characteristics, which define how the
+ * ESB are controlled.
+ */
+#define XIVE_SRC_H_INT_ESB     0x1 /* ESB managed with hcall H_INT_ESB */
+#define XIVE_SRC_STORE_EOI     0x4 /* Store EOI supported */
+
+typedef struct XiveSource {
+    SysBusDevice parent;
+
+    /* IRQs */
+    uint32_t     nr_irqs;
+    uint32_t     offset;
+    qemu_irq     *qirqs;
+
+    /* PQ bits */
+    uint8_t      *sbe;
+    uint32_t     sbe_size;
+
+    /* ESB memory region */
+    uint64_t     esb_flags;
+    hwaddr       esb_base;
+    uint32_t     esb_shift;
+    MemoryRegion esb_mmio;
+} XiveSource;
+
+/*
Quoted 8 lines

include/hw/ppc/xive.h line 54

Quoted 6 lines
include/hw/ppc/xive.h new file +6 -0
+ * ESB MMIO setting. Can be one page, for both source triggering and
+ * source management, or two different pages. See below for magic
+ * values.
+ */
+#define XIVE_ESB_4K          12 /* PSI HB */
+#define XIVE_ESB_4K_2PAGE    17

Should this be 13 instead of 17?

oups. obviously this is not used.

Quoted 6 lines

include/hw/ppc/xive.h line 56

Quoted 2 lines
include/hw/ppc/xive.h new file +2 -0
+#define XIVE_ESB_64K         16
+#define XIVE_ESB_64K_2PAGE   17

(Also, who the hell comes up with a brand new PIC and decides to have *4* different interface variants. But that's not your problem, I realise).

HW constraints on the different controllers which need to expose sources : PSI, PHB4. The internal sources of the XIVE interrupt controller can be configured to use 4K or 64K but I doubt the 4k will be ever used.

Thanks,

C.

Quoted 75 lines
Quoted 74 lines
include/hw/ppc/xive.h new file +74 -0
+
+static inline bool xive_source_esb_2page(XiveSource *xsrc)
+{
+    return xsrc->esb_shift == XIVE_ESB_64K_2PAGE;
+}
+
+static inline hwaddr xive_source_esb_base(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->esb_base + (1ull << xsrc->esb_shift) * srcno;
+}
+
+/* The trigger page is always the first/even page */
+#define xive_source_esb_trigger xive_source_esb_base
+
+/* In a two pages ESB MMIO setting, the odd page is for management */
+static inline hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)
+{
+    hwaddr addr = xive_source_esb_base(xsrc, srcno);
+
+    if (xive_source_esb_2page(xsrc)) {
+        addr += (1 << (xsrc->esb_shift - 1));
+    }
+
+    return addr;
+}
+
+/*
+ * Each interrupt source has a 2-bit state machine called ESB which
+ * can be controlled by MMIO. It's made of 2 bits, P and Q. P
+ * indicates that an interrupt is pending (has been sent to a queue
+ * and is waiting for an EOI). Q indicates that the interrupt has been
+ * triggered while pending.
+ *
+ * This acts as a coalescing mechanism in order to guarantee
+ * that a given interrupt only occurs at most once in a queue.
+ *
+ * When doing an EOI, the Q bit will indicate if the interrupt
+ * needs to be re-triggered.
+ */
+#define XIVE_ESB_VAL_P        0x2
+#define XIVE_ESB_VAL_Q        0x1
+
+#define XIVE_ESB_RESET        0x0
+#define XIVE_ESB_PENDING      XIVE_ESB_VAL_P
+#define XIVE_ESB_QUEUED       (XIVE_ESB_VAL_P | XIVE_ESB_VAL_Q)
+#define XIVE_ESB_OFF          XIVE_ESB_VAL_Q
+
+/*
+ * "magic" Event State Buffer (ESB) MMIO offsets.
+ *
+ * The following offsets into the ESB MMIO allow to read or
+ * manipulate the PQ bits. They must be used with an 8-bytes
+ * load instruction. They all return the previous state of the
+ * interrupt (atomically).
+ *
+ * Additionally, some ESB pages support doing an EOI via a
+ * store at 0 and some ESBs support doing a trigger via a
+ * separate trigger page.
+ */
+#define XIVE_ESB_STORE_EOI      0x400 /* Store */
+#define XIVE_ESB_LOAD_EOI       0x000 /* Load */
+#define XIVE_ESB_GET            0x800 /* Load */
+#define XIVE_ESB_SET_PQ_00      0xc00 /* Load */
+#define XIVE_ESB_SET_PQ_01      0xd00 /* Load */
+#define XIVE_ESB_SET_PQ_10      0xe00 /* Load */
+#define XIVE_ESB_SET_PQ_11      0xf00 /* Load */
+
+uint8_t xive_source_pq_get(XiveSource *xsrc, uint32_t srcno);
+uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
+
+void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
+
+#endif /* PPC_XIVE_H */
#4 lore raw

Re: [Qemu-devel] [PATCH v3 01/35] ppc/xive: introduce a XIVE interrupt source model

Quoted 179 lines · Cédric Le Goater
Quoted 169 lines · David Gibson
Quoted 19 lines · Cédric Le Goater

Each XIVE interrupt source is associated with a two bit state machine called an Event State Buffer (ESB) : the first bit "P" means that an interrupt is "pending" and waiting for an EOI and the bit "Q" (queued) means a new interrupt was triggered while another was still pending.

When an event is triggered, the associated interrupt state bits are fetched and modified and forwarded to the virtualization engine of the controller doing the routing. These can also be controlled by MMIO, to trigger events or turn off the sources for instance. See code for more details on the states and transitions.

On a sPAPR machine, the OS will obtain the address of the MMIO page of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used.

The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on.

Quoted 6 lines

---
Changes since v2:

- added support for Store EOI
- added support for two page MMIO setting like on KVM

Looks generally sane to me, though I have a few queries.

Quoted 140 lines
 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xive.c                    | 335 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xive.h             | 130 +++++++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 hw/intc/xive.c
 create mode 100644 include/hw/ppc/xive.h
default-configs/ppc64-softmmu.mak +1 -0
index b94af6c7c62a..c6d13e757977 100644
@@ -16,4 +16,5 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_XICS=$(CONFIG_PSERIES)
 CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
+CONFIG_XIVE=$(CONFIG_PSERIES)
 CONFIG_MEM_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 0e9963f5eecc..72a46ed91c31 100644
@@ -37,6 +37,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
 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_POWERNV) += xics_pnv.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xive.c new file +103 -0
new file mode 100644
index 000000000000..c70578759d02
@@ -0,0 +1,335 @@
+/*
+ * QEMU PowerPC 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/dma.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/xive.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+uint8_t xive_source_pq_get(XiveSource *xsrc, uint32_t srcno)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+
+    assert(byte < xsrc->sbe_size);
+
+    return (xsrc->sbe[byte] >> bit) & 0x3;
+}
+
+uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+    uint8_t old, new;
+
+    assert(byte < xsrc->sbe_size);
+
+    old = xsrc->sbe[byte];
+
+    new = xsrc->sbe[byte] & ~(0x3 << bit);
+    new |= (pq & 0x3) << bit;
+
+    xsrc->sbe[byte] = new;
+
+    return (old >> bit) & 0x3;
+}
+
+static bool xive_source_pq_eoi(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*
+ * Returns whether the event notification should be forwarded.
+ */
+static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*
+ * Forward the source event notification to the associated XiveFabric,
+ * the device owning the sources.
+ */
+static void xive_source_notify(XiveSource *xsrc, int srcno)
+{
Quoted 8 lines

hw/intc/xive.c line 108

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+
+}
+
+/* In a two pages ESB MMIO setting, even page is the trigger page, odd
+ * page is for management */

Can I understand from this that the result from this function is only meaningful in 2-pages mode?

Quoted 1 line

yes. May be I should rename it to xive_source_is_even_page() ?

Seems very long winded. Maybe keep the name but have it check both whether it's an even page and also whether you're in 2pages mode.

Quoted 7 lines
Quoted 6 lines

hw/intc/xive.c line 111

Quoted 3 lines
hw/intc/xive.c new file +3 -0
+static inline bool xive_source_is_trigger_page(hwaddr addr)
+{
+    return !((addr >> 16) & 1);

Later on you seem to have both 4k and 64k variants list, but here you hardcode 64k. Is that a problem?

Quoted 6 lines

oups. This should be :

	(addr >> (xsrc->esb_shift - 1))

I did the tests with the spapr guest which uses 64k ESB MMIO pages. The check is only significant in a 2 pages setting.

Ok.

Quoted 23 lines
Quoted 10 lines

hw/intc/xive.c line 117

Quoted 6 lines
hw/intc/xive.c new file +6 -0
+}
+
+static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;

You ignore the low bits of the address entirely, so effective you have a 256 byte range that's all aliases of the same register. Is that intentional?

yes but it's not entirely correct. The exact ranges are :

Load Store

0x000 .. 0x3FF EOI and return 0|1 Trigger
0x400 .. 0x7FF EOI and return 0|1 EOI
0x800 .. 0xBFF return PQ undefined
0xC00 .. 0xCFF return PQ and PQ=00 PQ=00
0xD00 .. 0xDFF return PQ and PQ=01 PQ=01
0xE00 .. 0xDFF return PQ and PQ=10 PQ=10
0xF00 .. 0xDFF return PQ and PQ=11 PQ=11

Quoted 3 lines

There is room for some improvements.

The trigger page in a two pages ESB MMIO settings only triggers on stores.

Ok.

Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
hw/intc/xive.c new file +12 -0
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    uint64_t ret = -1;
+
+    if (xive_source_esb_2page(xsrc) && xive_source_is_trigger_page(addr)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "XIVE: invalid load on IRQ %d trigger page at "
+                      "0x%"HWADDR_PRIx"\n", srcno, addr);
+        return -1;
+    }
+
+    switch (offset) {
+    case XIVE_ESB_LOAD_EOI:
Quoted 8 lines

hw/intc/xive.c line 134

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+        /*
+         * Load EOI is not the default source setting under QEMU, but
+         * this is what HW uses currently.
+         */
+        ret = xive_source_pq_eoi(xsrc, srcno);

You're implicitly casting a bool return value into a u64 here, is that intentional?

Quoted 2 lines

yes. is that bad ? This is what the load is supposed to return in the transition algo.

No, that's fine, as long as just using the LSB in your return is correct. Just making sure I understood.

Quoted 67 lines
Quoted 58 lines
Quoted 58 lines
hw/intc/xive.c new file +58 -0
+
+        break;
+
+    case XIVE_ESB_GET:
+        ret = xive_source_pq_get(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_SET_PQ_00:
+    case XIVE_ESB_SET_PQ_01:
+    case XIVE_ESB_SET_PQ_10:
+    case XIVE_ESB_SET_PQ_11:
+        ret = xive_source_pq_set(xsrc, srcno, (offset >> 8) & 0x3);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB addr %d\n", offset);
+    }
+
+    return ret;
+}
+
+static void xive_source_esb_write(void *opaque, hwaddr addr,
+                                 uint64_t value, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    bool notify = false;
+
+    switch (offset) {
+    case 0:
+        notify = xive_source_pq_trigger(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_STORE_EOI:
+        if (xive_source_is_trigger_page(addr)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid store on IRQ %d trigger page at "
+                          "0x%"HWADDR_PRIx"\n", srcno, addr);
+            return;
+        }
+
+        if (!(xsrc->esb_flags & XIVE_SRC_STORE_EOI)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid Store EOI for IRQ %d\n", srcno);
+            return;
+        }
+
+        /* If the Q bit is set, we should forward a new source event
+         * notification
+         */
+        notify = xive_source_pq_eoi(xsrc, srcno);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %d\n",
+                      offset);
+        return;
+    }
Quoted 8 lines

hw/intc/xive.c line 197

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }

EOI via this path calls notify, but the one via the read path doesn't. Is that correct?

Quoted 3 lines

No. I have given attention to the one page ESB MMIO setting + Store EOI in the emulated mode and not enough to the two pages ESB MMIO setting. This is a late change to be compatible with KVM. I will fix.

Ok.

Quoted 119 lines
Quoted 106 lines
Quoted 106 lines
hw/intc/xive.c new file +105 -0
+}
+
+static const MemoryRegionOps xive_source_esb_ops = {
+    .read = xive_source_esb_read,
+    .write = xive_source_esb_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+};
+
+static void xive_source_set_irq(void *opaque, int srcno, int val)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    bool notify = false;
+
+    if (val) {
+        notify = xive_source_pq_trigger(xsrc, srcno);
+    }
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }
+}
+
+void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "XIVE Source %6x ..%6x\n",
+                   xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        uint8_t pq = xive_source_pq_get(xsrc, i);
+        uint32_t lisn = i  + xsrc->offset;
+
+        if (pq == XIVE_ESB_OFF) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %c%c\n", lisn,
+                       pq & XIVE_ESB_VAL_P ? 'P' : '-',
+                       pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
+    }
+}
+
+static void xive_source_reset(DeviceState *dev)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    /* SBEs are initialized to 0b01 which corresponds to "ints off" */
+    memset(xsrc->sbe, 0x55, xsrc->sbe_size);
+}
+
+static void xive_source_realize(DeviceState *dev, Error **errp)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    if (!xsrc->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater than 0");
+        return;
+    }
+
+    if (xsrc->esb_shift != XIVE_ESB_4K &&
+        xsrc->esb_shift != XIVE_ESB_4K_2PAGE &&
+        xsrc->esb_shift != XIVE_ESB_64K &&
+        xsrc->esb_shift != XIVE_ESB_64K_2PAGE) {
+        error_setg(errp, "Invalid ESB shift setting");
+        return;
+    }
+
+    xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
+                                     xsrc->nr_irqs);
+
+    /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
+    xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
+    xsrc->sbe = g_malloc0(xsrc->sbe_size);
+
+    /* TODO: H_INT_ESB support, which removing the ESB MMIOs */
+
+    memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
+                          &xive_source_esb_ops, xsrc, "xive.esb",
+                          (1ull << xsrc->esb_shift) * xsrc->nr_irqs);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xsrc->esb_mmio);
+}
+
+static const VMStateDescription vmstate_xive_source = {
+    .name = TYPE_XIVE_SOURCE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL),
+        VMSTATE_VBUFFER_UINT32(sbe, XiveSource, 1, NULL, sbe_size),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+/*
+ * The default XIVE interrupt source setting for ESB MMIO is two 64k
+ * pages without Store EOI. This is in sync with KVM.
Quoted 8 lines

hw/intc/xive.c line 307

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+ */
+static Property xive_source_properties[] = {
+    DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
+    DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
+    DEFINE_PROP_UINT64("bar", XiveSource, esb_base, 0),

Isn't this redundant with however the base address is handled through the SysBusDevice stuff (I forget the details)?

Storing the ESB MMIO base address under the XiveSource object is convenient later on in the h_int_get_source_info hcall which make use of the helpers :

Quoted 7 lines

hwaddr xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno) hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)

But it is only used in that place. So we could just store the ESB MMIO base address under the sPAPRXive controller. This makes some sense in the design, as we have to inform KVM of this address with a KVM device ioctl.

Well.. I really dislike the idea that you could change the actual MMIO mapping address in one place, but other bits of code would still think it was mapped somewhere else.

Quoted 100 lines
Quoted 82 lines
Quoted 82 lines
hw/intc/xive.c new file +28 -0
+    DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xive_source_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_source_realize;
+    dc->reset = xive_source_reset;
+    dc->props = xive_source_properties;
+    dc->desc = "XIVE interrupt source";
+    dc->vmsd = &vmstate_xive_source;
+}
+
+static const TypeInfo xive_source_info = {
+    .name          = TYPE_XIVE_SOURCE,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XiveSource),
+    .class_init    = xive_source_class_init,
+};
+
+static void xive_register_types(void)
+{
+    type_register_static(&xive_source_info);
+}
+
+type_init(xive_register_types)
include/hw/ppc/xive.h new file +48 -0
new file mode 100644
index 000000000000..d92a50519edf
@@ -0,0 +1,130 @@
+/*
+ * QEMU PowerPC 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_XIVE_H
+#define PPC_XIVE_H
+
+#include "hw/sysbus.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+#define TYPE_XIVE_SOURCE "xive-source"
+#define XIVE_SOURCE(obj) OBJECT_CHECK(XiveSource, (obj), TYPE_XIVE_SOURCE)
+
+/*
+ * XIVE Source Interrupt source characteristics, which define how the
+ * ESB are controlled.
+ */
+#define XIVE_SRC_H_INT_ESB     0x1 /* ESB managed with hcall H_INT_ESB */
+#define XIVE_SRC_STORE_EOI     0x4 /* Store EOI supported */
+
+typedef struct XiveSource {
+    SysBusDevice parent;
+
+    /* IRQs */
+    uint32_t     nr_irqs;
+    uint32_t     offset;
+    qemu_irq     *qirqs;
+
+    /* PQ bits */
+    uint8_t      *sbe;
+    uint32_t     sbe_size;
+
+    /* ESB memory region */
+    uint64_t     esb_flags;
+    hwaddr       esb_base;
+    uint32_t     esb_shift;
+    MemoryRegion esb_mmio;
+} XiveSource;
+
+/*
Quoted 8 lines

include/hw/ppc/xive.h line 54

Quoted 6 lines
include/hw/ppc/xive.h new file +6 -0
+ * ESB MMIO setting. Can be one page, for both source triggering and
+ * source management, or two different pages. See below for magic
+ * values.
+ */
+#define XIVE_ESB_4K          12 /* PSI HB */
+#define XIVE_ESB_4K_2PAGE    17

Should this be 13 instead of 17?

oups. obviously this is not used.

Quoted 6 lines

include/hw/ppc/xive.h line 56

Quoted 2 lines
include/hw/ppc/xive.h new file +2 -0
+#define XIVE_ESB_64K         16
+#define XIVE_ESB_64K_2PAGE   17

(Also, who the hell comes up with a brand new PIC and decides to have *4* different interface variants. But that's not your problem, I realise).

Quoted 4 lines

HW constraints on the different controllers which need to expose sources : PSI, PHB4. The internal sources of the XIVE interrupt controller can be configured to use 4K or 64K but I doubt the 4k will be ever used.

Sure, the hardware is different, but *why* is it different. This is a brand new design, you'd think they could come up with one variant that works for all the cases.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#5 lore raw

Re: [Qemu-devel] [PATCH v3 01/35] ppc/xive: introduce a XIVE interrupt source model

Quoted 180 lines · David Gibson
Quoted 179 lines · Cédric Le Goater
Quoted 177 lines · David Gibson
Quoted 19 lines · Cédric Le Goater

Each XIVE interrupt source is associated with a two bit state machine called an Event State Buffer (ESB) : the first bit "P" means that an interrupt is "pending" and waiting for an EOI and the bit "Q" (queued) means a new interrupt was triggered while another was still pending.

When an event is triggered, the associated interrupt state bits are fetched and modified and forwarded to the virtualization engine of the controller doing the routing. These can also be controlled by MMIO, to trigger events or turn off the sources for instance. See code for more details on the states and transitions.

On a sPAPR machine, the OS will obtain the address of the MMIO page of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used.

The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on.

Quoted 6 lines

---
Changes since v2:

- added support for Store EOI
- added support for two page MMIO setting like on KVM

Looks generally sane to me, though I have a few queries.

Quoted 135 lines
 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xive.c                    | 335 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xive.h             | 130 +++++++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 hw/intc/xive.c
 create mode 100644 include/hw/ppc/xive.h
default-configs/ppc64-softmmu.mak +1 -0
index b94af6c7c62a..c6d13e757977 100644
@@ -16,4 +16,5 @@ CONFIG_VIRTIO_VGA=y
 CONFIG_XICS=$(CONFIG_PSERIES)
 CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
+CONFIG_XIVE=$(CONFIG_PSERIES)
 CONFIG_MEM_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 0e9963f5eecc..72a46ed91c31 100644
@@ -37,6 +37,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
 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_POWERNV) += xics_pnv.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xive.c new file +98 -0
new file mode 100644
index 000000000000..c70578759d02
@@ -0,0 +1,335 @@
+/*
+ * QEMU PowerPC 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/dma.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/xive.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+uint8_t xive_source_pq_get(XiveSource *xsrc, uint32_t srcno)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+
+    assert(byte < xsrc->sbe_size);
+
+    return (xsrc->sbe[byte] >> bit) & 0x3;
+}
+
+uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq)
+{
+    uint32_t byte = srcno / 4;
+    uint32_t bit  = (srcno % 4) * 2;
+    uint8_t old, new;
+
+    assert(byte < xsrc->sbe_size);
+
+    old = xsrc->sbe[byte];
+
+    new = xsrc->sbe[byte] & ~(0x3 << bit);
+    new |= (pq & 0x3) << bit;
+
+    xsrc->sbe[byte] = new;
+
+    return (old >> bit) & 0x3;
+}
+
+static bool xive_source_pq_eoi(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_RESET);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*
+ * Returns whether the event notification should be forwarded.
+ */
+static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    switch (old_pq) {
+    case XIVE_ESB_RESET:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    case XIVE_ESB_PENDING:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_QUEUED:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_QUEUED);
+        return false;
+    case XIVE_ESB_OFF:
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_OFF);
+        return false;
+    default:
+         g_assert_not_reached();
+    }
+}
+
+/*

hw/intc/xive.c line 108

Quoted 10 lines
hw/intc/xive.c new file +10 -0
+ * Forward the source event notification to the associated XiveFabric,
+ * the device owning the sources.
+ */
+static void xive_source_notify(XiveSource *xsrc, int srcno)
+{
+
+}
+
+/* In a two pages ESB MMIO setting, even page is the trigger page, odd
+ * page is for management */

Can I understand from this that the result from this function is only meaningful in 2-pages mode?

Quoted 4 lines
Quoted 1 line

yes. May be I should rename it to xive_source_is_even_page() ?

Seems very long winded. Maybe keep the name but have it check both whether it's an even page and also whether you're in 2pages mode.

yes. I had that in mind.

Quoted 40 lines
Quoted 8 lines
Quoted 7 lines

hw/intc/xive.c line 111

Quoted 4 lines
hw/intc/xive.c new file +3 -0
+static inline bool xive_source_is_trigger_page(hwaddr addr)
+{
+    return !((addr >> 16) & 1);

Later on you seem to have both 4k and 64k variants list, but here you hardcode 64k. Is that a problem?

Quoted 6 lines

oups. This should be :

	(addr >> (xsrc->esb_shift - 1))

I did the tests with the spapr guest which uses 64k ESB MMIO pages. The check is only significant in a 2 pages setting.

Ok.

Quoted 23 lines
Quoted 10 lines

hw/intc/xive.c line 117

Quoted 6 lines
hw/intc/xive.c new file +6 -0
+}
+
+static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;

You ignore the low bits of the address entirely, so effective you have a 256 byte range that's all aliases of the same register. Is that intentional?

yes but it's not entirely correct. The exact ranges are :

Load Store

0x000 .. 0x3FF EOI and return 0|1 Trigger
0x400 .. 0x7FF EOI and return 0|1 EOI
0x800 .. 0xBFF return PQ undefined
0xC00 .. 0xCFF return PQ and PQ=00 PQ=00
0xD00 .. 0xDFF return PQ and PQ=01 PQ=01
0xE00 .. 0xDFF return PQ and PQ=10 PQ=10
0xF00 .. 0xDFF return PQ and PQ=11 PQ=11

Quoted 5 lines
Quoted 3 lines

There is room for some improvements.

The trigger page in a two pages ESB MMIO settings only triggers on stores.

Ok.

I have fixed the ranges and added the above table as a comment in the code. it helps in understanding what are the MMIOs are.

Quoted 223 lines
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
hw/intc/xive.c new file +12 -0
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    uint64_t ret = -1;
+
+    if (xive_source_esb_2page(xsrc) && xive_source_is_trigger_page(addr)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "XIVE: invalid load on IRQ %d trigger page at "
+                      "0x%"HWADDR_PRIx"\n", srcno, addr);
+        return -1;
+    }
+
+    switch (offset) {
+    case XIVE_ESB_LOAD_EOI:
Quoted 8 lines

hw/intc/xive.c line 134

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+        /*
+         * Load EOI is not the default source setting under QEMU, but
+         * this is what HW uses currently.
+         */
+        ret = xive_source_pq_eoi(xsrc, srcno);

You're implicitly casting a bool return value into a u64 here, is that intentional?

Quoted 2 lines

yes. is that bad ? This is what the load is supposed to return in the transition algo.

No, that's fine, as long as just using the LSB in your return is correct. Just making sure I understood.

Quoted 67 lines
Quoted 58 lines
Quoted 58 lines
hw/intc/xive.c new file +58 -0
+
+        break;
+
+    case XIVE_ESB_GET:
+        ret = xive_source_pq_get(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_SET_PQ_00:
+    case XIVE_ESB_SET_PQ_01:
+    case XIVE_ESB_SET_PQ_10:
+    case XIVE_ESB_SET_PQ_11:
+        ret = xive_source_pq_set(xsrc, srcno, (offset >> 8) & 0x3);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB addr %d\n", offset);
+    }
+
+    return ret;
+}
+
+static void xive_source_esb_write(void *opaque, hwaddr addr,
+                                 uint64_t value, unsigned size)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    uint32_t offset = addr & 0xF00;
+    uint32_t srcno = addr >> xsrc->esb_shift;
+    bool notify = false;
+
+    switch (offset) {
+    case 0:
+        notify = xive_source_pq_trigger(xsrc, srcno);
+        break;
+
+    case XIVE_ESB_STORE_EOI:
+        if (xive_source_is_trigger_page(addr)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid store on IRQ %d trigger page at "
+                          "0x%"HWADDR_PRIx"\n", srcno, addr);
+            return;
+        }
+
+        if (!(xsrc->esb_flags & XIVE_SRC_STORE_EOI)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "XIVE: invalid Store EOI for IRQ %d\n", srcno);
+            return;
+        }
+
+        /* If the Q bit is set, we should forward a new source event
+         * notification
+         */
+        notify = xive_source_pq_eoi(xsrc, srcno);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %d\n",
+                      offset);
+        return;
+    }
Quoted 8 lines

hw/intc/xive.c line 197

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }

EOI via this path calls notify, but the one via the read path doesn't. Is that correct?

Quoted 3 lines

No. I have given attention to the one page ESB MMIO setting + Store EOI in the emulated mode and not enough to the two pages ESB MMIO setting. This is a late change to be compatible with KVM. I will fix.

Ok.

Quoted 122 lines
Quoted 106 lines
Quoted 106 lines
hw/intc/xive.c new file +105 -0
+}
+
+static const MemoryRegionOps xive_source_esb_ops = {
+    .read = xive_source_esb_read,
+    .write = xive_source_esb_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    },
+};
+
+static void xive_source_set_irq(void *opaque, int srcno, int val)
+{
+    XiveSource *xsrc = XIVE_SOURCE(opaque);
+    bool notify = false;
+
+    if (val) {
+        notify = xive_source_pq_trigger(xsrc, srcno);
+    }
+
+    /* Forward the source event notification for routing */
+    if (notify) {
+        xive_source_notify(xsrc, srcno);
+    }
+}
+
+void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "XIVE Source %6x ..%6x\n",
+                   xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        uint8_t pq = xive_source_pq_get(xsrc, i);
+        uint32_t lisn = i  + xsrc->offset;
+
+        if (pq == XIVE_ESB_OFF) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %c%c\n", lisn,
+                       pq & XIVE_ESB_VAL_P ? 'P' : '-',
+                       pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
+    }
+}
+
+static void xive_source_reset(DeviceState *dev)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    /* SBEs are initialized to 0b01 which corresponds to "ints off" */
+    memset(xsrc->sbe, 0x55, xsrc->sbe_size);
+}
+
+static void xive_source_realize(DeviceState *dev, Error **errp)
+{
+    XiveSource *xsrc = XIVE_SOURCE(dev);
+
+    if (!xsrc->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater than 0");
+        return;
+    }
+
+    if (xsrc->esb_shift != XIVE_ESB_4K &&
+        xsrc->esb_shift != XIVE_ESB_4K_2PAGE &&
+        xsrc->esb_shift != XIVE_ESB_64K &&
+        xsrc->esb_shift != XIVE_ESB_64K_2PAGE) {
+        error_setg(errp, "Invalid ESB shift setting");
+        return;
+    }
+
+    xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
+                                     xsrc->nr_irqs);
+
+    /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
+    xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
+    xsrc->sbe = g_malloc0(xsrc->sbe_size);
+
+    /* TODO: H_INT_ESB support, which removing the ESB MMIOs */
+
+    memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
+                          &xive_source_esb_ops, xsrc, "xive.esb",
+                          (1ull << xsrc->esb_shift) * xsrc->nr_irqs);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xsrc->esb_mmio);
+}
+
+static const VMStateDescription vmstate_xive_source = {
+    .name = TYPE_XIVE_SOURCE,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL),
+        VMSTATE_VBUFFER_UINT32(sbe, XiveSource, 1, NULL, sbe_size),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+/*
+ * The default XIVE interrupt source setting for ESB MMIO is two 64k
+ * pages without Store EOI. This is in sync with KVM.
Quoted 8 lines

hw/intc/xive.c line 307

Quoted 5 lines
hw/intc/xive.c new file +5 -0
+ */
+static Property xive_source_properties[] = {
+    DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
+    DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
+    DEFINE_PROP_UINT64("bar", XiveSource, esb_base, 0),

Isn't this redundant with however the base address is handled through the SysBusDevice stuff (I forget the details)?

Storing the ESB MMIO base address under the XiveSource object is convenient later on in the h_int_get_source_info hcall which make use of the helpers :

hwaddr xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno) hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)

Quoted 8 lines
Quoted 4 lines

But it is only used in that place. So we could just store the ESB MMIO base address under the sPAPRXive controller. This makes some sense in the design, as we have to inform KVM of this address with a KVM device ioctl.

Well.. I really dislike the idea that you could change the actual MMIO mapping address in one place, but other bits of code would still think it was mapped somewhere else.

OK. I think that my last proposal of removing the ESB MMIO address from the source and letting the owning device, the sPAPR Xive controller in this case, but this is the same for PoweNV or PSI HB, handle the mapping goes in the direction you want to take ?

It does looks better in the overall XIVE model and the XiveSource object have no need of this address.

Quoted 101 lines
Quoted 101 lines
Quoted 83 lines
Quoted 83 lines
hw/intc/xive.c new file +28 -0
+    DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xive_source_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_source_realize;
+    dc->reset = xive_source_reset;
+    dc->props = xive_source_properties;
+    dc->desc = "XIVE interrupt source";
+    dc->vmsd = &vmstate_xive_source;
+}
+
+static const TypeInfo xive_source_info = {
+    .name          = TYPE_XIVE_SOURCE,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XiveSource),
+    .class_init    = xive_source_class_init,
+};
+
+static void xive_register_types(void)
+{
+    type_register_static(&xive_source_info);
+}
+
+type_init(xive_register_types)
include/hw/ppc/xive.h new file +48 -0
new file mode 100644
index 000000000000..d92a50519edf
@@ -0,0 +1,130 @@
+/*
+ * QEMU PowerPC 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_XIVE_H
+#define PPC_XIVE_H
+
+#include "hw/sysbus.h"
+
+/*
+ * XIVE Interrupt Source
+ */
+
+#define TYPE_XIVE_SOURCE "xive-source"
+#define XIVE_SOURCE(obj) OBJECT_CHECK(XiveSource, (obj), TYPE_XIVE_SOURCE)
+
+/*
+ * XIVE Source Interrupt source characteristics, which define how the
+ * ESB are controlled.
+ */
+#define XIVE_SRC_H_INT_ESB     0x1 /* ESB managed with hcall H_INT_ESB */
+#define XIVE_SRC_STORE_EOI     0x4 /* Store EOI supported */
+
+typedef struct XiveSource {
+    SysBusDevice parent;
+
+    /* IRQs */
+    uint32_t     nr_irqs;
+    uint32_t     offset;
+    qemu_irq     *qirqs;
+
+    /* PQ bits */
+    uint8_t      *sbe;
+    uint32_t     sbe_size;
+
+    /* ESB memory region */
+    uint64_t     esb_flags;
+    hwaddr       esb_base;
+    uint32_t     esb_shift;
+    MemoryRegion esb_mmio;
+} XiveSource;
+
+/*
Quoted 8 lines

include/hw/ppc/xive.h line 54

Quoted 6 lines
include/hw/ppc/xive.h new file +6 -0
+ * ESB MMIO setting. Can be one page, for both source triggering and
+ * source management, or two different pages. See below for magic
+ * values.
+ */
+#define XIVE_ESB_4K          12 /* PSI HB */
+#define XIVE_ESB_4K_2PAGE    17

Should this be 13 instead of 17?

oups. obviously this is not used.

Quoted 6 lines

include/hw/ppc/xive.h line 56

Quoted 2 lines
include/hw/ppc/xive.h new file +2 -0
+#define XIVE_ESB_64K         16
+#define XIVE_ESB_64K_2PAGE   17

(Also, who the hell comes up with a brand new PIC and decides to have *4* different interface variants. But that's not your problem, I realise).

Quoted 9 lines
Quoted 4 lines

HW constraints on the different controllers which need to expose sources : PSI, PHB4. The internal sources of the XIVE interrupt controller can be configured to use 4K or 64K but I doubt the 4k will be ever used.

Sure, the hardware is different, but *why* is it different. This is a brand new design, you'd think they could come up with one variant that works for all the cases.

Yes this is interesting. I will ask the HW team.

Thanks.

C.

#6 lore raw

Re: [Qemu-devel] [PATCH v3 01/35] ppc/xive: introduce a XIVE interrupt source model

Quoted 31 lines · Cédric Le Goater
Quoted 30 lines · David Gibson
Quoted 29 lines · Cédric Le Goater
Quoted 28 lines · David Gibson
Quoted 19 lines · Cédric Le Goater

Each XIVE interrupt source is associated with a two bit state machine called an Event State Buffer (ESB) : the first bit "P" means that an interrupt is "pending" and waiting for an EOI and the bit "Q" (queued) means a new interrupt was triggered while another was still pending.

When an event is triggered, the associated interrupt state bits are fetched and modified and forwarded to the virtualization engine of the controller doing the routing. These can also be controlled by MMIO, to trigger events or turn off the sources for instance. See code for more details on the states and transitions.

On a sPAPR machine, the OS will obtain the address of the MMIO page of the ESB entry associated with a source and its characteristic using the H_INT_GET_SOURCE_INFO hcall. On PowerNV, a similar OPAL call is used.

The xive_source_notify() routine is in charge forwarding the source event notification to the routing engine. It will be filled later on.

Quoted 6 lines

---
Changes since v2:

- added support for Store EOI
- added support for two page MMIO setting like on KVM

Looks generally sane to me, though I have a few queries.

[snip]

Quoted 27 lines
Quoted 18 lines
Quoted 18 lines
Quoted 10 lines

hw/intc/xive.c line 307

Quoted 7 lines

+ * The default XIVE interrupt source setting for ESB MMIO is two 64k
+ * pages without Store EOI. This is in sync with KVM.
+ */

hw/intc/xive.c new file +4 -0
+static Property xive_source_properties[] = {
+    DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0),
+    DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0),
+    DEFINE_PROP_UINT64("bar", XiveSource, esb_base, 0),

Isn't this redundant with however the base address is handled through the SysBusDevice stuff (I forget the details)?

Storing the ESB MMIO base address under the XiveSource object is convenient later on in the h_int_get_source_info hcall which make use of the helpers :

hwaddr xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno) hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno)

Quoted 8 lines
Quoted 4 lines

But it is only used in that place. So we could just store the ESB MMIO base address under the sPAPRXive controller. This makes some sense in the design, as we have to inform KVM of this address with a KVM device ioctl.

Well.. I really dislike the idea that you could change the actual MMIO mapping address in one place, but other bits of code would still think it was mapped somewhere else.

Quoted 7 lines

OK. I think that my last proposal of removing the ESB MMIO address from the source and letting the owning device, the sPAPR Xive controller in this case, but this is the same for PoweNV or PSI HB, handle the mapping goes in the direction you want to take ?

It does looks better in the overall XIVE model and the XiveSource object have no need of this address.

Yes, I think that's the way to go.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#7 lore raw

PATCH v3 2/35 ppc/xive: add support for the LSI interrupt sources

In reply to Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +50 -4
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +16 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;
 
     /* PQ bits */
     uint8_t      *sbe;
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
Signature · 1 line
2.13.6
#8 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 128 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +50 -4
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 6 lines
include/hw/ppc/xive.h +3 -0
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

include/hw/ppc/xive.h typedef struct XiveSource { · line 41

Quoted 2 lines
include/hw/ppc/xive.h +0 -0
     /* PQ bits */
     uint8_t      *sbe;

.. and come to that is there a reason to keep the ASSERTED bit in a separate array from sbe? AFAICT the actual 2-bit-per-irq layout is never exposed to the guests.

Or, even re-use the Q bit for asserted in LSIs (but report it as always 0 in the register read/write path).

Quoted 18 lines
include/hw/ppc/xive.h +13 -0
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#9 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +22 -0
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
Quoted 13 lines

hw/intc/xive.c static void xive_source_esb_write(void *opaque, hwaddr addr, · line 215

Quoted 13 lines
hw/intc/xive.c +8 -0
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }

FYI, I have moved that common test under xive_source_pq_eoi()

Quoted 67 lines
Quoted 67 lines
hw/intc/xive.c +20 -4
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
Quoted 8 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 4 lines
include/hw/ppc/xive.h +3 -0
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

none. I can change that.

Quoted 3 lines

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

yes. I would have preferred two distinct interrupt source objects but this is to be compatible with XICS, which uses only one. If we want to be able to change interrupt mode, the IRQ number space should be organized in the exact same way. Or we should change XICS also.

Also, the change (a bitmap) is really small.

Quoted 6 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 41

Quoted 2 lines
include/hw/ppc/xive.h +0 -0
     /* PQ bits */
     uint8_t      *sbe;

.. and come to that is there a reason to keep the ASSERTED bit in a separate array from sbe? AFAICT the actual 2-bit-per-irq layout is never exposed to the guests.

indeed. we always use the xive_source_pq_get/set() helpers to manipulate the PQ bits. So we could add an extra bit for the ASSERT without too much changes. Could also we put the type there or would you still prefer a bitmap ?

Quoted 2 lines

Or, even re-use the Q bit for asserted in LSIs (but report it as always 0 in the register read/write path).

I would prefer to add extra status bits. It is easier to debug.

Thanks,

C.

Quoted 19 lines
Quoted 18 lines
include/hw/ppc/xive.h +13 -0
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
#10 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 52 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +22 -0
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
Quoted 15 lines
Quoted 5 lines
Quoted 5 lines
hw/intc/xive.c +0 -0
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
Quoted 8 lines

hw/intc/xive.c static void xive_source_esb_write(void *opaque, hwaddr addr, · line 215

Quoted 8 lines
hw/intc/xive.c +8 -0
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }

FYI, I have moved that common test under xive_source_pq_eoi()

Ok.

Quoted 76 lines
Quoted 67 lines
Quoted 67 lines
hw/intc/xive.c +20 -4
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
Quoted 8 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 4 lines
include/hw/ppc/xive.h +3 -0
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

Quoted 1 line

none. I can change that.

Ok.

Quoted 10 lines
Quoted 3 lines

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

yes. I would have preferred two distinct interrupt source objects but this is to be compatible with XICS, which uses only one. If we want to be able to change interrupt mode, the IRQ number space should be organized in the exact same way. Or we should change XICS also.

Also, the change (a bitmap) is really small.

Hrm, but since XIVE supports thousands of irqs, it could be quite a large bitmap.

It's not impossible - in fact, not really even that hard - to change the existing irq layout on xics. It does need a new machine type variant, of course.

Quoted 11 lines
Quoted 6 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 41

Quoted 2 lines
include/hw/ppc/xive.h +0 -0
     /* PQ bits */
     uint8_t      *sbe;

.. and come to that is there a reason to keep the ASSERTED bit in a separate array from sbe? AFAICT the actual 2-bit-per-irq layout is never exposed to the guests.

indeed. we always use the xive_source_pq_get/set() helpers to manipulate the PQ bits. So we could add an extra bit for the ASSERT without too much changes. Could also we put the type there or would you still prefer a bitmap ?

I'd prefer the type (config information) be separate from the P, Q, ASSERTED bits (state information).

Quoted 29 lines
Quoted 2 lines

Or, even re-use the Q bit for asserted in LSIs (but report it as always 0 in the register read/write path).

I would prefer to add extra status bits. It is easier to debug.

Thanks,

C.

Quoted 19 lines
Quoted 18 lines
include/hw/ppc/xive.h +13 -0
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#11 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 160 lines · David Gibson
Quoted 52 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +22 -0
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
Quoted 15 lines
Quoted 5 lines
Quoted 5 lines
hw/intc/xive.c +0 -0
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
Quoted 8 lines

hw/intc/xive.c static void xive_source_esb_write(void *opaque, hwaddr addr, · line 215

Quoted 8 lines
hw/intc/xive.c +8 -0
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }

FYI, I have moved that common test under xive_source_pq_eoi()

Ok.

Quoted 76 lines
Quoted 67 lines
Quoted 67 lines
hw/intc/xive.c +20 -4
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
Quoted 8 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 4 lines
include/hw/ppc/xive.h +3 -0
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

Quoted 1 line

none. I can change that.

Ok.

Quoted 9 lines
Quoted 3 lines

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

yes. I would have preferred two distinct interrupt source objects but this is to be compatible with XICS, which uses only one. If we want to be able to change interrupt mode, the IRQ number space should be organized in the exact same way. Or we should change XICS also.

Quoted 4 lines
Quoted 1 line

Also, the change (a bitmap) is really small.

Hrm, but since XIVE supports thousands of irqs, it could be quite a large bitmap.

Yes. The change is small, not the bitmap.

Quoted 3 lines

It's not impossible - in fact, not really even that hard - to change the existing irq layout on xics. It does need a new machine type variant, of course.

I did some work on that topic a while ago :

	https://patchwork.ozlabs.org/cover/836782/

But we stopped exploring the idea. May be it was not the good approach. The PHBs LSIs would benefit from such a split though.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 41

Quoted 2 lines
include/hw/ppc/xive.h +0 -0
     /* PQ bits */
     uint8_t      *sbe;

.. and come to that is there a reason to keep the ASSERTED bit in a separate array from sbe? AFAICT the actual 2-bit-per-irq layout is never exposed to the guests.

Quoted 7 lines
Quoted 4 lines

indeed. we always use the xive_source_pq_get/set() helpers to manipulate the PQ bits. So we could add an extra bit for the ASSERT without too much changes. Could also we put the type there or would you still prefer a bitmap ?

I'd prefer the type (config information) be separate from the P, Q, ASSERTED bits (state information).

ok. So I will use the 'uint8_t *status' for P, Q, ASSERT, which leaves 5 bits available, but I don't think it is really worth the pain to optimize the size. The sbe array will disappear and we will have a bitmap for the type.

Thanks,

C.

Quoted 30 lines
Quoted 29 lines
Quoted 2 lines

Or, even re-use the Q bit for asserted in LSIs (but report it as always 0 in the register read/write path).

I would prefer to add extra status bits. It is easier to debug.

Thanks,

C.

Quoted 19 lines
Quoted 18 lines
include/hw/ppc/xive.h +13 -0
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
#12 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 172 lines · Cédric Le Goater
Quoted 160 lines · David Gibson
Quoted 52 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +22 -0
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
Quoted 15 lines
Quoted 5 lines
Quoted 5 lines
hw/intc/xive.c +0 -0
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
Quoted 8 lines

hw/intc/xive.c static void xive_source_esb_write(void *opaque, hwaddr addr, · line 215

Quoted 8 lines
hw/intc/xive.c +8 -0
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }

FYI, I have moved that common test under xive_source_pq_eoi()

Ok.

Quoted 76 lines
Quoted 67 lines
Quoted 67 lines
hw/intc/xive.c +20 -4
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
Quoted 8 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 4 lines
include/hw/ppc/xive.h +3 -0
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

Quoted 1 line

none. I can change that.

Ok.

Quoted 9 lines
Quoted 3 lines

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

yes. I would have preferred two distinct interrupt source objects but this is to be compatible with XICS, which uses only one. If we want to be able to change interrupt mode, the IRQ number space should be organized in the exact same way. Or we should change XICS also.

Quoted 4 lines
Quoted 1 line

Also, the change (a bitmap) is really small.

Hrm, but since XIVE supports thousands of irqs, it could be quite a large bitmap.

Yes. The change is small, not the bitmap.

Quoted 3 lines

It's not impossible - in fact, not really even that hard - to change the existing irq layout on xics. It does need a new machine type variant, of course.

Quoted 6 lines

I did some work on that topic a while ago :

	https://patchwork.ozlabs.org/cover/836782/

But we stopped exploring the idea. May be it was not the good approach. The PHBs LSIs would benefit from such a split though.

So, no, I don't think that was a good approach, but that doesn't mean other ways of rearranging the irq numbers aren't ok. The thing here is that we don't want to think of an "irq allocator" - there are some bits like that in there already, but they were always a mistake.

We have lots of irq space (both XICS and XIVE) so instead we should come up with a static mapping of irqs to devices.

Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
Quoted 6 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 41

Quoted 2 lines
include/hw/ppc/xive.h +0 -0
     /* PQ bits */
     uint8_t      *sbe;

.. and come to that is there a reason to keep the ASSERTED bit in a separate array from sbe? AFAICT the actual 2-bit-per-irq layout is never exposed to the guests.

indeed. we always use the xive_source_pq_get/set() helpers to manipulate the PQ bits. So we could add an extra bit for the ASSERT without too much changes. Could also we put the type there or would you still prefer a bitmap ?

Quoted 6 lines
Quoted 2 lines

I'd prefer the type (config information) be separate from the P, Q, ASSERTED bits (state information).

ok. So I will use the 'uint8_t *status' for P, Q, ASSERT, which leaves 5 bits available, but I don't think it is really worth the pain to optimize the size.

Sure. I don't really care if it's packed or not.

Quoted 2 lines

The sbe array will disappear and we will have
a bitmap for the type.

We may or may not keep the type bitmap based on the discussion above, but in any case this is a good step forward.

Quoted 36 lines

Thanks,

C.

Quoted 30 lines
Quoted 29 lines
Quoted 2 lines

Or, even re-use the Q bit for asserted in LSIs (but report it as always 0 in the register read/write path).

I would prefer to add extra status bits. It is easier to debug.

Thanks,

C.

Quoted 19 lines
Quoted 18 lines
include/hw/ppc/xive.h +13 -0
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#13 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 180 lines · David Gibson
Quoted 172 lines · Cédric Le Goater
Quoted 160 lines · David Gibson
Quoted 52 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +22 -0
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
Quoted 15 lines
Quoted 5 lines
Quoted 5 lines
hw/intc/xive.c +0 -0
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
Quoted 8 lines

hw/intc/xive.c static void xive_source_esb_write(void *opaque, hwaddr addr, · line 215

Quoted 8 lines
hw/intc/xive.c +8 -0
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }

FYI, I have moved that common test under xive_source_pq_eoi()

Ok.

Quoted 76 lines
Quoted 67 lines
Quoted 67 lines
hw/intc/xive.c +20 -4
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
Quoted 8 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 4 lines
include/hw/ppc/xive.h +3 -0
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

Quoted 1 line

none. I can change that.

Ok.

Quoted 9 lines
Quoted 3 lines

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

yes. I would have preferred two distinct interrupt source objects but this is to be compatible with XICS, which uses only one. If we want to be able to change interrupt mode, the IRQ number space should be organized in the exact same way. Or we should change XICS also.

Quoted 4 lines
Quoted 1 line

Also, the change (a bitmap) is really small.

Hrm, but since XIVE supports thousands of irqs, it could be quite a large bitmap.

Yes. The change is small, not the bitmap.

Quoted 3 lines

It's not impossible - in fact, not really even that hard - to change the existing irq layout on xics. It does need a new machine type variant, of course.

Quoted 6 lines

I did some work on that topic a while ago :

	https://patchwork.ozlabs.org/cover/836782/

But we stopped exploring the idea. May be it was not the good approach. The PHBs LSIs would benefit from such a split though.

Quoted 7 lines

So, no, I don't think that was a good approach, but that doesn't mean other ways of rearranging the irq numbers aren't ok. The thing here is that we don't want to think of an "irq allocator" - there are some bits like that in there already, but they were always a mistake.

We have lots of irq space (both XICS and XIVE) so instead we should come up with a static mapping of irqs to devices.

yes. I would prefer that also.

We could change the spapr_irq_alloc() routine to get a block of IRQs in the range defined for a device family, and use a device id to offset in that family range ? Here are some figures :

device family block size max devices

EVENT_CLASS_EPOW              1           1  
EVENT_CLASS_HOT_PLUG          1           1   
VIO_VSCSI                     1          10  
VIO_LLAN                      1          10  
VIO_VTY                       1           5  

PCI/PHB 1024 5

C.

Quoted 64 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
Quoted 6 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 41

Quoted 2 lines
include/hw/ppc/xive.h +0 -0
     /* PQ bits */
     uint8_t      *sbe;

.. and come to that is there a reason to keep the ASSERTED bit in a separate array from sbe? AFAICT the actual 2-bit-per-irq layout is never exposed to the guests.

indeed. we always use the xive_source_pq_get/set() helpers to manipulate the PQ bits. So we could add an extra bit for the ASSERT without too much changes. Could also we put the type there or would you still prefer a bitmap ?

Quoted 6 lines
Quoted 2 lines

I'd prefer the type (config information) be separate from the P, Q, ASSERTED bits (state information).

ok. So I will use the 'uint8_t *status' for P, Q, ASSERT, which leaves 5 bits available, but I don't think it is really worth the pain to optimize the size.

Sure. I don't really care if it's packed or not.

Quoted 2 lines

The sbe array will disappear and we will have
a bitmap for the type.

We may or may not keep the type bitmap based on the discussion above, but in any case this is a good step forward.

Quoted 36 lines

Thanks,

C.

Quoted 30 lines
Quoted 29 lines
Quoted 2 lines

Or, even re-use the Q bit for asserted in LSIs (but report it as always 0 in the register read/write path).

I would prefer to add extra status bits. It is easier to debug.

Thanks,

C.

Quoted 19 lines
Quoted 18 lines
include/hw/ppc/xive.h +13 -0
@@ -127,4 +130,17 @@ uint8_t xive_source_pq_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
 
 void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon);
 
+static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno)
+{
+    assert(srcno < xsrc->nr_irqs);
+    return xsrc->status[srcno] & XIVE_STATUS_LSI;
+}
+
+static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
+                                       bool lsi)
+{
+    assert(srcno < xsrc->nr_irqs);
+    xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
+}
+
 #endif /* PPC_XIVE_H */
#14 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 197 lines · Cédric Le Goater
Quoted 180 lines · David Gibson
Quoted 172 lines · Cédric Le Goater
Quoted 160 lines · David Gibson
Quoted 52 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The 'sent' status of the LSI interrupt source is modeled with the 'P' bit of the ESB and the assertion status of the source is maintained in an array under the main sPAPRXive object. The type of the source is stored in the same array for practical reasons.

---
 hw/intc/xive.c        | 54 +++++++++++++++++++++++++++++++++++++++++++++++----
 include/hw/ppc/xive.h | 16 +++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)
hw/intc/xive.c +22 -0
index c70578759d02..060976077dd7 100644
@@ -104,6 +104,21 @@ static void xive_source_notify(XiveSource *xsrc, int srcno)
 
 }
 
+/*
+ * LSI interrupt sources use the P bit and a custom assertion flag
+ */
+static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
+{
+    uint8_t old_pq = xive_source_pq_get(xsrc, srcno);
+
+    if  (old_pq == XIVE_ESB_RESET &&
+         xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
+        xive_source_pq_set(xsrc, srcno, XIVE_ESB_PENDING);
+        return true;
+    }
+    return false;
+}
+
 /* In a two pages ESB MMIO setting, even page is the trigger page, odd
  * page is for management */
 static inline bool xive_source_is_trigger_page(hwaddr addr)
@@ -133,6 +148,13 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size)
          */
         ret = xive_source_pq_eoi(xsrc, srcno);
 
+        /* If the LSI source is still asserted, forward a new source
+         * event notification */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            if (xive_source_lsi_trigger(xsrc, srcno)) {
+                xive_source_notify(xsrc, srcno);
+            }
+        }
         break;
 
Quoted 15 lines
Quoted 5 lines
Quoted 5 lines
hw/intc/xive.c +0 -0
     case XIVE_ESB_GET:
@@ -183,6 +205,14 @@ static void xive_source_esb_write(void *opaque, hwaddr addr,
          * notification
          */
         notify = xive_source_pq_eoi(xsrc, srcno);
Quoted 8 lines

hw/intc/xive.c static void xive_source_esb_write(void *opaque, hwaddr addr, · line 215

Quoted 8 lines
hw/intc/xive.c +8 -0
+
+        /* LSI sources do not set the Q bit but they can still be
+         * asserted, in which case we should forward a new source
+         * event notification
+         */
+        if (xive_source_irq_is_lsi(xsrc, srcno)) {
+            notify = xive_source_lsi_trigger(xsrc, srcno);
+        }

FYI, I have moved that common test under xive_source_pq_eoi()

Ok.

Quoted 76 lines
Quoted 67 lines
Quoted 67 lines
hw/intc/xive.c +20 -4
         break;
 
     default:
@@ -216,8 +246,17 @@ static void xive_source_set_irq(void *opaque, int srcno, int val)
     XiveSource *xsrc = XIVE_SOURCE(opaque);
     bool notify = false;
 
-    if (val) {
-        notify = xive_source_pq_trigger(xsrc, srcno);
+    if (xive_source_irq_is_lsi(xsrc, srcno)) {
+        if (val) {
+            xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
+        } else {
+            xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
+        }
+        notify = xive_source_lsi_trigger(xsrc, srcno);
+    } else {
+        if (val) {
+            notify = xive_source_pq_trigger(xsrc, srcno);
+        }
     }
 
     /* Forward the source event notification for routing */
@@ -234,13 +273,13 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
                    xsrc->offset, xsrc->offset + xsrc->nr_irqs - 1);
     for (i = 0; i < xsrc->nr_irqs; i++) {
         uint8_t pq = xive_source_pq_get(xsrc, i);
-        uint32_t lisn = i  + xsrc->offset;
 
         if (pq == XIVE_ESB_OFF) {
             continue;
         }
 
-        monitor_printf(mon, "  %4x %c%c\n", lisn,
+        monitor_printf(mon, "  %4x %s %c%c\n", i + xsrc->offset,
+                       xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
                        pq & XIVE_ESB_VAL_P ? 'P' : '-',
                        pq & XIVE_ESB_VAL_Q ? 'Q' : '-');
     }
@@ -249,6 +288,12 @@ void xive_source_pic_print_info(XiveSource *xsrc, Monitor *mon)
 static void xive_source_reset(DeviceState *dev)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    int i;
+
+    /* Keep the IRQ type */
+    for (i = 0; i < xsrc->nr_irqs; i++) {
+        xsrc->status[i] &= ~XIVE_STATUS_ASSERTED;
+    }
 
     /* SBEs are initialized to 0b01 which corresponds to "ints off" */
     memset(xsrc->sbe, 0x55, xsrc->sbe_size);
@@ -273,6 +318,7 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
 
     xsrc->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc,
                                      xsrc->nr_irqs);
+    xsrc->status = g_malloc0(xsrc->nr_irqs);
 
     /* Allocate the SBEs (State Bit Entry). 2 bits, so 4 entries per byte */
     xsrc->sbe_size = DIV_ROUND_UP(xsrc->nr_irqs, 4);
include/hw/ppc/xive.h +0 -0
index d92a50519edf..0b76dd278d9b 100644
@@ -33,6 +33,9 @@ typedef struct XiveSource {
     uint32_t     nr_irqs;
     uint32_t     offset;
Quoted 8 lines

include/hw/ppc/xive.h typedef struct XiveSource { · line 38

Quoted 4 lines
include/hw/ppc/xive.h +3 -0
     qemu_irq     *qirqs;
+#define XIVE_STATUS_LSI         0x1
+#define XIVE_STATUS_ASSERTED    0x2
+    uint8_t      *status;

I don't love the idea of mixing configuration information (STATUS_LSI) with runtime state information (ASSERTED) in the same field. Any reason not to have these as parallel bitmaps.

Quoted 1 line

none. I can change that.

Ok.

Quoted 9 lines
Quoted 3 lines

Come to that.. is there a compelling reason to allow any individual irq to be marked LSI or MSI, rather than using separate XiveSource objects for MSIs and LSIs?

yes. I would have preferred two distinct interrupt source objects but this is to be compatible with XICS, which uses only one. If we want to be able to change interrupt mode, the IRQ number space should be organized in the exact same way. Or we should change XICS also.

Quoted 4 lines
Quoted 1 line

Also, the change (a bitmap) is really small.

Hrm, but since XIVE supports thousands of irqs, it could be quite a large bitmap.

Yes. The change is small, not the bitmap.

Quoted 3 lines

It's not impossible - in fact, not really even that hard - to change the existing irq layout on xics. It does need a new machine type variant, of course.

Quoted 6 lines

I did some work on that topic a while ago :

	https://patchwork.ozlabs.org/cover/836782/

But we stopped exploring the idea. May be it was not the good approach. The PHBs LSIs would benefit from such a split though.

Quoted 7 lines

So, no, I don't think that was a good approach, but that doesn't mean other ways of rearranging the irq numbers aren't ok. The thing here is that we don't want to think of an "irq allocator" - there are some bits like that in there already, but they were always a mistake.

We have lots of irq space (both XICS and XIVE) so instead we should come up with a static mapping of irqs to devices.

yes. I would prefer that also.

We could change the spapr_irq_alloc() routine to get a block of IRQs in the range defined for a device family, and use a device id to offset in that family range ? Here are some figures :

device family block size max devices

Quoted 7 lines
EVENT_CLASS_EPOW              1           1  
EVENT_CLASS_HOT_PLUG          1           1   
VIO_VSCSI                     1          10  
VIO_LLAN                      1          10  
VIO_VTY                       1           5  

PCI/PHB 1024 5

No, I'm thinking we should eliminate spapr_irq_alloc() entirely. Well, ok, not entirely, we'll still need it for the old machine types. But remove it's use for the current machine type completely.

Instead we have an explicit map of ranges for various purposes. The one-off things like EPOW and HOTPLUG can have plain constant values. PCI LSIs will be calculated as something like PCI_IRQ_BASE + <phb index>*4 + <irq pin>. The VIO devices we handle as VIO_BASE + <reg value> or something.

MSIs will still need some sort of allocation, but we can do that within a range set aside for them.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#15 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 32 lines · David Gibson
Quoted 21 lines
Quoted 6 lines
Quoted 6 lines

I did some work on that topic a while ago :

	https://patchwork.ozlabs.org/cover/836782/

But we stopped exploring the idea. May be it was not the good approach. The PHBs LSIs would benefit from such a split though.

Quoted 7 lines

So, no, I don't think that was a good approach, but that doesn't mean other ways of rearranging the irq numbers aren't ok. The thing here is that we don't want to think of an "irq allocator" - there are some bits like that in there already, but they were always a mistake.

We have lots of irq space (both XICS and XIVE) so instead we should come up with a static mapping of irqs to devices.

yes. I would prefer that also.

We could change the spapr_irq_alloc() routine to get a block of IRQs in the range defined for a device family, and use a device id to offset in that family range ? Here are some figures :

device family block size max devices

Quoted 7 lines
EVENT_CLASS_EPOW              1           1  
EVENT_CLASS_HOT_PLUG          1           1   
VIO_VSCSI                     1          10  
VIO_LLAN                      1          10  
VIO_VTY                       1           5  

PCI/PHB 1024 5

No, I'm thinking we should eliminate spapr_irq_alloc() entirely. Well, ok, not entirely, we'll still need it for the old machine types. But remove it's use for the current machine type completely.

Quoted 8 lines

Instead we have an explicit map of ranges for various purposes. The one-off things like EPOW and HOTPLUG can have plain constant values. PCI LSIs will be calculated as something like PCI_IRQ_BASE + <phb index>*4 + <irq pin>. The VIO devices we handle as VIO_BASE + <reg value> or something.

MSIs will still need some sort of allocation, but we can do that within a range set aside for them.

Should we address the static mapping of irqs before introducing XIVE ?

I don't think it changes much of the architecture now that the allocator is under the machine. However, I wonder what would be the impact of PHB hotplug.

C.

#16 lore raw

Re: [Qemu-devel] [PATCH v3 02/35] ppc/xive: add support for the LSI interrupt sources

Quoted 39 lines · Cédric Le Goater
Quoted 38 lines · David Gibson
Quoted 21 lines
Quoted 6 lines
Quoted 6 lines

I did some work on that topic a while ago :

	https://patchwork.ozlabs.org/cover/836782/

But we stopped exploring the idea. May be it was not the good approach. The PHBs LSIs would benefit from such a split though.

Quoted 7 lines

So, no, I don't think that was a good approach, but that doesn't mean other ways of rearranging the irq numbers aren't ok. The thing here is that we don't want to think of an "irq allocator" - there are some bits like that in there already, but they were always a mistake.

We have lots of irq space (both XICS and XIVE) so instead we should come up with a static mapping of irqs to devices.

yes. I would prefer that also.

We could change the spapr_irq_alloc() routine to get a block of IRQs in the range defined for a device family, and use a device id to offset in that family range ? Here are some figures :

device family block size max devices

Quoted 7 lines
EVENT_CLASS_EPOW              1           1  
EVENT_CLASS_HOT_PLUG          1           1   
VIO_VSCSI                     1          10  
VIO_LLAN                      1          10  
VIO_VTY                       1           5  

PCI/PHB 1024 5

No, I'm thinking we should eliminate spapr_irq_alloc() entirely. Well, ok, not entirely, we'll still need it for the old machine types. But remove it's use for the current machine type completely.

Instead we have an explicit map of ranges for various purposes. The one-off things like EPOW and HOTPLUG can have plain constant values. PCI LSIs will be calculated as something like PCI_IRQ_BASE + <phb index>*4 + <irq pin>. The VIO devices we handle as VIO_BASE + <reg value> or something.

Quoted 4 lines
Quoted 2 lines

MSIs will still need some sort of allocation, but we can do that within a range set aside for them.

Should we address the static mapping of irqs before introducing XIVE ?

Yes, I think so.

Quoted 3 lines

I don't think it changes much of the architecture now that the allocator is under the machine. However, I wonder what would be the impact of PHB hotplug.

I don't think it should be too bad. We now require that PHBs have the 'index' parameter set, and that won't change with hotplug. We can then set aside a region of irq #s for each index of PHB.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#17 lore raw

PATCH v3 3/35 ppc/xive: introduce the XiveFabric interface

In reply to Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +36 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
Signature · 1 line
2.13.6
#18 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 46 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 10 lines
hw/intc/xive.c +7 -0
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 77 lines
hw/intc/xive.c +12 -0
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#19 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

	static void pnv_psi_notify(XiveFabric *xf, uint32_t lisn)
	{
	    PnvPsi *psi = PNV_PSI(xf);
	    uint64_t notif_port =
	        psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)];
	    bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID;
	    uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID;
	    uint32_t data = cpu_to_be32(lisn);
	
	    if (valid) {
	        cpu_physical_memory_write(notify_addr, &data, sizeof(data));
	    }
	}

The PnvXive model handles the load and forwards to the fabric again.

The IPIs under PowerNV do not need an extra hop so they reach the routing routine directly without the extra notify() hop.

However, PowerNV at the end should be using xive_fabric_route() but there are some differences on how the NVT registers are updated (HV vs. OS mode) and it's not handled yet so it uses a notify() handler. But is should disappear and call directly xive_fabric_route() in a near future.

May be, XiveFabricNotifier would be a better name for this feature ? I am adding a few ops later which are more related to routing.

Thanks,

C.

Quoted 79 lines
Quoted 78 lines
hw/intc/xive.c +12 -0
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
#20 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit, that would then (after possible masking) forward on to the overall xive fabric?

That seems like a property of the source object, rather than a property of the fabric. Indeed varying this by source object would require the objects have a different xive pointer, when I thought the idea was that the XiveFabric was global.

Quoted 114 lines
	static void pnv_psi_notify(XiveFabric *xf, uint32_t lisn)
	{
	    PnvPsi *psi = PNV_PSI(xf);
	    uint64_t notif_port =
	        psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)];
	    bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID;
	    uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID;
	    uint32_t data = cpu_to_be32(lisn);
	
	    if (valid) {
	        cpu_physical_memory_write(notify_addr, &data, sizeof(data));
	    }
	}

The PnvXive model handles the load and forwards to the fabric again.

The IPIs under PowerNV do not need an extra hop so they reach the routing routine directly without the extra notify() hop.

However, PowerNV at the end should be using xive_fabric_route() but there are some differences on how the NVT registers are updated (HV vs. OS mode) and it's not handled yet so it uses a notify() handler. But is should disappear and call directly xive_fabric_route() in a near future.

May be, XiveFabricNotifier would be a better name for this feature ? I am adding a few ops later which are more related to routing.

Thanks,

C.

Quoted 79 lines
Quoted 78 lines
hw/intc/xive.c +12 -0
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#21 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

The PHB4 sources should be the same.

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Quoted 4 lines

rather than a
property of the fabric. Indeed varying this by source object would require the objects have a different xive pointer, when I thought the idea was that the XiveFabric was global.

When a notification is forwarded, the sources needs to call an interface which generally is implemented by the source owner, which is not necessarily the main IC.

Quoted 115 lines
Quoted 114 lines
	static void pnv_psi_notify(XiveFabric *xf, uint32_t lisn)
	{
	    PnvPsi *psi = PNV_PSI(xf);
	    uint64_t notif_port =
	        psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)];
	    bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID;
	    uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID;
	    uint32_t data = cpu_to_be32(lisn);
	
	    if (valid) {
	        cpu_physical_memory_write(notify_addr, &data, sizeof(data));
	    }
	}

The PnvXive model handles the load and forwards to the fabric again.

The IPIs under PowerNV do not need an extra hop so they reach the routing routine directly without the extra notify() hop.

However, PowerNV at the end should be using xive_fabric_route() but there are some differences on how the NVT registers are updated (HV vs. OS mode) and it's not handled yet so it uses a notify() handler. But is should disappear and call directly xive_fabric_route() in a near future.

May be, XiveFabricNotifier would be a better name for this feature ? I am adding a few ops later which are more related to routing.

Thanks,

C.

Quoted 79 lines
Quoted 78 lines
hw/intc/xive.c +12 -0
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
#22 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 87 lines · Cédric Le Goater
Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

Quoted 7 lines

The PHB4 sources should be the same.

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Hrm. Apparently I'm missing something, I'm really not getting what you're trying to explain here.

Quoted 3 lines
Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Maybe..?

Quoted 4 lines
Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Hmm. Isn't the source object also responsible for forwarding the interrupt to something up the chain (whatever that is)?

Quoted 8 lines
Quoted 5 lines

rather than a
property of the fabric. Indeed varying this by source object would require the objects have a different xive pointer, when I thought the idea was that the XiveFabric was global.

When a notification is forwarded, the sources needs to call an interface which generally is implemented by the source owner,

I'm not quite sure what you mean by "source owner".

Quoted 118 lines

which is not necessarily the main IC.

Quoted 115 lines
Quoted 114 lines
	static void pnv_psi_notify(XiveFabric *xf, uint32_t lisn)
	{
	    PnvPsi *psi = PNV_PSI(xf);
	    uint64_t notif_port =
	        psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)];
	    bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID;
	    uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID;
	    uint32_t data = cpu_to_be32(lisn);
	
	    if (valid) {
	        cpu_physical_memory_write(notify_addr, &data, sizeof(data));
	    }
	}

The PnvXive model handles the load and forwards to the fabric again.

The IPIs under PowerNV do not need an extra hop so they reach the routing routine directly without the extra notify() hop.

However, PowerNV at the end should be using xive_fabric_route() but there are some differences on how the NVT registers are updated (HV vs. OS mode) and it's not handled yet so it uses a notify() handler. But is should disappear and call directly xive_fabric_route() in a near future.

May be, XiveFabricNotifier would be a better name for this feature ? I am adding a few ops later which are more related to routing.

Thanks,

C.

Quoted 79 lines
Quoted 78 lines
hw/intc/xive.c +12 -0
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#23 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 90 lines · David Gibson
Quoted 89 lines · Cédric Le Goater
Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

The PHB4 sources should be the same.

Quoted 8 lines
Quoted 5 lines

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Hrm. Apparently I'm missing something, I'm really not getting what you're trying to explain here.

I see that. Let's try again.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Maybe..?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Hmm. Isn't the source object also responsible for forwarding the interrupt to something up the chain (whatever that is)?

Yes but it can not forward directly. The XiveSource is generic and can only call a handler :

	xfc->notify(xsrc->xive, srcno + xsrc->offset);

The device model owner, the parent of the XiveSource object, would do the real forward.

It's very similar to what we have today with XICS :

- The sPAPR model has an ICSState
- The PnvPSI model has an ICSState
- The PnvPHB3 model has two ICSStates

and the 'xics' pointer in ICSState points to the 'interrupt unit' of the machine to do resends and to grab ICPs. So it used for routing essentially.

in Xive

- sPAPRXive model has a XiveSource
- PnvXive model has a XiveSource
- PnvPSI model has a XiveSource
- PnvPHB4 model should have also.

and the 'xive' pointer in XiveSource points to the parent object, which will handle the event notification forwarding or routing.

C.

Quoted 9 lines
Quoted 7 lines
Quoted 4 lines

rather than a
property of the fabric. Indeed varying this by source object would require the objects have a different xive pointer, when I thought the idea was that the XiveFabric was global.

When a notification is forwarded, the sources needs to call an interface which generally is implemented by the source owner,

I'm not quite sure what you mean by "source owner".

The parent object.

Quoted 119 lines
Quoted 118 lines

which is not necessarily the main IC.

Quoted 115 lines
Quoted 114 lines
	static void pnv_psi_notify(XiveFabric *xf, uint32_t lisn)
	{
	    PnvPsi *psi = PNV_PSI(xf);
	    uint64_t notif_port =
	        psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)];
	    bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID;
	    uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID;
	    uint32_t data = cpu_to_be32(lisn);
	
	    if (valid) {
	        cpu_physical_memory_write(notify_addr, &data, sizeof(data));
	    }
	}

The PnvXive model handles the load and forwards to the fabric again.

The IPIs under PowerNV do not need an extra hop so they reach the routing routine directly without the extra notify() hop.

However, PowerNV at the end should be using xive_fabric_route() but there are some differences on how the NVT registers are updated (HV vs. OS mode) and it's not handled yet so it uses a notify() handler. But is should disappear and call directly xive_fabric_route() in a near future.

May be, XiveFabricNotifier would be a better name for this feature ? I am adding a few ops later which are more related to routing.

Thanks,

C.

Quoted 79 lines
Quoted 78 lines
hw/intc/xive.c +12 -0
 }
 
 /*
@@ -302,6 +325,17 @@ static void xive_source_reset(DeviceState *dev)
 static void xive_source_realize(DeviceState *dev, Error **errp)
 {
     XiveSource *xsrc = XIVE_SOURCE(dev);
+    Object *obj;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "xive", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'xive' not found: ");
+        return;
+    }
+
+    xsrc->xive = XIVE_FABRIC(obj);
 
     if (!xsrc->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater than 0");
@@ -376,6 +410,7 @@ static const TypeInfo xive_source_info = {
 static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
+    type_register_static(&xive_fabric_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/xive.h +25 -0
index 0b76dd278d9b..4fcae2c763e6 100644
@@ -12,6 +12,8 @@
 
 #include "hw/sysbus.h"
 
+typedef struct XiveFabric XiveFabric;
+
 /*
  * XIVE Interrupt Source
  */
@@ -46,6 +48,8 @@ typedef struct XiveSource {
     hwaddr       esb_base;
     uint32_t     esb_shift;
     MemoryRegion esb_mmio;
+
+    XiveFabric   *xive;
 } XiveSource;
 
 /*
@@ -143,4 +147,25 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
     xsrc->status[srcno] |= lsi ? XIVE_STATUS_LSI : 0;
 }
 
+/*
+ * XIVE Fabric
+ */
+
+typedef struct XiveFabric {
+    Object parent;
+} XiveFabric;
+
+#define TYPE_XIVE_FABRIC "xive-fabric"
+#define XIVE_FABRIC(obj)                                     \
+    OBJECT_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_CLASS(klass)                                     \
+    OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC)
+#define XIVE_FABRIC_GET_CLASS(obj)                                   \
+    OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC)
+
+typedef struct XiveFabricClass {
+    InterfaceClass parent;
+    void (*notify)(XiveFabric *xf, uint32_t lisn);
+} XiveFabricClass;
+
 #endif /* PPC_XIVE_H */
#24 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 113 lines · Cédric Le Goater
Quoted 90 lines · David Gibson
Quoted 89 lines · Cédric Le Goater
Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

The PHB4 sources should be the same.

Quoted 8 lines
Quoted 5 lines

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Hrm. Apparently I'm missing something, I'm really not getting what you're trying to explain here.

I see that. Let's try again.

Quoted 11 lines
Quoted 3 lines
Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Maybe..?

Quoted 4 lines
Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Quoted 7 lines
Quoted 2 lines

Hmm. Isn't the source object also responsible for forwarding the interrupt to something up the chain (whatever that is)?

Yes but it can not forward directly. The XiveSource is generic and can only call a handler :

	xfc->notify(xsrc->xive, srcno + xsrc->offset);

But.. your patch doesn't do that always, it's conditional which I still don't understand.

Quoted 2 lines

The device model owner, the parent of the XiveSource object, would do the real forward.

Why? I mean the XiveSource basically represents the xive irq related logic of the PHB or whatever, why would it not represent *all* of that, rather than just the ESB bits, meaning the owner has to have some more xive logic for the forwarding.

Note that I don't think the fact that some sources notify via mmio and some are internal really matters. It's not like we're modelling the power bus down to the wire-transaction level.

Quoted 9 lines

It's very similar to what we have today with XICS :

- The sPAPR model has an ICSState
- The PnvPSI model has an ICSState
- The PnvPHB3 model has two ICSStates

and the 'xics' pointer in ICSState points to the 'interrupt unit' of the machine to do resends and to grab ICPs. So it used for routing essentially.

Hmm. I think you and I are looking at XICSFabric kind of differently. As I see it, it's not really an active component at all. Rather it's basically a global "map" of the xics components so that they can find each other.

Quoted 8 lines

in Xive

- sPAPRXive model has a XiveSource
- PnvXive model has a XiveSource
- PnvPSI model has a XiveSource
- PnvPHB4 model should have also.

and the 'xive' pointer in XiveSource points to the parent object,

Uh.. yeah.. the xics pointer in ICS units doesn't point to the parent object, except maybe by accident. It's absolutely intended to be global, and so points to the machine.

Quoted 1 line

which will handle the event notification forwarding or routing.

Ok, how about this for a partial model. We have:

XiveSource objects:
* Owns an ESB table
* Knows the mapping of its local irq offsets to global irq numbers
* Provides the mmio interface for ESB manipulation
* When neccessary, notifies a new interrupt to a XiveRouter

XiveRouter objects:
* Responsible for a fixed range of global irq numbers
* Owns an IVT (but what that means can vary, see below)
* When notified of an irq, routes it to the appropriate EQ (haven't thought about this part yet)
* Abstract class - needs subclasses to define how to get IVEs

XiveFabric interface:
* Lets XIVE components locate each other
* get_router() method: maps a global irq number to XiveRouter object
* Always global (implemented on the machine)

On pseries we have:

? XiveSource objects. We probably only need one, but 1 for LSI and one for MSI might be convenient. More wouldn't break the model

1 sPAPRXiveRouter. This is a subclass of XiveRouter that holds the IVT internall (and migrates it).

the XiveFabric implementation always returns the single global router for get_router()

On powernv we have:
N XiveSource objects. Some in PHBs, some extra ones on each chip

(#chips) PowerXiveRouter objects. This subclass of XiveRouter stores the register giving the IVT base address and migrates that, but the IVT contents are in RAM

the XiveFabric get_router() implementation returns the right chip's router based on the irq number

Obviously the router->EQ sides still needs a bunch of thought.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#25 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 117 lines · David Gibson
Quoted 116 lines · Cédric Le Goater
Quoted 90 lines · David Gibson
Quoted 89 lines · Cédric Le Goater
Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

The PHB4 sources should be the same.

Quoted 8 lines
Quoted 5 lines

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Hrm. Apparently I'm missing something, I'm really not getting what you're trying to explain here.

I see that. Let's try again.

Quoted 13 lines
Quoted 3 lines
Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Maybe..?

Quoted 4 lines
Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Hmm. Isn't the source object also responsible for forwarding the interrupt to something up the chain (whatever that is)?

Quoted 7 lines
Quoted 4 lines

Yes but it can not forward directly. The XiveSource is generic and can only call a handler :

	xfc->notify(xsrc->xive, srcno + xsrc->offset);

But.. your patch doesn't do that always, it's conditional which I still don't understand.

Because at the end of the notify/forward chain, you route.

Quoted 4 lines
Quoted 2 lines

The device model owner, the parent of the XiveSource object, would do the real forward.

Why?

because, in my idea of the XiveSource concept, it does not have the logic to do so: the register for the MMIO address to use, another one for the IVT offset, etc

Quoted 4 lines

I mean the XiveSource basically represents the xive irq related logic of the PHB or whatever, why would it not represent *all* of that, rather than just the ESB bits, meaning the owner has to have some more xive logic for the forwarding.

ok. This is where we diverge in the concept.

The PQ bits, the ESB MMIO region handlers can be easily shared between the different device models. They are the common part of devices with XIVE interrupt sources.

Quoted 3 lines

Note that I don't think the fact that some sources notify via mmio and some are internal really matters. It's not like we're modelling the power bus down to the wire-transaction level.

yes but the configuration of the devices are different. pnv devices will have registers accessible through MMIO or XSCOM and configured by the firmware. spapr is all set up by QEMU.

Quoted 6 lines
Quoted 6 lines

It's very similar to what we have today with XICS :

- The sPAPR model has an ICSState
- The PnvPSI model has an ICSState
- The PnvPHB3 model has two ICSStates

Quoted 8 lines
Quoted 3 lines

and the 'xics' pointer in ICSState points to the 'interrupt unit' of the machine to do resends and to grab ICPs. So it used for routing essentially.

Hmm. I think you and I are looking at XICSFabric kind of differently. As I see it, it's not really an active component at all. Rather it's basically a global "map" of the xics components so that they can find each other.

ok. I am not that far either.

Quoted 7 lines
Quoted 7 lines

in Xive

- sPAPRXive model has a XiveSource
- PnvXive model has a XiveSource
- PnvPSI model has a XiveSource
- PnvPHB4 model should have also.

Quoted 5 lines
Quoted 1 line

and the 'xive' pointer in XiveSource points to the parent object,

Uh.. yeah.. the xics pointer in ICS units doesn't point to the parent object, except maybe by accident. It's absolutely intended to be global, and so points to the machine.

yes. I agree.

XIVE has more layers and visible components due to the internal tables used for routing.

Quoted 8 lines
Quoted 1 line

which will handle the event notification forwarding or routing.

Ok, how about this for a partial model. We have:

XiveSource objects:
* Owns an ESB table
* Knows the mapping of its local irq offsets to global irq numbers

That is the 'offset' attribute I suppose. This is set at runtime for the powernv devices. For pseries, we should need it for passthrough. I think. I haven't looked at that part yet.

Quoted 2 lines

* Provides the mmio interface for ESB manipulation
* When neccessary, notifies a new interrupt to a XiveRouter

ok. I think that what we have today fits the idea then.

Quoted 3 lines

XiveRouter objects:
* Responsible for a fixed range of global irq numbers
* Owns an IVT (but what that means can vary, see below)

the size of the IVT table is determined at runtime for powernv.

Quoted 2 lines

* When notified of an irq, routes it to the appropriate EQ (haven't thought about this part yet)

we will need a class handler to get EQs

Quoted 1 line

* Abstract class - needs subclasses to define how to get IVEs

OK. We will need a few other ops. The router needs to :

- get IVEs
- get EQ descriptors
- update OS EQs (write event data in OS RAM)
- update EQ descriptors (to set EQ descriptor index & toggle)
- get an NVT/VP (to notify CPUs)

For powernv, we should also consider updating the NVT/VP. It can be done later.

Quoted 2 lines

XiveFabric interface:
* Lets XIVE components locate each other

hmm, it should be a chain :

	source -> router -> presenter -> cpu

So the components should not have to locate each other. The presenter does not know about the source for instance. Only the sources need to forward events to the main controller logic doing the routing.

Quoted 2 lines

* get_router() method: maps a global irq number to XiveRouter object

Ah. you are thinking about the multichip case under powernv. I need to look at that more closely. But XIVE has a concept of block which is used by skiboot to map a chip to a block and the XIVE tables have a block field.

Quoted 1 line

* Always global (implemented on the machine)

OK. this is more or less the object modeling the main interrupt controller ? What I called sPAPRXive in the current patchset.

Quoted 4 lines

On pseries we have:

? XiveSource objects. We probably only need one, but 1 for LSI and one for MSI might be convenient.

It's not too ugly for the moment. If we create a source object for LSIs only that might be more complex for passthrough devices and their associated ESB MMIO region.

side note :

LSIs work under TCG, and used to work under KVM until we removed the StoreEOI support. Since the EOI is now different, we need to find a way to handle EOI for guest virtual LSIs which are not LSIs for the host. We can still change the Linux spapr backend or have QEMU handle the EOI for virtual LSIs. This is on my TODO list.

Quoted 1 line

More wouldn't break the model

It should not. Initially the pachset had two source objects :
one for IPIs and one for the virtual devices interrupts.

Quoted 2 lines

1 sPAPRXiveRouter. This is a subclass of XiveRouter that holds the IVT internall (and migrates it).

OK.

Quoted 2 lines

the XiveFabric implementation always returns the single global router for get_router()

we can do that.

Do we gather all XIVE components objects under an object 'sPAPRXive' modeling that way the main interrupt controller of the machine ? It should not have any state but it will hold the TIMA region most certainly, and the addresses where to map the ESB and TIMA regions.

KVM support will bring extra needs.

Quoted 3 lines

On powernv we have:
N XiveSource objects. Some in PHBs, some extra ones on each chip

yes and PSI to start with.

Quoted 8 lines

(#chips) PowerXiveRouter objects. This subclass of XiveRouter stores the register giving the IVT base address and migrates that, but the IVT contents are in RAM

the XiveFabric get_router() implementation returns the right chip's router based on the irq number

Obviously the router->EQ sides still needs a bunch of thought.

These are all routing tables :

- IVT
- EQDT
- VPDT

Thanks,

C.

#26 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 121 lines · Cédric Le Goater
Quoted 120 lines · David Gibson
Quoted 119 lines · Cédric Le Goater
Quoted 90 lines · David Gibson
Quoted 89 lines · Cédric Le Goater
Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

The PHB4 sources should be the same.

Quoted 8 lines
Quoted 5 lines

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Hrm. Apparently I'm missing something, I'm really not getting what you're trying to explain here.

I see that. Let's try again.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Maybe..?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Hmm. Isn't the source object also responsible for forwarding the interrupt to something up the chain (whatever that is)?

Yes but it can not forward directly. The XiveSource is generic and can only call a handler :

Quoted 6 lines
Quoted 4 lines
Quoted 1 line
	xfc->notify(xsrc->xive, srcno + xsrc->offset);

But.. your patch doesn't do that always, it's conditional which I still don't understand.

Because at the end of the notify/forward chain, you route.

Hrm. I'm really not understanding this notify/forward thing as distinct from routing. I mean, from the description here it sounds kind of like cascaded interrupt controllers, which qemu already has mechanisms to handle, but I didn't think (most) POWER9 devices worked like that.

Quoted 14 lines
Quoted 4 lines
Quoted 2 lines

The device model owner, the parent of the XiveSource object, would do the real forward.

Why?

because, in my idea of the XiveSource concept, it does not have the logic to do so: the register for the MMIO address to use, another one for the IVT offset, etc

Quoted 4 lines

I mean the XiveSource basically represents the xive irq related logic of the PHB or whatever, why would it not represent *all* of that, rather than just the ESB bits, meaning the owner has to have some more xive logic for the forwarding.

Quoted 5 lines

ok. This is where we diverge in the concept.

The PQ bits, the ESB MMIO region handlers can be easily shared between the different device models. They are the common part of devices with XIVE interrupt sources.

Sure, but QOM subclasses are a thing, which sounds like a better way of doing this than having to have extra XIVE logic in all the parent devices.

Quoted 7 lines
Quoted 3 lines

Note that I don't think the fact that some sources notify via mmio and some are internal really matters. It's not like we're modelling the power bus down to the wire-transaction level.

yes but the configuration of the devices are different. pnv devices will have registers accessible through MMIO or XSCOM and configured by the firmware. spapr is all set up by QEMU.

Ah.. ok. Actually modelling the mmio forwards probably makes sense then. I still think it makes more sense in a pnv XiveSource subclass rather than putting it in the containing device.

Quoted 26 lines
Quoted 6 lines
Quoted 6 lines

It's very similar to what we have today with XICS :

- The sPAPR model has an ICSState
- The PnvPSI model has an ICSState
- The PnvPHB3 model has two ICSStates

Quoted 8 lines
Quoted 3 lines

and the 'xics' pointer in ICSState points to the 'interrupt unit' of the machine to do resends and to grab ICPs. So it used for routing essentially.

Hmm. I think you and I are looking at XICSFabric kind of differently. As I see it, it's not really an active component at all. Rather it's basically a global "map" of the xics components so that they can find each other.

ok. I am not that far either.

Quoted 9 lines
Quoted 8 lines

in Xive

- sPAPRXive model has a XiveSource
- PnvXive model has a XiveSource
- PnvPSI model has a XiveSource
- PnvPHB4 model should have also.

and the 'xive' pointer in XiveSource points to the parent object,

Quoted 8 lines
Quoted 3 lines

Uh.. yeah.. the xics pointer in ICS units doesn't point to the parent object, except maybe by accident. It's absolutely intended to be global, and so points to the machine.

yes. I agree.

XIVE has more layers and visible components due to the internal tables used for routing.

Right.

Quoted 11 lines
Quoted 8 lines
Quoted 1 line

which will handle the event notification forwarding or routing.

Ok, how about this for a partial model. We have:

XiveSource objects:
* Owns an ESB table
* Knows the mapping of its local irq offsets to global irq numbers

That is the 'offset' attribute I suppose. This is set at runtime for the powernv devices.

Ok, so that offset is effectively a register (which will have to be migrated), rather than a device property.

Quoted 2 lines

For pseries, we should need it for
passthrough. I think. I haven't looked at that part yet.

Uh.. I really hope not. AIUI the offsets are decided by the platform rather than the guest in this case, yes? In which case if we can't count on a fixed offset even in passthrough mode, then migration is basically impossible.

Quoted 4 lines
Quoted 2 lines

* Provides the mmio interface for ESB manipulation
* When neccessary, notifies a new interrupt to a XiveRouter

ok. I think that what we have today fits the idea then.

Yes, I think so to - just clarifying in the context of the rest of this proposal.

Quoted 5 lines
Quoted 3 lines

XiveRouter objects:
* Responsible for a fixed range of global irq numbers
* Owns an IVT (but what that means can vary, see below)

the size of the IVT table is determined at runtime for powernv.

That's fine - both the size and base of the IVT will be registers of the powernv variant of the device.

Quoted 4 lines
Quoted 2 lines

* When notified of an irq, routes it to the appropriate EQ (haven't thought about this part yet)

we will need a class handler to get EQs

Sure.

Quoted 7 lines
Quoted 1 line

* Abstract class - needs subclasses to define how to get IVEs

OK. We will need a few other ops. The router needs to :

- get IVEs
- get EQ descriptors
- update OS EQs (write event data in OS RAM)

IIUC the actual EQ (as opposed to its descriptor) will be in guest RAM for both powernv and spapr, so this can be common (utiliizing a subclass hook to locate the EQ base address).

Quoted 2 lines

- update EQ descriptors (to set EQ descriptor index & toggle)
- get an NVT/VP (to notify CPUs)

Getting NVT/VP sounds more like it would belong in the XiveFabric than the router, but I haven't looked at this in detail yet.

Quoted 6 lines

For powernv, we should also consider updating the NVT/VP. It can be done later.

Quoted 2 lines

XiveFabric interface:
* Lets XIVE components locate each other

Quoted 7 lines

hmm, it should be a chain :

	source -> router -> presenter -> cpu

So the components should not have to locate each other. The presenter does not know about the source for instance. Only the sources need to forward events to the main controller logic doing the routing.

source -> router

From the above sounds like we can maybe make this just a router property in the source, rather than a lookup through a fabric.

router -> presenter

Here we might still need a "fabric". By defintion the router can direct to a bunch of different presenters, so it needs some kind of map to find them, no?

presenter -> cpu

This one's trivial; just the cpu->intc pointer or similar.

Quoted 7 lines
Quoted 2 lines

* get_router() method: maps a global irq number to XiveRouter object

Ah. you are thinking about the multichip case under powernv. I need to look at that more closely. But XIVE has a concept of block which is used by skiboot to map a chip to a block and the XIVE tables have a block field.

Hmm.. I think we need to understand this to make a real model. I'd thought the block number would just be the chip number somewhere in the high bits of the global irq, but sounds like there might be yet another indirection here.

Quoted 4 lines
Quoted 1 line

* Always global (implemented on the machine)

OK. this is more or less the object modeling the main interrupt controller ? What I called sPAPRXive in the current patchset.

No. This is *always* global - assuming we need it at all - even on multichip powernv.

Quoted 9 lines
Quoted 4 lines

On pseries we have:

? XiveSource objects. We probably only need one, but 1 for LSI and one for MSI might be convenient.

It's not too ugly for the moment. If we create a source object for LSIs only that might be more complex for passthrough devices and their associated ESB MMIO region.

Quoted 6 lines

side note :

LSIs work under TCG, and used to work under KVM until we removed the StoreEOI support. Since the EOI is now different, we need to find a way to handle EOI for guest virtual LSIs which are not LSIs for the host.

I can't quite picture that case - can you give a concrete example?

Quoted 12 lines

We can still change the Linux spapr
backend or have QEMU handle the EOI for virtual LSIs. This is on my TODO list.

Quoted 1 line

More wouldn't break the model

It should not. Initially the pachset had two source objects :
one for IPIs and one for the virtual devices interrupts.

Quoted 2 lines

1 sPAPRXiveRouter. This is a subclass of XiveRouter that holds the IVT internall (and migrates it).

Quoted 6 lines

OK.

Quoted 2 lines

the XiveFabric implementation always returns the single global router for get_router()

we can do that.

Again, if it's true that each source object always forwards to just one router, then we don't need a get_router(), we can just use a link property.

Quoted 2 lines

Do we gather all XIVE components objects under an object 'sPAPRXive' modeling that way the main interrupt controller of the machine ?

No, I don't think so.

Quoted 3 lines

It should not have any state but it will hold the TIMA region most certainly, and the addresses where to map the ESB and TIMA regions.

Uh.. I'd expect the TIMAs to be held by the NVT objects, which we haven't covered yet.

Quoted 15 lines

KVM support will bring extra needs.

Quoted 3 lines

On powernv we have:
N XiveSource objects. Some in PHBs, some extra ones on each chip

yes and PSI to start with.

Quoted 7 lines

(#chips) PowerXiveRouter objects. This subclass of XiveRouter stores the register giving the IVT base address and migrates that, but the IVT contents are in RAM

the XiveFabric get_router() implementation returns the right chip's router based on the irq number

Quoted 7 lines
Quoted 1 line

Obviously the router->EQ sides still needs a bunch of thought.

These are all routing tables :

- IVT
- EQDT
- VPDT

Thinking about what you've said, I'm thinking maybe what we need on the source side is two versions which don't exactly correspond to spapr vs powernv:

InBandXiveSource: this one's notify behaviour is to issue an mmio read to a configured address. It's expected that mmio address belongs to a XiveRouter, but the source itself doesn't know anything routers.

OutOfBandXiveSource: this one's notify behaviour is to explicitly poke a XiveRouter. A link to the router and offset (which range of router irqs are used by this source) would be object properties.

powernv would use the first for "external" irq sources and the second for internal ones. spapr would use the second for everything.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#27 lore raw

Re: [Qemu-devel] [PATCH v3 03/35] ppc/xive: introduce the XiveFabric interface

Quoted 127 lines · David Gibson
Quoted 126 lines · Cédric Le Goater
Quoted 124 lines · David Gibson
Quoted 113 lines · Cédric Le Goater
Quoted 90 lines · David Gibson
Quoted 89 lines · Cédric Le Goater
Quoted 68 lines · David Gibson
Quoted 61 lines · Cédric Le Goater
Quoted 50 lines · David Gibson
Quoted 49 lines · Cédric Le Goater

The XiveFabric offers a simple interface, between the XiveSourve object and the device model owning the interrupt sources, to forward an event notification to the XIVE interrupt controller of the machine and if the owner is the controller, to call directly the routing sub-engine.

---
 hw/intc/xive.c        | 37 ++++++++++++++++++++++++++++++++++++-
 include/hw/ppc/xive.h | 25 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
hw/intc/xive.c +17 -1
index 060976077dd7..b4c3d06c1219 100644
@@ -17,6 +17,21 @@
 #include "hw/ppc/xive.h"
 
 /*
+ * XIVE Fabric
+ */
+
+static void xive_fabric_route(XiveFabric *xf, int lisn)
+{
+
+}
+
+static const TypeInfo xive_fabric_info = {
+    .name = TYPE_XIVE_FABRIC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(XiveFabricClass),
+};
+
+/*
  * XIVE Interrupt Source
  */
 
@@ -97,11 +112,19 @@ static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno)
 
 /*
  * Forward the source event notification to the associated XiveFabric,
- * the device owning the sources.
+ * the device owning the sources, or perform the routing if the device
+ * is the interrupt controller.
  */
 static void xive_source_notify(XiveSource *xsrc, int srcno)
 {
 
Quoted 10 lines

hw/intc/xive.c static bool xive_source_pq_trigger(XiveSource *xsrc, uint32_t srcno) · line 127

Quoted 7 lines
hw/intc/xive.c +7 -0
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xsrc->xive);
+
+    if (xfc->notify) {
+        xfc->notify(xsrc->xive, srcno + xsrc->offset);
+    } else {
+        xive_fabric_route(xsrc->xive, srcno + xsrc->offset);
+    }

Why 2 cases? Can't the XiveFabric object just make its notify equal to xive_fabric_route if that's what it wants?

Quoted 3 lines

Under sPAPR, all the sources, IPIs and virtual device interrupts, generate events which are directly routed by xive_fabric_route(). There is no need of an extra hop. Indeed.

Ok.

Quoted 7 lines
Quoted 4 lines

Under PowerNV, some sources forward the notification to the routing engine using a specific MMIO load on a notify address which is stored in one of the controller registers. So we need a hop to reach the device model, owning the sources, and do that load :

Hm. So you're saying that in pnv some sources send their notification to some other unit,

Not to any unit/device, to the device owning the sources.

For the XiveSource object under PSI, the XIVEFabric interface is the PSI device object it self, which knows how to forward the notification on the XIVE Power "bus". To be more precise, the PSI HB device has 14 interrupt sources, which notifications are forwarded using a MMIO load to some address. The load address is configured (by skiboot) in one of the PSI device registers, and points to a MMIO region of the main XIVE interrupt controller.

The PHB4 sources should be the same.

Quoted 8 lines
Quoted 5 lines

For the XiveSource object (all interrupts) under sPAPRXive, the XIVEFabric is the main interrupt controller sPAPRXive.

For the XiveSource object (IPIs) under PnvXive, the XIVEFabric is also the main interrupt controller PnvXive.

Hrm. Apparently I'm missing something, I'm really not getting what you're trying to explain here.

I see that. Let's try again.

Quoted 11 lines
Quoted 3 lines
Quoted 1 line

that would then (after possible masking) forward on to the overall> xive fabric ?

yes. May be XIVEFabric is a confusing name. What about XIVEForwarder ?

Maybe..?

Quoted 4 lines
Quoted 1 line

That seems like a property of the source object,

The source object is generic. It's a bunch of PQ bits that can be controlled by MMIOs. Nothing more.

Quoted 7 lines
Quoted 2 lines

Hmm. Isn't the source object also responsible for forwarding the interrupt to something up the chain (whatever that is)?

Yes but it can not forward directly. The XiveSource is generic and can only call a handler :

	xfc->notify(xsrc->xive, srcno + xsrc->offset);

But.. your patch doesn't do that always, it's conditional which I still don't understand.

Quoted 7 lines
Quoted 1 line

Because at the end of the notify/forward chain, you route.

Hrm. I'm really not understanding this notify/forward thing as distinct from routing. I mean, from the description here it sounds kind of like cascaded interrupt controllers, which qemu already has mechanisms to handle, but I didn't think (most) POWER9 devices worked like that.

they don't.

The concept I have in mind for the XIVEFabric QOM interface is something close to the XIVE logic unit in HW which links the device interrupt sources and the XIVE interrupt controller of the chip to the PowerBUS. The interrupt controller also has a special CQ (common queue) unit acting as a proxy for the PowerBus for the virtualization/router and the presenter units.

It's a convenient model for QEMU because the XiveSource model only has to call an interface handler when a notification event is let through. It's like doing a MMIO to send on the PowerBUS the ISN of the source that just triggered.

Quoted 16 lines
Quoted 16 lines
Quoted 4 lines
Quoted 2 lines

The device model owner, the parent of the XiveSource object, would do the real forward.

Why?

because, in my idea of the XiveSource concept, it does not have the logic to do so: the register for the MMIO address to use, another one for the IVT offset, etc

Quoted 4 lines

I mean the XiveSource basically represents the xive irq related logic of the PHB or whatever, why would it not represent *all* of that, rather than just the ESB bits, meaning the owner has to have some more xive logic for the forwarding.

ok. This is where we diverge in the concept.

Quoted 7 lines
Quoted 3 lines

The PQ bits, the ESB MMIO region handlers can be easily shared between the different device models. They are the common part of devices with XIVE interrupt sources.

Sure, but QOM subclasses are a thing, which sounds like a better way of doing this than having to have extra XIVE logic in all the parent devices.

I agree It can be done with subclasses also but QOM interfaces are quickly defined and stateless.

On sPAPR, the extra XIVE logic would only be the sPAPRXive model acting as the machine interrupt controller model. It owns all the guest interrupt sources.

In PowerNV, we would have the PnvXive interrupt controller model for the chip, which also owns the IPI sources, and the PSIHB model and the PHB4 model.

This is very much like XICS. Each relevant device has one or more ICSstate objects. ICSstate has a pointer to the XICSfabric.

Quoted 11 lines
Quoted 7 lines
Quoted 3 lines

Note that I don't think the fact that some sources notify via mmio and some are internal really matters. It's not like we're modelling the power bus down to the wire-transaction level.

yes but the configuration of the devices are different. pnv devices will have registers accessible through MMIO or XSCOM and configured by the firmware. spapr is all set up by QEMU.

Ah.. ok. Actually modelling the mmio forwards probably makes sense then. I still think it makes more sense in a pnv XiveSource subclass rather than putting it in the containing device.

Hmm, we will then need to define classes for the sPAPRXiveSource, PnvXiveSource, PSIXiveSource, which works fine for sure. But with a QOM interface, we could use directly the XiveSource class.

I will take a look.

Quoted 46 lines
Quoted 26 lines
Quoted 6 lines
Quoted 6 lines

It's very similar to what we have today with XICS :

- The sPAPR model has an ICSState
- The PnvPSI model has an ICSState
- The PnvPHB3 model has two ICSStates

Quoted 8 lines
Quoted 3 lines

and the 'xics' pointer in ICSState points to the 'interrupt unit' of the machine to do resends and to grab ICPs. So it used for routing essentially.

Hmm. I think you and I are looking at XICSFabric kind of differently. As I see it, it's not really an active component at all. Rather it's basically a global "map" of the xics components so that they can find each other.

ok. I am not that far either.

Quoted 9 lines
Quoted 8 lines

in Xive

- sPAPRXive model has a XiveSource
- PnvXive model has a XiveSource
- PnvPSI model has a XiveSource
- PnvPHB4 model should have also.

and the 'xive' pointer in XiveSource points to the parent object,

Quoted 8 lines
Quoted 3 lines

Uh.. yeah.. the xics pointer in ICS units doesn't point to the parent object, except maybe by accident. It's absolutely intended to be global, and so points to the machine.

yes. I agree.

XIVE has more layers and visible components due to the internal tables used for routing.

Right.

Quoted 9 lines
Quoted 8 lines
Quoted 1 line

which will handle the event notification forwarding or routing.

Ok, how about this for a partial model. We have:

XiveSource objects:
* Owns an ESB table
* Knows the mapping of its local irq offsets to global irq numbers

Quoted 5 lines
Quoted 2 lines

That is the 'offset' attribute I suppose. This is set at runtime for the powernv devices.

Ok, so that offset is effectively a register (which will have to be migrated), rather than a device property.

powernv migration is a not on my radar yet :)

Quoted 5 lines
Quoted 2 lines

For pseries, we should need it for
passthrough. I think. I haven't looked at that part yet.

Uh.. I really hope not. AIUI the offsets are decided by the platform rather than the guest in this case, yes?

A mapping is done in the guest. I still need to check how the ESB pages are populated in the guest ESB MMIO region. We should be fine I think.

Quoted 10 lines

In which case if we can't
count on a fixed offset even in passthrough mode, then migration is basically impossible.

Quoted 4 lines
Quoted 2 lines
Quoted 1 line

* Provides the mmio interface for ESB manipulation

* When neccessary, notifies a new interrupt to a XiveRouter

ok. I think that what we have today fits the idea then.

Yes, I think so to - just clarifying in the context of the rest of this proposal.

yes. I think we should be adding a XiveRouter class like you propose below.

Quoted 18 lines
Quoted 5 lines
Quoted 3 lines

XiveRouter objects:
* Responsible for a fixed range of global irq numbers
* Owns an IVT (but what that means can vary, see below)

the size of the IVT table is determined at runtime for powernv.

That's fine - both the size and base of the IVT will be registers of the powernv variant of the device.

Quoted 4 lines
Quoted 2 lines

* When notified of an irq, routes it to the appropriate EQ (haven't thought about this part yet)

we will need a class handler to get EQs

Sure.

Quoted 2 lines
Quoted 1 line

* Abstract class - needs subclasses to define how to get IVEs

Quoted 8 lines
Quoted 5 lines

OK. We will need a few other ops. The router needs to :

- get IVEs
- get EQ descriptors
- update OS EQs (write event data in OS RAM)

IIUC the actual EQ (as opposed to its descriptor) will be in guest RAM for both powernv and spapr,

yes.

Quoted 2 lines

so this can be common (utiliizing a
subclass hook to locate the EQ base address).

yes.

Quoted 5 lines
Quoted 2 lines

- update EQ descriptors (to set EQ descriptor index & toggle)
- get an NVT/VP (to notify CPUs)

Getting NVT/VP sounds more like it would belong in the XiveFabric than the router, but I haven't looked at this in detail yet.

The VP table is owned by the presenter unit. It is notified by the router unit when an event is let through. The presenter then looks for the VP to notify among the VPs dispatched on the HW threads. If none is found, the event is escalated.

The router units owns the IVE and the EQ tables, but still can do some remote access to the VPDT, to update the backlog and IBP I think. I don't think we need to introduce a presenter model, accessors for the VPs should enough.

Quoted 10 lines
Quoted 10 lines

For powernv, we should also consider updating the NVT/VP. It can be done later.

Quoted 2 lines

XiveFabric interface:
* Lets XIVE components locate each other

hmm, it should be a chain :

	source -> router -> presenter -> cpu
Quoted 8 lines
Quoted 3 lines

So the components should not have to locate each other. The presenter does not know about the source for instance. Only the sources need to forward events to the main controller logic doing the routing.

source -> router

From the above sounds like we can maybe make this just a router property in the source, rather than a lookup through a fabric.

If we use a direct link from the source to the router, how do we handle the powernv case which does event notification with a MMIO load ? I think a call through an interface is better (the XiveFabric)

Quoted 5 lines

router -> presenter

Here we might still need a "fabric". By defintion the router can direct to a bunch of different presenters, so it needs some kind of map to find them, no?

yes. The vp_block identifies the chip. There are different possible scenarios for the block configuration on a system but OPAL uses a simple one : one chip <-> one block. We can use that to start with on PowerNV.

And on pseries, just use block 0 for all, we don't really care. The concept of block does not reach the spapr specs.

As for the presenter it self, it loops on the CPUs to check the CAM line register of the thread interrupt management context to see if one matches vp_block+vp_index. This is one routine. I don't think we need a model for that.

Quoted 3 lines

presenter -> cpu

This one's trivial; just the cpu->intc pointer or similar.

yes. We store under the cpu the thread interrupt management context, which is a set of registers that the OS accesses through the TIMA, one per machine.

Quoted 8 lines
Quoted 7 lines
Quoted 2 lines

* get_router() method: maps a global irq number to XiveRouter object

Ah. you are thinking about the multichip case under powernv. I need to look at that more closely. But XIVE has a concept of block which is used by skiboot to map a chip to a block and the XIVE tables have a block field.

Quoted 4 lines

Hmm.. I think we need to understand this to make a real model. I'd thought the block number would just be the chip number somewhere in the high bits of the global irq, but sounds like there might be yet another indirection here.

We have a one-to-one mapping today. I doubt it will change. We can start with that.

Quoted 17 lines
Quoted 4 lines
Quoted 1 line

* Always global (implemented on the machine)

OK. this is more or less the object modeling the main interrupt controller ? What I called sPAPRXive in the current patchset.

No. This is *always* global - assuming we need it at all - even on multichip powernv.

Quoted 9 lines
Quoted 4 lines

On pseries we have:

? XiveSource objects. We probably only need one, but 1 for LSI and one for MSI might be convenient.

It's not too ugly for the moment. If we create a source object for LSIs only that might be more complex for passthrough devices and their associated ESB MMIO region.

Quoted 8 lines
Quoted 6 lines

side note :

LSIs work under TCG, and used to work under KVM until we removed the StoreEOI support. Since the EOI is now different, we need to find a way to handle EOI for guest virtual LSIs which are not LSIs for the host.

I can't quite picture that case - can you give a concrete example?

A guest won't be able to use a rtl8139 nic under KVM.

As it is the guest that does the EOI, it should use the appropriate sequence depending on the interrupt type, LSI vs. MSI. The store EOI was used for both types until we removed the support bc of ordering issues. So now, the guest uses a specific EOI sequence for LSI but the hypervisor implements virtual LSIs on top of IPIs interrupts which are MSIs ... we are doomed. We need to fix the guest or to find a way to reroute such interrupts to QEMU.

Quoted 15 lines
Quoted 15 lines

We can still change the Linux spapr
backend or have QEMU handle the EOI for virtual LSIs. This is on my TODO list.

Quoted 1 line

More wouldn't break the model

It should not. Initially the pachset had two source objects :
one for IPIs and one for the virtual devices interrupts.

Quoted 2 lines

1 sPAPRXiveRouter. This is a subclass of XiveRouter that holds the IVT internall (and migrates it).

OK.

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

the XiveFabric implementation always returns the single global router for get_router()

we can do that.

Again, if it's true that each source object always forwards to just one router, then we don't need a get_router(), we can just use a link property.

that is the goal of the architecture, yes. powernv sources use MMIOs to forward events to the router and source event notifications are usually forwarded to the local chip.

There are some case in which the IVE lookups are be remote. We can use the block to scan the system chips for that. we don't have to implement all the protocol.

Quoted 5 lines
Quoted 3 lines

Do we gather all XIVE components objects under an object 'sPAPRXive' modeling that way the main interrupt controller of the machine ?

No, I don't think so.

Hmm,

sPAPRXive would inherit from the XiveRouter and define the IVT and the EQDT. It is also practical to set up the TIMA memory region which is unique per machine. KVM support will need custom version of this model.

Quoted 6 lines
Quoted 3 lines

It should not have any state but it will hold the TIMA region most certainly, and the addresses where to map the ESB and TIMA regions.

Uh.. I'd expect the TIMAs to be held by the NVT objects, which we haven't covered yet.

There is a set of registers per CPU (thread interrupt management context), but there is one TIMA (thread interrupt management areao) memory region for the whole machine.

We should change the NVT name, it is confusing. NVT is an old name for the backing store struct in RAM, which was replaced by VP.

XiveTCTX (Xive Thread Context) is better IMO and its reflects the names of the IC registers.

Quoted 31 lines
Quoted 15 lines

KVM support will bring extra needs.

Quoted 3 lines

On powernv we have:
N XiveSource objects. Some in PHBs, some extra ones on each chip

yes and PSI to start with.

Quoted 7 lines

(#chips) PowerXiveRouter objects. This subclass of XiveRouter stores the register giving the IVT base address and migrates that, but the IVT contents are in RAM

the XiveFabric get_router() implementation returns the right chip's router based on the irq number

Quoted 7 lines
Quoted 1 line

Obviously the router->EQ sides still needs a bunch of thought.

These are all routing tables :

- IVT
- EQDT
- VPDT

Thinking about what you've said, I'm thinking maybe what we need on the source side is two versions which don't exactly correspond to spapr vs powernv:

InBandXiveSource: this one's notify behaviour is to issue an mmio read to a configured address. It's expected that mmio address belongs to a XiveRouter, but the source itself doesn't know anything routers.

Quoted 7 lines

OutOfBandXiveSource: this one's notify behaviour is to explicitly poke a XiveRouter. A link to the router and offset (which range of router irqs are used by this source) would be object properties.

powernv would use the first for "external" irq sources and the second for internal ones. spapr would use the second for everything.

OK.

I think I will stick to my idea for the next version because there are quite a lot of changes in the models already : a new spapr irq backend, a XiveRouter, a EQD table for sPAPR, a complete TIMA support for all privileges, a renamed XiveTCTX, a simple Presenter scanning CAM lines, support for EQ ESBs (that no system uses), a pseries-2.13-xive machine, etc.

I will share the powernv and PSIHB models to validate the common XIVE model. We can change to the above if it is really not satisfying.

Thanks,

C.

#28 lore raw

PATCH v3 4/35 spapr/xive: introduce a XIVE interrupt controller for sPAPR

In reply to Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Changes since v2 :

- introduced the XiveFabric interface

 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/spapr_xive.c              | 159 ++++++++++++++++++++++++++++++++++++++
 hw/intc/xive.c                    |   7 ++
 include/hw/ppc/spapr_xive.h       |  31 ++++++++
 include/hw/ppc/xive.h             |   5 ++
 include/hw/ppc/xive_regs.h        |  33 ++++++++
 7 files changed, 237 insertions(+)
 create mode 100644 hw/intc/spapr_xive.c
 create mode 100644 include/hw/ppc/spapr_xive.h
 create mode 100644 include/hw/ppc/xive_regs.h
default-configs/ppc64-softmmu.mak +1 -0
index c6d13e757977..f8d34722931d 100644
@@ -17,4 +17,5 @@ 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_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 72a46ed91c31..301a8e972d91 100644
@@ -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
hw/intc/spapr_xive.c new file +159 -0

159 lines not on this page · raw message

hw/intc/xive.c +7 -0
index b4c3d06c1219..dccad0318834 100644
@@ -20,6 +20,13 @@
  * XIVE Fabric
  */
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_ive(xf, lisn);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
include/hw/ppc/spapr_xive.h new file +31 -0
new file mode 100644
index 000000000000..1d966b5d3a96
@@ -0,0 +1,31 @@
+/*
+ * 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/sysbus.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 {
+    SysBusDevice parent;
+
+    /* Routing table */
+    XiveIVE      *ivt;
+    uint32_t     nr_irqs;
+} sPAPRXive;
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn);
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon);
+
+#endif /* PPC_SPAPR_XIVE_H */
include/hw/ppc/xive.h +5 -0
index 4fcae2c763e6..5b145816acdc 100644
@@ -11,6 +11,7 @@
 #define PPC_XIVE_H
 
 #include "hw/sysbus.h"
+#include "hw/ppc/xive_regs.h"
 
 typedef struct XiveFabric XiveFabric;
 
@@ -166,6 +167,10 @@ typedef struct XiveFabric {
 typedef struct XiveFabricClass {
     InterfaceClass parent;
     void (*notify)(XiveFabric *xf, uint32_t lisn);
+
+    XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
 } XiveFabricClass;
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h new file +33 -0
new file mode 100644
index 000000000000..5903f29eb789
@@ -0,0 +1,33 @@
+/*
+ * QEMU PowerPC XIVE interrupt controller model
+ *
+ * Copyright (c) 2016-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_XIVE_REGS_H
+#define _PPC_XIVE_REGS_H
+
+/* IVE/EAS
+ *
+ * One per interrupt source. Targets that interrupt to a given EQ
+ * and provides the corresponding logical interrupt number (EQ data)
+ *
+ * We also map this structure to the escalation descriptor inside
+ * an EQ, though in that case the valid and masked bits are not used.
+ */
+typedef struct XiveIVE {
+        /* Use a single 64-bit definition to make it easier to
+         * perform atomic updates
+         */
+        uint64_t        w;
+#define IVE_VALID       PPC_BIT(0)
+#define IVE_EQ_BLOCK    PPC_BITMASK(4, 7)        /* Destination EQ block# */
+#define IVE_EQ_INDEX    PPC_BITMASK(8, 31)       /* Destination EQ index */
+#define IVE_MASKED      PPC_BIT(32)              /* Masked */
+#define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
+} XiveIVE;
+
+#endif /* _INTC_XIVE_INTERNAL_H */
Signature · 1 line
2.13.6
#29 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Quoted 87 lines

Changes since v2 :

- introduced the XiveFabric interface

 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/spapr_xive.c              | 159 ++++++++++++++++++++++++++++++++++++++
 hw/intc/xive.c                    |   7 ++
 include/hw/ppc/spapr_xive.h       |  31 ++++++++
 include/hw/ppc/xive.h             |   5 ++
 include/hw/ppc/xive_regs.h        |  33 ++++++++
 7 files changed, 237 insertions(+)
 create mode 100644 hw/intc/spapr_xive.c
 create mode 100644 include/hw/ppc/spapr_xive.h
 create mode 100644 include/hw/ppc/xive_regs.h
default-configs/ppc64-softmmu.mak +1 -0
index c6d13e757977..f8d34722931d 100644
@@ -17,4 +17,5 @@ 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_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 72a46ed91c31..301a8e972d91 100644
@@ -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
hw/intc/spapr_xive.c new file +42 -0
new file mode 100644
index 000000000000..020444e2665a
@@ -0,0 +1,159 @@
+/*
+ * 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive_regs.h"
+
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "IVE Table\n");
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+
+        if (!(ive->w & IVE_VALID)) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %s %08x %08x\n", i,
+                       ive->w & IVE_MASKED ? "M" : " ",
+                       (int) GETFIELD(IVE_EQ_INDEX, ive->w),
+                       (int) GETFIELD(IVE_EQ_DATA, ive->w));
+    }
+}
+
+static void spapr_xive_reset(DeviceState *dev)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+    int i;
+

hw/intc/spapr_xive.c line 52

Quoted 10 lines
hw/intc/spapr_xive.c new file +10 -0
+    /* Mask all valid IVEs in the IRQ number space. */
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+        if (ive->w & IVE_VALID) {
+            ive->w |= IVE_MASKED;
+        }
+    }
+}
+
+static void spapr_xive_init(Object *obj)

I'm trying to standardize on init_instance methods being called *_instance_init(). It helps to make it obvious that this is ineed an instance_init() method, rather than one of the various other init calls that exist in various places.

Quoted 224 lines
hw/intc/spapr_xive.c new file +107 -0
+{
+
+}
+
+static void spapr_xive_realize(DeviceState *dev, Error **errp)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+
+    if (!xive->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater 0");
+        return;
+    }
+
+    /* Allocate the Interrupt Virtualization Table */
+    xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+}
+
+static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    sPAPRXive *xive = SPAPR_XIVE(xf);
+
+    return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
+}
+
+static const VMStateDescription vmstate_spapr_xive_ive = {
+    .name = TYPE_SPAPR_XIVE "/ive",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT64(w, XiveIVE),
+        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(ivt, sPAPRXive, nr_irqs,
+                                     vmstate_spapr_xive_ive, XiveIVE),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property spapr_xive_properties[] = {
+    DEFINE_PROP_UINT32("nr-irqs", sPAPRXive, nr_irqs, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_xive_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    XiveFabricClass *xfc = XIVE_FABRIC_CLASS(klass);
+
+    dc->realize = spapr_xive_realize;
+    dc->reset = spapr_xive_reset;
+    dc->props = spapr_xive_properties;
+    dc->desc = "sPAPR XIVE interrupt controller";
+    dc->vmsd = &vmstate_spapr_xive;
+
+    xfc->get_ive = spapr_xive_get_ive;
+}
+
+static const TypeInfo spapr_xive_info = {
+    .name = TYPE_SPAPR_XIVE,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_init = spapr_xive_init,
+    .instance_size = sizeof(sPAPRXive),
+    .class_init = spapr_xive_class_init,
+    .interfaces = (InterfaceInfo[]) {
+            { TYPE_XIVE_FABRIC },
+            { },
+    },
+};
+
+static void spapr_xive_register_types(void)
+{
+    type_register_static(&spapr_xive_info);
+}
+
+type_init(spapr_xive_register_types)
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w |= IVE_VALID;
+    return true;
+}
+
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w &= ~IVE_VALID;
+    return true;
+}
hw/intc/xive.c +7 -0
index b4c3d06c1219..dccad0318834 100644
@@ -20,6 +20,13 @@
  * XIVE Fabric
  */
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_ive(xf, lisn);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
include/hw/ppc/spapr_xive.h new file +31 -0
new file mode 100644
index 000000000000..1d966b5d3a96
@@ -0,0 +1,31 @@
+/*
+ * 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/sysbus.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 {
+    SysBusDevice parent;
+
+    /* Routing table */
+    XiveIVE      *ivt;
+    uint32_t     nr_irqs;
+} sPAPRXive;
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn);
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon);
+
+#endif /* PPC_SPAPR_XIVE_H */
include/hw/ppc/xive.h +5 -0
index 4fcae2c763e6..5b145816acdc 100644
@@ -11,6 +11,7 @@
 #define PPC_XIVE_H
 
 #include "hw/sysbus.h"
+#include "hw/ppc/xive_regs.h"
 
 typedef struct XiveFabric XiveFabric;
 
@@ -166,6 +167,10 @@ typedef struct XiveFabric {
 typedef struct XiveFabricClass {
     InterfaceClass parent;
     void (*notify)(XiveFabric *xf, uint32_t lisn);
+
+    XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
 } XiveFabricClass;
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h new file +33 -0
new file mode 100644
index 000000000000..5903f29eb789
@@ -0,0 +1,33 @@
+/*
+ * QEMU PowerPC XIVE interrupt controller model
+ *
+ * Copyright (c) 2016-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_XIVE_REGS_H
+#define _PPC_XIVE_REGS_H
+
+/* IVE/EAS
+ *
+ * One per interrupt source. Targets that interrupt to a given EQ
+ * and provides the corresponding logical interrupt number (EQ data)
+ *
+ * We also map this structure to the escalation descriptor inside
+ * an EQ, though in that case the valid and masked bits are not used.
+ */
+typedef struct XiveIVE {
+        /* Use a single 64-bit definition to make it easier to
+         * perform atomic updates
+         */
+        uint64_t        w;
+#define IVE_VALID       PPC_BIT(0)
+#define IVE_EQ_BLOCK    PPC_BITMASK(4, 7)        /* Destination EQ block# */
+#define IVE_EQ_INDEX    PPC_BITMASK(8, 31)       /* Destination EQ index */
+#define IVE_MASKED      PPC_BIT(32)              /* Masked */
+#define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
+} XiveIVE;
+
+#endif /* _INTC_XIVE_INTERNAL_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#30 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 9 lines · David Gibson
Quoted 8 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

Quoted 8 lines
Quoted 5 lines

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

There is only one, the main IC.

Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

Quoted 94 lines
Quoted 94 lines

Changes since v2 :

- introduced the XiveFabric interface

 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/spapr_xive.c              | 159 ++++++++++++++++++++++++++++++++++++++
 hw/intc/xive.c                    |   7 ++
 include/hw/ppc/spapr_xive.h       |  31 ++++++++
 include/hw/ppc/xive.h             |   5 ++
 include/hw/ppc/xive_regs.h        |  33 ++++++++
 7 files changed, 237 insertions(+)
 create mode 100644 hw/intc/spapr_xive.c
 create mode 100644 include/hw/ppc/spapr_xive.h
 create mode 100644 include/hw/ppc/xive_regs.h
default-configs/ppc64-softmmu.mak +1 -0
index c6d13e757977..f8d34722931d 100644
@@ -17,4 +17,5 @@ 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_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 72a46ed91c31..301a8e972d91 100644
@@ -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
hw/intc/spapr_xive.c new file +49 -0
new file mode 100644
index 000000000000..020444e2665a
@@ -0,0 +1,159 @@
+/*
+ * 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive_regs.h"
+
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "IVE Table\n");
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+
+        if (!(ive->w & IVE_VALID)) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %s %08x %08x\n", i,
+                       ive->w & IVE_MASKED ? "M" : " ",
+                       (int) GETFIELD(IVE_EQ_INDEX, ive->w),
+                       (int) GETFIELD(IVE_EQ_DATA, ive->w));
+    }
+}
+
+static void spapr_xive_reset(DeviceState *dev)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+    int i;
+
+    /* Mask all valid IVEs in the IRQ number space. */
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+        if (ive->w & IVE_VALID) {
+            ive->w |= IVE_MASKED;
+        }
+    }
Quoted 8 lines

hw/intc/spapr_xive.c line 52

Quoted 3 lines
hw/intc/spapr_xive.c new file +3 -0
+}
+
+static void spapr_xive_init(Object *obj)

I'm trying to standardize on init_instance methods being called *_instance_init(). It helps to make it obvious that this is ineed an instance_init() method, rather than one of the various other init calls that exist in various places.

ok. this is good practice. I will fix.

Thanks,

C.

Quoted 226 lines
Quoted 225 lines
hw/intc/spapr_xive.c new file +107 -0
+{
+
+}
+
+static void spapr_xive_realize(DeviceState *dev, Error **errp)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+
+    if (!xive->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater 0");
+        return;
+    }
+
+    /* Allocate the Interrupt Virtualization Table */
+    xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+}
+
+static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    sPAPRXive *xive = SPAPR_XIVE(xf);
+
+    return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
+}
+
+static const VMStateDescription vmstate_spapr_xive_ive = {
+    .name = TYPE_SPAPR_XIVE "/ive",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT64(w, XiveIVE),
+        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(ivt, sPAPRXive, nr_irqs,
+                                     vmstate_spapr_xive_ive, XiveIVE),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property spapr_xive_properties[] = {
+    DEFINE_PROP_UINT32("nr-irqs", sPAPRXive, nr_irqs, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_xive_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    XiveFabricClass *xfc = XIVE_FABRIC_CLASS(klass);
+
+    dc->realize = spapr_xive_realize;
+    dc->reset = spapr_xive_reset;
+    dc->props = spapr_xive_properties;
+    dc->desc = "sPAPR XIVE interrupt controller";
+    dc->vmsd = &vmstate_spapr_xive;
+
+    xfc->get_ive = spapr_xive_get_ive;
+}
+
+static const TypeInfo spapr_xive_info = {
+    .name = TYPE_SPAPR_XIVE,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_init = spapr_xive_init,
+    .instance_size = sizeof(sPAPRXive),
+    .class_init = spapr_xive_class_init,
+    .interfaces = (InterfaceInfo[]) {
+            { TYPE_XIVE_FABRIC },
+            { },
+    },
+};
+
+static void spapr_xive_register_types(void)
+{
+    type_register_static(&spapr_xive_info);
+}
+
+type_init(spapr_xive_register_types)
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w |= IVE_VALID;
+    return true;
+}
+
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w &= ~IVE_VALID;
+    return true;
+}
hw/intc/xive.c +7 -0
index b4c3d06c1219..dccad0318834 100644
@@ -20,6 +20,13 @@
  * XIVE Fabric
  */
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_ive(xf, lisn);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
include/hw/ppc/spapr_xive.h new file +31 -0
new file mode 100644
index 000000000000..1d966b5d3a96
@@ -0,0 +1,31 @@
+/*
+ * 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/sysbus.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 {
+    SysBusDevice parent;
+
+    /* Routing table */
+    XiveIVE      *ivt;
+    uint32_t     nr_irqs;
+} sPAPRXive;
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn);
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon);
+
+#endif /* PPC_SPAPR_XIVE_H */
include/hw/ppc/xive.h +5 -0
index 4fcae2c763e6..5b145816acdc 100644
@@ -11,6 +11,7 @@
 #define PPC_XIVE_H
 
 #include "hw/sysbus.h"
+#include "hw/ppc/xive_regs.h"
 
 typedef struct XiveFabric XiveFabric;
 
@@ -166,6 +167,10 @@ typedef struct XiveFabric {
 typedef struct XiveFabricClass {
     InterfaceClass parent;
     void (*notify)(XiveFabric *xf, uint32_t lisn);
+
+    XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
 } XiveFabricClass;
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h new file +33 -0
new file mode 100644
index 000000000000..5903f29eb789
@@ -0,0 +1,33 @@
+/*
+ * QEMU PowerPC XIVE interrupt controller model
+ *
+ * Copyright (c) 2016-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_XIVE_REGS_H
+#define _PPC_XIVE_REGS_H
+
+/* IVE/EAS
+ *
+ * One per interrupt source. Targets that interrupt to a given EQ
+ * and provides the corresponding logical interrupt number (EQ data)
+ *
+ * We also map this structure to the escalation descriptor inside
+ * an EQ, though in that case the valid and masked bits are not used.
+ */
+typedef struct XiveIVE {
+        /* Use a single 64-bit definition to make it easier to
+         * perform atomic updates
+         */
+        uint64_t        w;
+#define IVE_VALID       PPC_BIT(0)
+#define IVE_EQ_BLOCK    PPC_BITMASK(4, 7)        /* Destination EQ block# */
+#define IVE_EQ_INDEX    PPC_BITMASK(8, 31)       /* Destination EQ index */
+#define IVE_MASKED      PPC_BIT(32)              /* Masked */
+#define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
+} XiveIVE;
+
+#endif /* _INTC_XIVE_INTERNAL_H */
#31 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 13 lines · Cédric Le Goater
Quoted 12 lines · David Gibson
Quoted 11 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT. It may or may not do other stuff as well.

Now IIUC, on pnv the IVT lives in main system memory. Under PAPR is the IVT in guest memory, or is it outside (updated by hypercalls/rtas)?

Quoted 2 lines

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

The further we go here, the less sure I am that XiveFabric even makes sense as a concept.

Quoted 337 lines
Quoted 95 lines
Quoted 95 lines

Changes since v2 :

- introduced the XiveFabric interface

 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/spapr_xive.c              | 159 ++++++++++++++++++++++++++++++++++++++
 hw/intc/xive.c                    |   7 ++
 include/hw/ppc/spapr_xive.h       |  31 ++++++++
 include/hw/ppc/xive.h             |   5 ++
 include/hw/ppc/xive_regs.h        |  33 ++++++++
 7 files changed, 237 insertions(+)
 create mode 100644 hw/intc/spapr_xive.c
 create mode 100644 include/hw/ppc/spapr_xive.h
 create mode 100644 include/hw/ppc/xive_regs.h
default-configs/ppc64-softmmu.mak +1 -0
index c6d13e757977..f8d34722931d 100644
@@ -17,4 +17,5 @@ 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_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 72a46ed91c31..301a8e972d91 100644
@@ -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
hw/intc/spapr_xive.c new file +49 -0
new file mode 100644
index 000000000000..020444e2665a
@@ -0,0 +1,159 @@
+/*
+ * 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive_regs.h"
+
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "IVE Table\n");
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+
+        if (!(ive->w & IVE_VALID)) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %s %08x %08x\n", i,
+                       ive->w & IVE_MASKED ? "M" : " ",
+                       (int) GETFIELD(IVE_EQ_INDEX, ive->w),
+                       (int) GETFIELD(IVE_EQ_DATA, ive->w));
+    }
+}
+
+static void spapr_xive_reset(DeviceState *dev)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+    int i;
+
+    /* Mask all valid IVEs in the IRQ number space. */
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+        if (ive->w & IVE_VALID) {
+            ive->w |= IVE_MASKED;
+        }
+    }
Quoted 8 lines

hw/intc/spapr_xive.c line 52

Quoted 3 lines
hw/intc/spapr_xive.c new file +3 -0
+}
+
+static void spapr_xive_init(Object *obj)

I'm trying to standardize on init_instance methods being called *_instance_init(). It helps to make it obvious that this is ineed an instance_init() method, rather than one of the various other init calls that exist in various places.

ok. this is good practice. I will fix.

Thanks,

C.

Quoted 226 lines
Quoted 225 lines
hw/intc/spapr_xive.c new file +107 -0
+{
+
+}
+
+static void spapr_xive_realize(DeviceState *dev, Error **errp)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+
+    if (!xive->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater 0");
+        return;
+    }
+
+    /* Allocate the Interrupt Virtualization Table */
+    xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+}
+
+static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    sPAPRXive *xive = SPAPR_XIVE(xf);
+
+    return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
+}
+
+static const VMStateDescription vmstate_spapr_xive_ive = {
+    .name = TYPE_SPAPR_XIVE "/ive",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT64(w, XiveIVE),
+        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(ivt, sPAPRXive, nr_irqs,
+                                     vmstate_spapr_xive_ive, XiveIVE),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property spapr_xive_properties[] = {
+    DEFINE_PROP_UINT32("nr-irqs", sPAPRXive, nr_irqs, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_xive_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    XiveFabricClass *xfc = XIVE_FABRIC_CLASS(klass);
+
+    dc->realize = spapr_xive_realize;
+    dc->reset = spapr_xive_reset;
+    dc->props = spapr_xive_properties;
+    dc->desc = "sPAPR XIVE interrupt controller";
+    dc->vmsd = &vmstate_spapr_xive;
+
+    xfc->get_ive = spapr_xive_get_ive;
+}
+
+static const TypeInfo spapr_xive_info = {
+    .name = TYPE_SPAPR_XIVE,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_init = spapr_xive_init,
+    .instance_size = sizeof(sPAPRXive),
+    .class_init = spapr_xive_class_init,
+    .interfaces = (InterfaceInfo[]) {
+            { TYPE_XIVE_FABRIC },
+            { },
+    },
+};
+
+static void spapr_xive_register_types(void)
+{
+    type_register_static(&spapr_xive_info);
+}
+
+type_init(spapr_xive_register_types)
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w |= IVE_VALID;
+    return true;
+}
+
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w &= ~IVE_VALID;
+    return true;
+}
hw/intc/xive.c +7 -0
index b4c3d06c1219..dccad0318834 100644
@@ -20,6 +20,13 @@
  * XIVE Fabric
  */
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_ive(xf, lisn);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
include/hw/ppc/spapr_xive.h new file +31 -0
new file mode 100644
index 000000000000..1d966b5d3a96
@@ -0,0 +1,31 @@
+/*
+ * 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/sysbus.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 {
+    SysBusDevice parent;
+
+    /* Routing table */
+    XiveIVE      *ivt;
+    uint32_t     nr_irqs;
+} sPAPRXive;
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn);
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon);
+
+#endif /* PPC_SPAPR_XIVE_H */
include/hw/ppc/xive.h +5 -0
index 4fcae2c763e6..5b145816acdc 100644
@@ -11,6 +11,7 @@
 #define PPC_XIVE_H
 
 #include "hw/sysbus.h"
+#include "hw/ppc/xive_regs.h"
 
 typedef struct XiveFabric XiveFabric;
 
@@ -166,6 +167,10 @@ typedef struct XiveFabric {
 typedef struct XiveFabricClass {
     InterfaceClass parent;
     void (*notify)(XiveFabric *xf, uint32_t lisn);
+
+    XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
 } XiveFabricClass;
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h new file +33 -0
new file mode 100644
index 000000000000..5903f29eb789
@@ -0,0 +1,33 @@
+/*
+ * QEMU PowerPC XIVE interrupt controller model
+ *
+ * Copyright (c) 2016-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_XIVE_REGS_H
+#define _PPC_XIVE_REGS_H
+
+/* IVE/EAS
+ *
+ * One per interrupt source. Targets that interrupt to a given EQ
+ * and provides the corresponding logical interrupt number (EQ data)
+ *
+ * We also map this structure to the escalation descriptor inside
+ * an EQ, though in that case the valid and masked bits are not used.
+ */
+typedef struct XiveIVE {
+        /* Use a single 64-bit definition to make it easier to
+         * perform atomic updates
+         */
+        uint64_t        w;
+#define IVE_VALID       PPC_BIT(0)
+#define IVE_EQ_BLOCK    PPC_BITMASK(4, 7)        /* Destination EQ block# */
+#define IVE_EQ_INDEX    PPC_BITMASK(8, 31)       /* Destination EQ index */
+#define IVE_MASKED      PPC_BIT(32)              /* Masked */
+#define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
+} XiveIVE;
+
+#endif /* _INTC_XIVE_INTERNAL_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#32 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 17 lines · David Gibson
Quoted 16 lines · Cédric Le Goater
Quoted 15 lines · David Gibson
Quoted 13 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Quoted 8 lines
Quoted 5 lines

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

Quoted 1 line

Now IIUC, on pnv the IVT lives in main system memory.

yes. It is allocated by skiboot in RAM and fed to the HW using some IC configuration registers. Then, each entry is configured with OPAL calls and the HW is updated using cache scrub registers.

Quoted 2 lines

Under PAPR is the IVT in guest memory, or is it outside (updated by hypercalls/rtas)?

Under sPAPR, the IVT is updated by the H_INT_SET_SOURCE_CONFIG hcall which configures the targeting of an IRQ. It's not in the guest memory.

Behind the hood, the IVT is still configured by OPAL under KVM and by QEMU when kernel_irqchip=off

Quoted 5 lines
Quoted 2 lines

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

The further we go here, the less sure I am that XiveFabric even makes sense as a concept.

See previous email.

C.

Quoted 338 lines
Quoted 337 lines
Quoted 95 lines
Quoted 95 lines

Changes since v2 :

- introduced the XiveFabric interface

 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/spapr_xive.c              | 159 ++++++++++++++++++++++++++++++++++++++
 hw/intc/xive.c                    |   7 ++
 include/hw/ppc/spapr_xive.h       |  31 ++++++++
 include/hw/ppc/xive.h             |   5 ++
 include/hw/ppc/xive_regs.h        |  33 ++++++++
 7 files changed, 237 insertions(+)
 create mode 100644 hw/intc/spapr_xive.c
 create mode 100644 include/hw/ppc/spapr_xive.h
 create mode 100644 include/hw/ppc/xive_regs.h
default-configs/ppc64-softmmu.mak +1 -0
index c6d13e757977..f8d34722931d 100644
@@ -17,4 +17,5 @@ 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_HOTPLUG=y
hw/intc/Makefile.objs +1 -0
index 72a46ed91c31..301a8e972d91 100644
@@ -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
hw/intc/spapr_xive.c new file +49 -0
new file mode 100644
index 000000000000..020444e2665a
@@ -0,0 +1,159 @@
+/*
+ * 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 "target/ppc/cpu.h"
+#include "sysemu/cpus.h"
+#include "monitor/monitor.h"
+#include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive_regs.h"
+
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
+{
+    int i;
+
+    monitor_printf(mon, "IVE Table\n");
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+
+        if (!(ive->w & IVE_VALID)) {
+            continue;
+        }
+
+        monitor_printf(mon, "  %4x %s %08x %08x\n", i,
+                       ive->w & IVE_MASKED ? "M" : " ",
+                       (int) GETFIELD(IVE_EQ_INDEX, ive->w),
+                       (int) GETFIELD(IVE_EQ_DATA, ive->w));
+    }
+}
+
+static void spapr_xive_reset(DeviceState *dev)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+    int i;
+
+    /* Mask all valid IVEs in the IRQ number space. */
+    for (i = 0; i < xive->nr_irqs; i++) {
+        XiveIVE *ive = &xive->ivt[i];
+        if (ive->w & IVE_VALID) {
+            ive->w |= IVE_MASKED;
+        }
+    }
Quoted 8 lines

hw/intc/spapr_xive.c line 52

Quoted 3 lines
hw/intc/spapr_xive.c new file +3 -0
+}
+
+static void spapr_xive_init(Object *obj)

I'm trying to standardize on init_instance methods being called *_instance_init(). It helps to make it obvious that this is ineed an instance_init() method, rather than one of the various other init calls that exist in various places.

ok. this is good practice. I will fix.

Thanks,

C.

Quoted 226 lines
Quoted 225 lines
hw/intc/spapr_xive.c new file +107 -0
+{
+
+}
+
+static void spapr_xive_realize(DeviceState *dev, Error **errp)
+{
+    sPAPRXive *xive = SPAPR_XIVE(dev);
+
+    if (!xive->nr_irqs) {
+        error_setg(errp, "Number of interrupt needs to be greater 0");
+        return;
+    }
+
+    /* Allocate the Interrupt Virtualization Table */
+    xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+}
+
+static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    sPAPRXive *xive = SPAPR_XIVE(xf);
+
+    return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
+}
+
+static const VMStateDescription vmstate_spapr_xive_ive = {
+    .name = TYPE_SPAPR_XIVE "/ive",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT64(w, XiveIVE),
+        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(ivt, sPAPRXive, nr_irqs,
+                                     vmstate_spapr_xive_ive, XiveIVE),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static Property spapr_xive_properties[] = {
+    DEFINE_PROP_UINT32("nr-irqs", sPAPRXive, nr_irqs, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_xive_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    XiveFabricClass *xfc = XIVE_FABRIC_CLASS(klass);
+
+    dc->realize = spapr_xive_realize;
+    dc->reset = spapr_xive_reset;
+    dc->props = spapr_xive_properties;
+    dc->desc = "sPAPR XIVE interrupt controller";
+    dc->vmsd = &vmstate_spapr_xive;
+
+    xfc->get_ive = spapr_xive_get_ive;
+}
+
+static const TypeInfo spapr_xive_info = {
+    .name = TYPE_SPAPR_XIVE,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_init = spapr_xive_init,
+    .instance_size = sizeof(sPAPRXive),
+    .class_init = spapr_xive_class_init,
+    .interfaces = (InterfaceInfo[]) {
+            { TYPE_XIVE_FABRIC },
+            { },
+    },
+};
+
+static void spapr_xive_register_types(void)
+{
+    type_register_static(&spapr_xive_info);
+}
+
+type_init(spapr_xive_register_types)
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w |= IVE_VALID;
+    return true;
+}
+
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
+{
+    XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+
+    if (!ive) {
+        return false;
+    }
+
+    ive->w &= ~IVE_VALID;
+    return true;
+}
hw/intc/xive.c +7 -0
index b4c3d06c1219..dccad0318834 100644
@@ -20,6 +20,13 @@
  * XIVE Fabric
  */
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_ive(xf, lisn);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
include/hw/ppc/spapr_xive.h new file +31 -0
new file mode 100644
index 000000000000..1d966b5d3a96
@@ -0,0 +1,31 @@
+/*
+ * 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/sysbus.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 {
+    SysBusDevice parent;
+
+    /* Routing table */
+    XiveIVE      *ivt;
+    uint32_t     nr_irqs;
+} sPAPRXive;
+
+bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
+bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn);
+void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon);
+
+#endif /* PPC_SPAPR_XIVE_H */
include/hw/ppc/xive.h +5 -0
index 4fcae2c763e6..5b145816acdc 100644
@@ -11,6 +11,7 @@
 #define PPC_XIVE_H
 
 #include "hw/sysbus.h"
+#include "hw/ppc/xive_regs.h"
 
 typedef struct XiveFabric XiveFabric;
 
@@ -166,6 +167,10 @@ typedef struct XiveFabric {
 typedef struct XiveFabricClass {
     InterfaceClass parent;
     void (*notify)(XiveFabric *xf, uint32_t lisn);
+
+    XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
 } XiveFabricClass;
 
+XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h new file +33 -0
new file mode 100644
index 000000000000..5903f29eb789
@@ -0,0 +1,33 @@
+/*
+ * QEMU PowerPC XIVE interrupt controller model
+ *
+ * Copyright (c) 2016-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_XIVE_REGS_H
+#define _PPC_XIVE_REGS_H
+
+/* IVE/EAS
+ *
+ * One per interrupt source. Targets that interrupt to a given EQ
+ * and provides the corresponding logical interrupt number (EQ data)
+ *
+ * We also map this structure to the escalation descriptor inside
+ * an EQ, though in that case the valid and masked bits are not used.
+ */
+typedef struct XiveIVE {
+        /* Use a single 64-bit definition to make it easier to
+         * perform atomic updates
+         */
+        uint64_t        w;
+#define IVE_VALID       PPC_BIT(0)
+#define IVE_EQ_BLOCK    PPC_BITMASK(4, 7)        /* Destination EQ block# */
+#define IVE_EQ_INDEX    PPC_BITMASK(8, 31)       /* Destination EQ index */
+#define IVE_MASKED      PPC_BIT(32)              /* Masked */
+#define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
+} XiveIVE;
+
+#endif /* _INTC_XIVE_INTERNAL_H */
#33 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 21 lines · Cédric Le Goater
Quoted 20 lines · David Gibson
Quoted 19 lines · Cédric Le Goater
Quoted 17 lines · David Gibson
Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Hmm. There's some things we can delay dealing with, but I don't think this is one of them. I think we need to understand how multichip is going to work in order to come up with a sane architecture. Otherwise I fear we'll end up with something that we either need to horribly bastardize for multichip, or have to rework things dramatically leading to migration nightmares.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Quoted 4 lines
Quoted 2 lines

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Yes. For papr variant it would have the whole IVT contents as its state. For the powernv, just the registers telling it where to find the IVT in RAM.

Quoted 4 lines
Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

That seems like a lot of steps. Up to push the EQ DATA, certainly. And I guess it'll have to ping an NVT somehow, but I'm not sure it should know about CPUs as such.

I'm not sure at this stage what should own the EQD table. In the multichip case is there one EQD table for every IVT? I'm guessing not - I figure the EQD table must be effectively global so that any chip's router can send events to any EQ in the whole system.

Quoted 5 lines
Quoted 1 line

Now IIUC, on pnv the IVT lives in main system memory.

yes. It is allocated by skiboot in RAM and fed to the HW using some IC configuration registers. Then, each entry is configured with OPAL calls and the HW is updated using cache scrub registers.

Right. At least for the first pass we should be able to treat the cache scrub registers as no-ops and just not cache anything in the qemu implementation.

Quoted 6 lines
Quoted 2 lines

Under PAPR is the IVT in guest memory, or is it outside (updated by hypercalls/rtas)?

Under sPAPR, the IVT is updated by the H_INT_SET_SOURCE_CONFIG hcall which configures the targeting of an IRQ. It's not in the guest memory.

Right.

Quoted 2 lines

Behind the hood, the IVT is still configured by OPAL under KVM and by QEMU when kernel_irqchip=off

Sure. Even with kernel_irqchip=on there's still logically a guest IVT (or "IVT view" I guess), even if it's actual entries are stored distributed across various places in the host's IVTs.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

The further we go here, the less sure I am that XiveFabric even makes sense as a concept.

See previous email.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#34 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 30 lines · David Gibson
Quoted 21 lines · Cédric Le Goater
Quoted 20 lines · David Gibson
Quoted 19 lines · Cédric Le Goater
Quoted 17 lines · David Gibson
Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Quoted 6 lines

Hmm. There's some things we can delay dealing with, but I don't think this is one of them. I think we need to understand how multichip is going to work in order to come up with a sane architecture. Otherwise I fear we'll end up with something that we either need to horribly bastardize for multichip, or have to rework things dramatically leading to migration nightmares.

So, it is all controlled by MMIO, so we should be fine on that part. As for the internal tables, they are all configured by firmware, using a chip identifier (block). I need to check how the remote XIVE are accessed. I think this is by MMIO.

I haven't looked at multichip XIVE support but I am not too worried as the framework is already in place for the machine.

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Quoted 4 lines
Quoted 2 lines

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Yes. For papr variant it would have the whole IVT contents as its state. For the powernv, just the registers telling it where to find the IVT in RAM.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

That seems like a lot of steps. Up to push the EQ DATA, certainly. And I guess it'll have to ping an NVT somehow, but I'm not sure it should know about CPUs as such.

For PowerNV, the concept could be generalized, yes. An NVT can contain the interrupt state of a logical server but the common case is baremetal without guests for QEMU and so we have a NVT per cpu.

PowerNV will have some limitation but we can make it better than today for sure. It boots.

We can improve some of the NVT notification process, the way NVT are matched eventually. may be support remote engines if the NVT is not local. I have not looked at the details.

Quoted 1 line

I'm not sure at this stage what should own the EQD table.

The EQDT is in RAM.

Quoted 1 line

In the multichip case is there one EQD table for every IVT?

There is one EQDT per chip, same for the IVT. They are in RAM, identified with a block ID.

Quoted 5 lines

I'm guessing
not - I figure the EQD table must be effectively global so that any chip's router can send events to any EQ in the whole system.

Quoted 2 lines
Quoted 1 line
Quoted 1 line

Now IIUC, on pnv the IVT lives in main system memory.

Quoted 7 lines
Quoted 3 lines

yes. It is allocated by skiboot in RAM and fed to the HW using some IC configuration registers. Then, each entry is configured with OPAL calls and the HW is updated using cache scrub registers.

Right. At least for the first pass we should be able to treat the cache scrub registers as no-ops and just not cache anything in the qemu implementation.

The model currently supports the cache scrub registers, we need it to update some values. It's not too complex.

Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

Under PAPR is the IVT in guest memory, or is it outside (updated by hypercalls/rtas)?

Under sPAPR, the IVT is updated by the H_INT_SET_SOURCE_CONFIG hcall which configures the targeting of an IRQ. It's not in the guest memory.

Quoted 8 lines

Right.

Quoted 2 lines

Behind the hood, the IVT is still configured by OPAL under KVM and by QEMU when kernel_irqchip=off

Sure. Even with kernel_irqchip=on there's still logically a guest IVT (or "IVT view" I guess), even if it's actual entries are stored distributed across various places in the host's IVTs.

yes. The XIVE KVM device caches the info. This is used to dump the state without doing OPAL calls.

C.

Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

The further we go here, the less sure I am that XiveFabric even makes sense as a concept.

See previous email.

#35 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 38 lines · Cédric Le Goater
Quoted 30 lines · David Gibson
Quoted 21 lines · Cédric Le Goater
Quoted 20 lines · David Gibson
Quoted 19 lines · Cédric Le Goater
Quoted 17 lines · David Gibson
Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Quoted 6 lines

Hmm. There's some things we can delay dealing with, but I don't think this is one of them. I think we need to understand how multichip is going to work in order to come up with a sane architecture. Otherwise I fear we'll end up with something that we either need to horribly bastardize for multichip, or have to rework things dramatically leading to migration nightmares.

Quoted 4 lines

So, it is all controlled by MMIO, so we should be fine on that part. As for the internal tables, they are all configured by firmware, using a chip identifier (block). I need to check how the remote XIVE are accessed. I think this is by MMIO.

Right, but for powernv we execute OPAL inside the VM, rather than emulating its effects. So we still need to model the actual hardware interfaces. OPAL hides the details from the kernel, but not from us on the other side.

Quoted 27 lines

I haven't looked at multichip XIVE support but I am not too worried as the framework is already in place for the machine.

Quoted 24 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Quoted 4 lines
Quoted 2 lines

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Yes. For papr variant it would have the whole IVT contents as its state. For the powernv, just the registers telling it where to find the IVT in RAM.

Quoted 4 lines
Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

Quoted 8 lines
Quoted 3 lines

That seems like a lot of steps. Up to push the EQ DATA, certainly. And I guess it'll have to ping an NVT somehow, but I'm not sure it should know about CPUs as such.

For PowerNV, the concept could be generalized, yes. An NVT can contain the interrupt state of a logical server but the common case is baremetal without guests for QEMU and so we have a NVT per cpu.

Hmm. We eventually want to support a kernel running guests under qemu/powernv though, right? So even if we don't allow it right now, we don't want allowing that to require major surgery to our architecture.

Quoted 10 lines

PowerNV will have some limitation but we can make it better than today for sure. It boots.

We can improve some of the NVT notification process, the way NVT are matched eventually. may be support remote engines if the NVT is not local. I have not looked at the details.

Quoted 1 line

I'm not sure at this stage what should own the EQD table.

The EQDT is in RAM.

Not for spapr, it's not. And even when it is in RAM, something needs to own the register that gives its base address.

Quoted 14 lines
Quoted 1 line

In the multichip case is there one EQD table for every IVT?

There is one EQDT per chip, same for the IVT. They are in RAM, identified with a block ID.

Quoted 9 lines

I'm guessing
not - I figure the EQD table must be effectively global so that any chip's router can send events to any EQ in the whole system.

Quoted 5 lines
Quoted 1 line
Quoted 1 line

Now IIUC, on pnv the IVT lives in main system memory.

yes. It is allocated by skiboot in RAM and fed to the HW using some IC configuration registers. Then, each entry is configured with OPAL calls and the HW is updated using cache scrub registers.

Quoted 6 lines
Quoted 3 lines

Right. At least for the first pass we should be able to treat the cache scrub registers as no-ops and just not cache anything in the qemu implementation.

The model currently supports the cache scrub registers, we need it to update some values. It's not too complex.

Ok.

Quoted 31 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

Under PAPR is the IVT in guest memory, or is it outside (updated by hypercalls/rtas)?

Under sPAPR, the IVT is updated by the H_INT_SET_SOURCE_CONFIG hcall which configures the targeting of an IRQ. It's not in the guest memory.

Quoted 8 lines

Right.

Quoted 2 lines

Behind the hood, the IVT is still configured by OPAL under KVM and by QEMU when kernel_irqchip=off

Sure. Even with kernel_irqchip=on there's still logically a guest IVT (or "IVT view" I guess), even if it's actual entries are stored distributed across various places in the host's IVTs.

yes. The XIVE KVM device caches the info. This is used to dump the state without doing OPAL calls.

C.

Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

The further we go here, the less sure I am that XiveFabric even makes sense as a concept.

See previous email.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#36 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 44 lines · David Gibson
Quoted 38 lines · Cédric Le Goater
Quoted 30 lines · David Gibson
Quoted 21 lines · Cédric Le Goater
Quoted 20 lines · David Gibson
Quoted 19 lines · Cédric Le Goater
Quoted 17 lines · David Gibson
Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Quoted 6 lines

Hmm. There's some things we can delay dealing with, but I don't think this is one of them. I think we need to understand how multichip is going to work in order to come up with a sane architecture. Otherwise I fear we'll end up with something that we either need to horribly bastardize for multichip, or have to rework things dramatically leading to migration nightmares.

Quoted 4 lines

So, it is all controlled by MMIO, so we should be fine on that part. As for the internal tables, they are all configured by firmware, using a chip identifier (block). I need to check how the remote XIVE are accessed. I think this is by MMIO.

Quoted 4 lines

Right, but for powernv we execute OPAL inside the VM, rather than emulating its effects. So we still need to model the actual hardware interfaces. OPAL hides the details from the kernel, but not from us on the other side.

Yes. This is the case in the current model. I took a look today and I have a few fixes for the MMIO layout for P9 chips which I will send.

As for XIVE, the model needs to be a little more complex to support VSD_MODE_FORWARD tables which describe how to forward a notification to another XIVE IC on another chip. They contain an address on which to load, This is another hop in the notification chain.

Quoted 31 lines
Quoted 31 lines

I haven't looked at multichip XIVE support but I am not too worried as the framework is already in place for the machine.

Quoted 27 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Quoted 4 lines
Quoted 2 lines

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Yes. For papr variant it would have the whole IVT contents as its state. For the powernv, just the registers telling it where to find the IVT in RAM.

Quoted 4 lines
Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

That seems like a lot of steps. Up to push the EQ DATA, certainly. And I guess it'll have to ping an NVT somehow, but I'm not sure it should know about CPUs as such.

Quoted 7 lines
Quoted 4 lines

For PowerNV, the concept could be generalized, yes. An NVT can contain the interrupt state of a logical server but the common case is baremetal without guests for QEMU and so we have a NVT per cpu.

Hmm. We eventually want to support a kernel running guests under qemu/powernv though, right?

arg. an emulated hypervisor ! OK let's say this is a long term goal :)

Quoted 3 lines

So even if we don't allow it right now,
we don't want allowing that to require major surgery to our architecture.

That I agree on.

Quoted 7 lines
Quoted 7 lines

PowerNV will have some limitation but we can make it better than today for sure. It boots.

We can improve some of the NVT notification process, the way NVT are matched eventually. may be support remote engines if the NVT is not local. I have not looked at the details.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

I'm not sure at this stage what should own the EQD table.

The EQDT is in RAM.

Not for spapr, it's not.

yeah ok. It's in QEMU/KVM.

Quoted 2 lines

And even when it is in RAM, something needs
to own the register that gives its base address.

It's more complex than registers on powernv. There is a procedure to define the XIVE tables using XIVE table descriptors which contain their characteristics, size, indirect vs. indirect, local vs remote. OPAL/skiboot defines all these to configure the HW, and the model necessarily needs to support the same interface. This is the case for a single chip.

C.

Quoted 55 lines
Quoted 14 lines
Quoted 1 line

In the multichip case is there one EQD table for every IVT?

There is one EQDT per chip, same for the IVT. They are in RAM, identified with a block ID.

Quoted 9 lines

I'm guessing
not - I figure the EQD table must be effectively global so that any chip's router can send events to any EQ in the whole system.

Quoted 5 lines
Quoted 1 line
Quoted 1 line

Now IIUC, on pnv the IVT lives in main system memory.

yes. It is allocated by skiboot in RAM and fed to the HW using some IC configuration registers. Then, each entry is configured with OPAL calls and the HW is updated using cache scrub registers.

Quoted 6 lines
Quoted 3 lines

Right. At least for the first pass we should be able to treat the cache scrub registers as no-ops and just not cache anything in the qemu implementation.

The model currently supports the cache scrub registers, we need it to update some values. It's not too complex.

Ok.

Quoted 31 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

Under PAPR is the IVT in guest memory, or is it outside (updated by hypercalls/rtas)?

Under sPAPR, the IVT is updated by the H_INT_SET_SOURCE_CONFIG hcall which configures the targeting of an IRQ. It's not in the guest memory.

Quoted 8 lines

Right.

Quoted 2 lines

Behind the hood, the IVT is still configured by OPAL under KVM and by QEMU when kernel_irqchip=off

Sure. Even with kernel_irqchip=on there's still logically a guest IVT (or "IVT view" I guess), even if it's actual entries are stored distributed across various places in the host's IVTs.

yes. The XIVE KVM device caches the info. This is used to dump the state without doing OPAL calls.

C.

Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

The XiveRouter would also be a XiveFabric (or some other name) to let the internal sources of the interrupt controller forward events.

The further we go here, the less sure I am that XiveFabric even makes sense as a concept.

See previous email.

#37 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 50 lines · Cédric Le Goater
Quoted 44 lines · David Gibson
Quoted 38 lines · Cédric Le Goater
Quoted 30 lines · David Gibson
Quoted 21 lines · Cédric Le Goater
Quoted 20 lines · David Gibson
Quoted 19 lines · Cédric Le Goater
Quoted 17 lines · David Gibson
Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Quoted 6 lines

Hmm. There's some things we can delay dealing with, but I don't think this is one of them. I think we need to understand how multichip is going to work in order to come up with a sane architecture. Otherwise I fear we'll end up with something that we either need to horribly bastardize for multichip, or have to rework things dramatically leading to migration nightmares.

Quoted 4 lines

So, it is all controlled by MMIO, so we should be fine on that part. As for the internal tables, they are all configured by firmware, using a chip identifier (block). I need to check how the remote XIVE are accessed. I think this is by MMIO.

Quoted 4 lines

Right, but for powernv we execute OPAL inside the VM, rather than emulating its effects. So we still need to model the actual hardware interfaces. OPAL hides the details from the kernel, but not from us on the other side.

Quoted 7 lines

Yes. This is the case in the current model. I took a look today and I have a few fixes for the MMIO layout for P9 chips which I will send.

As for XIVE, the model needs to be a little more complex to support VSD_MODE_FORWARD tables which describe how to forward a notification to another XIVE IC on another chip. They contain an address on which to load, This is another hop in the notification chain.

Ah, ok. So is that mode and address configured in the (bare metal) IVT as well? Or is that a different piece of configuration?

Quoted 65 lines
Quoted 31 lines
Quoted 31 lines

I haven't looked at multichip XIVE support but I am not too worried as the framework is already in place for the machine.

Quoted 27 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Quoted 4 lines
Quoted 2 lines

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Yes. For papr variant it would have the whole IVT contents as its state. For the powernv, just the registers telling it where to find the IVT in RAM.

Quoted 4 lines
Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

That seems like a lot of steps. Up to push the EQ DATA, certainly. And I guess it'll have to ping an NVT somehow, but I'm not sure it should know about CPUs as such.

Quoted 7 lines
Quoted 4 lines

For PowerNV, the concept could be generalized, yes. An NVT can contain the interrupt state of a logical server but the common case is baremetal without guests for QEMU and so we have a NVT per cpu.

Hmm. We eventually want to support a kernel running guests under qemu/powernv though, right?

arg. an emulated hypervisor ! OK let's say this is a long term goal :)

Quoted 3 lines

So even if we don't allow it right now,
we don't want allowing that to require major surgery to our architecture.

That I agree on.

Quoted 7 lines
Quoted 7 lines

PowerNV will have some limitation but we can make it better than today for sure. It boots.

We can improve some of the NVT notification process, the way NVT are matched eventually. may be support remote engines if the NVT is not local. I have not looked at the details.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

I'm not sure at this stage what should own the EQD table.

The EQDT is in RAM.

Not for spapr, it's not.

yeah ok. It's in QEMU/KVM.

Quoted 2 lines

And even when it is in RAM, something needs
to own the register that gives its base address.

Quoted 6 lines

It's more complex than registers on powernv. There is a procedure to define the XIVE tables using XIVE table descriptors which contain their characteristics, size, indirect vs. indirect, local vs remote. OPAL/skiboot defines all these to configure the HW, and the model necessarily needs to support the same interface. This is the case for a single chip.

Ah, ok. So there's some sort of IVTD. Also in RAM? Eventually there must be a register giving the base address of the IVTD, yes?

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#38 lore raw

Re: [Qemu-devel] [PATCH v3 04/35] spapr/xive: introduce a XIVE interrupt controller for sPAPR

Quoted 54 lines · David Gibson
Quoted 53 lines · Cédric Le Goater
Quoted 44 lines · David Gibson
Quoted 38 lines · Cédric Le Goater
Quoted 30 lines · David Gibson
Quoted 21 lines · Cédric Le Goater
Quoted 20 lines · David Gibson
Quoted 19 lines · Cédric Le Goater
Quoted 17 lines · David Gibson
Quoted 5 lines · Cédric Le Goater

sPAPRXive is a model for the XIVE interrupt controller device of the sPAPR machine. It holds the routing XIVE table, the Interrupt Virtualization Entry (IVE) table which associates interrupt source numbers with targets.

Quoted 8 lines

Also extend the XiveFabric with an accessor to the IVT. This will be needed by the routing algorithm.

---

May be should introduce a XiveRouter model to hold the IVT. To be discussed.

Yeah, maybe. Am I correct in thinking that on pnv there could be more than one XiveRouter?

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

There is only one, the main IC.

Ok, that's what I thought originally. In that case some of the stuff in the patches really doesn't make sense to me.

well, there is one IC per chip on powernv, but we haven't reach that part yet.

Quoted 6 lines

Hmm. There's some things we can delay dealing with, but I don't think this is one of them. I think we need to understand how multichip is going to work in order to come up with a sane architecture. Otherwise I fear we'll end up with something that we either need to horribly bastardize for multichip, or have to rework things dramatically leading to migration nightmares.

Quoted 4 lines

So, it is all controlled by MMIO, so we should be fine on that part. As for the internal tables, they are all configured by firmware, using a chip identifier (block). I need to check how the remote XIVE are accessed. I think this is by MMIO.

Quoted 4 lines

Right, but for powernv we execute OPAL inside the VM, rather than emulating its effects. So we still need to model the actual hardware interfaces. OPAL hides the details from the kernel, but not from us on the other side.

Yes. This is the case in the current model. I took a look today and I have a few fixes for the MMIO layout for P9 chips which I will send.

Quoted 7 lines
Quoted 4 lines

As for XIVE, the model needs to be a little more complex to support VSD_MODE_FORWARD tables which describe how to forward a notification to another XIVE IC on another chip. They contain an address on which to load, This is another hop in the notification chain.

Ah, ok. So is that mode and address configured in the (bare metal) IVT as well? Or is that a different piece of configuration?

The mode of a virtual structure table is configured by firmware. There are 4 main table types: IVT, SBE, EQD, VPD (and an extra one for IRQ) for the 16 possible blocks of a machine (I am simplifying a bit there).

Local tables to a block/chip, today, are set to EXCLUSIVE and all remotes tables set to FORWARD.

The address of a table is configured by FW also. In case of a FORWARD table, it is set to the remote IC BAR + one page. This page has two 2K windows : one for for HW interrupt triggers and another one for to forward interrupts and for operation synchronization.

Quoted 65 lines
Quoted 65 lines
Quoted 31 lines
Quoted 31 lines

I haven't looked at multichip XIVE support but I am not too worried as the framework is already in place for the machine.

Quoted 27 lines
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines

If we did have a XiveRouter, I'm not sure we'd need the XiveFabric interface, possibly its methods could just be class methods of XiveRouter.

Yes. We could introduce a XiveRouter to share the ivt table between the sPAPRXive and the PnvXIVE models, the interrupt controllers of the machines. Methods would provide way to get the ivt/eq/nvt objects required for routing. I need to add a set_eq() to push the EQ data.

Quoted 4 lines
Quoted 2 lines

Hrm. Well, to add some more clarity, let's say the XiveRouter is the object which owns the IVT.

OK. that would be a model with some state and not an interface.

Yes. For papr variant it would have the whole IVT contents as its state. For the powernv, just the registers telling it where to find the IVT in RAM.

Quoted 4 lines
Quoted 1 line

It may or may not do other stuff as well.

Its only task would be to do the final event routing: get the IVE, get the EQ, push the EQ DATA in the OS event queue, notify the CPU.

That seems like a lot of steps. Up to push the EQ DATA, certainly. And I guess it'll have to ping an NVT somehow, but I'm not sure it should know about CPUs as such.

Quoted 7 lines
Quoted 4 lines

For PowerNV, the concept could be generalized, yes. An NVT can contain the interrupt state of a logical server but the common case is baremetal without guests for QEMU and so we have a NVT per cpu.

Hmm. We eventually want to support a kernel running guests under qemu/powernv though, right?

arg. an emulated hypervisor ! OK let's say this is a long term goal :)

Quoted 3 lines

So even if we don't allow it right now,
we don't want allowing that to require major surgery to our architecture.

That I agree on.

Quoted 7 lines
Quoted 7 lines

PowerNV will have some limitation but we can make it better than today for sure. It boots.

We can improve some of the NVT notification process, the way NVT are matched eventually. may be support remote engines if the NVT is not local. I have not looked at the details.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

I'm not sure at this stage what should own the EQD table.

The EQDT is in RAM.

Not for spapr, it's not.

yeah ok. It's in QEMU/KVM.

Quoted 2 lines

And even when it is in RAM, something needs
to own the register that gives its base address.

Quoted 8 lines
Quoted 6 lines

It's more complex than registers on powernv. There is a procedure to define the XIVE tables using XIVE table descriptors which contain their characteristics, size, indirect vs. indirect, local vs remote. OPAL/skiboot defines all these to configure the HW, and the model necessarily needs to support the same interface. This is the case for a single chip.

Ah, ok. So there's some sort of IVTD.

These are called Virtual Structure table Descriptors (VSDs). Each XIVE chip has an array of these.

Quoted 1 line

Also in RAM?

Yes. But the vsd are just temporary structures to configure HW. What is important is the information it is holding : IVT, EQDT, VPDT, etc.

Quoted 2 lines

Eventually there
must be a register giving the base address of the IVTD, yes?

There are two registers to configure the table. One to set the table type and block, and one to set its VSD.

C.

#39 lore raw

PATCH v3 5/35 spapr/xive: add a single source block to the sPAPR XIVE model

In reply to Cédric Le Goater

Bare-metal systems (PowerNV) have multiples interrupt sources. The XIVE interrupt controller has an internal source for IPIs and generic IPIs, the PSIHB has one and also the PHBs. But, for simplicity on the sPAPR machine, we use a unique XiveSource object for all IPIs and virtual device interrupts of the VM.

The ESB MMIO region used to control the sources is mapped at the address of chip 0 of a real system and only the provisioned IRQ numbers are covered.

---
 hw/intc/spapr_xive.c        | 34 ++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |  3 +++
 include/hw/ppc/xive.h       |  6 ++++++
 3 files changed, 43 insertions(+)
hw/intc/spapr_xive.c +34 -0
index 020444e2665a..90cde8a4082d 100644
@@ -14,12 +14,15 @@
 #include "sysemu/cpus.h"
 #include "monitor/monitor.h"
 #include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive.h"
 #include "hw/ppc/xive_regs.h"
 
 void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
 {
     int i;
 
+    xive_source_pic_print_info(&xive->source, mon);
+
     monitor_printf(mon, "IVE Table\n");
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -40,6 +43,9 @@ static void spapr_xive_reset(DeviceState *dev)
     sPAPRXive *xive = SPAPR_XIVE(dev);
     int i;
 
+    /* Xive Source reset is done through SysBus, it should put all
+     * IRQs to OFF (!P|Q) */
+
     /* Mask all valid IVEs in the IRQ number space. */
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -51,18 +57,42 @@ static void spapr_xive_reset(DeviceState *dev)
 
 static void spapr_xive_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);
 }
 
 static void spapr_xive_realize(DeviceState *dev, Error **errp)
 {
     sPAPRXive *xive = SPAPR_XIVE(dev);
+    XiveSource *xsrc = &xive->source;
+    Error *local_err = NULL;
 
     if (!xive->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater 0");
         return;
     }
 
+    /* The XIVE interrupt controller has an internal source for IPIs
+     * and generic IPIs, the PSIHB has one and also the PHBs. For
+     * simplicity, we use a unique XIVE source object for *all*
+     * interrupts on sPAPR. The ESBs pages are mapped at the address
+     * of chip 0 of a real system.
+     */
+    object_property_set_int(OBJECT(xsrc), XIVE_VC_BASE, "bar",
+                            &error_fatal);
+    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;
+    }
+    qdev_set_parent_bus(DEVICE(xsrc), sysbus_get_default());
+
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
 }
@@ -137,23 +167,27 @@ type_init(spapr_xive_register_types)
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w |= IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, lsi);
     return true;
 }
 
 bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w &= ~IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, false);
     return true;
 }
include/hw/ppc/spapr_xive.h +3 -0
index 1d966b5d3a96..4538c622b60a 100644
@@ -19,6 +19,9 @@
 typedef struct sPAPRXive {
     SysBusDevice parent;
 
+    /* Internal interrupt source for IPIs and virtual devices */
+    XiveSource   source;
+
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
include/hw/ppc/xive.h +6 -0
index 5b145816acdc..57295715a4a5 100644
@@ -16,6 +16,12 @@
 typedef struct XiveFabric XiveFabric;
 
 /*
+ * XIVE MMIO regions
+ */
+
+#define XIVE_VC_BASE   0x0006010000000000ull
+
+/*
  * XIVE Interrupt Source
  */
 
Signature · 1 line
2.13.6
#40 lore raw

Re: [Qemu-devel] [PATCH v3 05/35] spapr/xive: add a single source block to the sPAPR XIVE model

Quoted 9 lines · Cédric Le Goater

Bare-metal systems (PowerNV) have multiples interrupt sources. The XIVE interrupt controller has an internal source for IPIs and generic IPIs, the PSIHB has one and also the PHBs. But, for simplicity on the sPAPR machine, we use a unique XiveSource object for all IPIs and virtual device interrupts of the VM.

The ESB MMIO region used to control the sources is mapped at the address of chip 0 of a real system and only the provisioned IRQ numbers are covered.

Is that MMIO address PAPR specified, or arbitrary?

Quoted 139 lines
---
 hw/intc/spapr_xive.c        | 34 ++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |  3 +++
 include/hw/ppc/xive.h       |  6 ++++++
 3 files changed, 43 insertions(+)
hw/intc/spapr_xive.c +34 -0
index 020444e2665a..90cde8a4082d 100644
@@ -14,12 +14,15 @@
 #include "sysemu/cpus.h"
 #include "monitor/monitor.h"
 #include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive.h"
 #include "hw/ppc/xive_regs.h"
 
 void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
 {
     int i;
 
+    xive_source_pic_print_info(&xive->source, mon);
+
     monitor_printf(mon, "IVE Table\n");
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -40,6 +43,9 @@ static void spapr_xive_reset(DeviceState *dev)
     sPAPRXive *xive = SPAPR_XIVE(dev);
     int i;
 
+    /* Xive Source reset is done through SysBus, it should put all
+     * IRQs to OFF (!P|Q) */
+
     /* Mask all valid IVEs in the IRQ number space. */
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -51,18 +57,42 @@ static void spapr_xive_reset(DeviceState *dev)
 
 static void spapr_xive_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);
 }
 
 static void spapr_xive_realize(DeviceState *dev, Error **errp)
 {
     sPAPRXive *xive = SPAPR_XIVE(dev);
+    XiveSource *xsrc = &xive->source;
+    Error *local_err = NULL;
 
     if (!xive->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater 0");
         return;
     }
 
+    /* The XIVE interrupt controller has an internal source for IPIs
+     * and generic IPIs, the PSIHB has one and also the PHBs. For
+     * simplicity, we use a unique XIVE source object for *all*
+     * interrupts on sPAPR. The ESBs pages are mapped at the address
+     * of chip 0 of a real system.
+     */
+    object_property_set_int(OBJECT(xsrc), XIVE_VC_BASE, "bar",
+                            &error_fatal);
+    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;
+    }
+    qdev_set_parent_bus(DEVICE(xsrc), sysbus_get_default());
+
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
 }
@@ -137,23 +167,27 @@ type_init(spapr_xive_register_types)
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w |= IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, lsi);
     return true;
 }
 
 bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w &= ~IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, false);
     return true;
 }
include/hw/ppc/spapr_xive.h +3 -0
index 1d966b5d3a96..4538c622b60a 100644
@@ -19,6 +19,9 @@
 typedef struct sPAPRXive {
     SysBusDevice parent;
 
+    /* Internal interrupt source for IPIs and virtual devices */
+    XiveSource   source;
+
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
include/hw/ppc/xive.h +6 -0
index 5b145816acdc..57295715a4a5 100644
@@ -16,6 +16,12 @@
 typedef struct XiveFabric XiveFabric;
 
 /*
+ * XIVE MMIO regions
+ */
+
+#define XIVE_VC_BASE   0x0006010000000000ull
+
+/*
  * XIVE Interrupt Source
  */
 
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#41 lore raw

Re: [Qemu-devel] [PATCH v3 05/35] spapr/xive: add a single source block to the sPAPR XIVE model

Quoted 7 lines · David Gibson
Quoted 6 lines · Cédric Le Goater

Bare-metal systems (PowerNV) have multiples interrupt sources. The XIVE interrupt controller has an internal source for IPIs and generic IPIs, the PSIHB has one and also the PHBs. But, for simplicity on the sPAPR machine, we use a unique XiveSource object for all IPIs and virtual device interrupts of the VM.

Quoted 5 lines
Quoted 3 lines

The ESB MMIO region used to control the sources is mapped at the address of chip 0 of a real system and only the provisioned IRQ numbers are covered.

Is that MMIO address PAPR specified, or arbitrary?

There are no specified value for the ESB address. It's queried by the guest using the H_INT_GET_SOURCE_INFO hcall. For KVM, I have introduced a ioctl to configure the KVM device.

Same for the TIMA, but in this case, the address is exposed to the guest in the device tree.

Quoted 141 lines
Quoted 140 lines
---
 hw/intc/spapr_xive.c        | 34 ++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |  3 +++
 include/hw/ppc/xive.h       |  6 ++++++
 3 files changed, 43 insertions(+)
hw/intc/spapr_xive.c +34 -0
index 020444e2665a..90cde8a4082d 100644
@@ -14,12 +14,15 @@
 #include "sysemu/cpus.h"
 #include "monitor/monitor.h"
 #include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive.h"
 #include "hw/ppc/xive_regs.h"
 
 void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
 {
     int i;
 
+    xive_source_pic_print_info(&xive->source, mon);
+
     monitor_printf(mon, "IVE Table\n");
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -40,6 +43,9 @@ static void spapr_xive_reset(DeviceState *dev)
     sPAPRXive *xive = SPAPR_XIVE(dev);
     int i;
 
+    /* Xive Source reset is done through SysBus, it should put all
+     * IRQs to OFF (!P|Q) */
+
     /* Mask all valid IVEs in the IRQ number space. */
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -51,18 +57,42 @@ static void spapr_xive_reset(DeviceState *dev)
 
 static void spapr_xive_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);
 }
 
 static void spapr_xive_realize(DeviceState *dev, Error **errp)
 {
     sPAPRXive *xive = SPAPR_XIVE(dev);
+    XiveSource *xsrc = &xive->source;
+    Error *local_err = NULL;
 
     if (!xive->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater 0");
         return;
     }
 
+    /* The XIVE interrupt controller has an internal source for IPIs
+     * and generic IPIs, the PSIHB has one and also the PHBs. For
+     * simplicity, we use a unique XIVE source object for *all*
+     * interrupts on sPAPR. The ESBs pages are mapped at the address
+     * of chip 0 of a real system.
+     */
+    object_property_set_int(OBJECT(xsrc), XIVE_VC_BASE, "bar",
+                            &error_fatal);
+    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;
+    }
+    qdev_set_parent_bus(DEVICE(xsrc), sysbus_get_default());
+
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
 }
@@ -137,23 +167,27 @@ type_init(spapr_xive_register_types)
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w |= IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, lsi);
     return true;
 }
 
 bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w &= ~IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, false);
     return true;
 }
include/hw/ppc/spapr_xive.h +3 -0
index 1d966b5d3a96..4538c622b60a 100644
@@ -19,6 +19,9 @@
 typedef struct sPAPRXive {
     SysBusDevice parent;
 
+    /* Internal interrupt source for IPIs and virtual devices */
+    XiveSource   source;
+
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
include/hw/ppc/xive.h +6 -0
index 5b145816acdc..57295715a4a5 100644
@@ -16,6 +16,12 @@
 typedef struct XiveFabric XiveFabric;
 
 /*
+ * XIVE MMIO regions
+ */
+
+#define XIVE_VC_BASE   0x0006010000000000ull
+
+/*
  * XIVE Interrupt Source
  */
 
#42 lore raw

Re: [Qemu-devel] [PATCH v3 05/35] spapr/xive: add a single source block to the sPAPR XIVE model

Quoted 12 lines · Cédric Le Goater
Quoted 11 lines · David Gibson
Quoted 9 lines · Cédric Le Goater

Bare-metal systems (PowerNV) have multiples interrupt sources. The XIVE interrupt controller has an internal source for IPIs and generic IPIs, the PSIHB has one and also the PHBs. But, for simplicity on the sPAPR machine, we use a unique XiveSource object for all IPIs and virtual device interrupts of the VM.

The ESB MMIO region used to control the sources is mapped at the address of chip 0 of a real system and only the provisioned IRQ numbers are covered.

Quoted 5 lines
Quoted 1 line

Is that MMIO address PAPR specified, or arbitrary?

There are no specified value for the ESB address. It's queried by the guest using the H_INT_GET_SOURCE_INFO hcall. For KVM, I have introduced a ioctl to configure the KVM device.

Ok.

Quoted 149 lines

Same for the TIMA, but in this case, the address is exposed to the guest in the device tree.

Quoted 144 lines
Quoted 143 lines
---
 hw/intc/spapr_xive.c        | 34 ++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |  3 +++
 include/hw/ppc/xive.h       |  6 ++++++
 3 files changed, 43 insertions(+)
hw/intc/spapr_xive.c +34 -0
index 020444e2665a..90cde8a4082d 100644
@@ -14,12 +14,15 @@
 #include "sysemu/cpus.h"
 #include "monitor/monitor.h"
 #include "hw/ppc/spapr_xive.h"
+#include "hw/ppc/xive.h"
 #include "hw/ppc/xive_regs.h"
 
 void spapr_xive_pic_print_info(sPAPRXive *xive, Monitor *mon)
 {
     int i;
 
+    xive_source_pic_print_info(&xive->source, mon);
+
     monitor_printf(mon, "IVE Table\n");
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -40,6 +43,9 @@ static void spapr_xive_reset(DeviceState *dev)
     sPAPRXive *xive = SPAPR_XIVE(dev);
     int i;
 
+    /* Xive Source reset is done through SysBus, it should put all
+     * IRQs to OFF (!P|Q) */
+
     /* Mask all valid IVEs in the IRQ number space. */
     for (i = 0; i < xive->nr_irqs; i++) {
         XiveIVE *ive = &xive->ivt[i];
@@ -51,18 +57,42 @@ static void spapr_xive_reset(DeviceState *dev)
 
 static void spapr_xive_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);
 }
 
 static void spapr_xive_realize(DeviceState *dev, Error **errp)
 {
     sPAPRXive *xive = SPAPR_XIVE(dev);
+    XiveSource *xsrc = &xive->source;
+    Error *local_err = NULL;
 
     if (!xive->nr_irqs) {
         error_setg(errp, "Number of interrupt needs to be greater 0");
         return;
     }
 
+    /* The XIVE interrupt controller has an internal source for IPIs
+     * and generic IPIs, the PSIHB has one and also the PHBs. For
+     * simplicity, we use a unique XIVE source object for *all*
+     * interrupts on sPAPR. The ESBs pages are mapped at the address
+     * of chip 0 of a real system.
+     */
+    object_property_set_int(OBJECT(xsrc), XIVE_VC_BASE, "bar",
+                            &error_fatal);
+    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;
+    }
+    qdev_set_parent_bus(DEVICE(xsrc), sysbus_get_default());
+
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
 }
@@ -137,23 +167,27 @@ type_init(spapr_xive_register_types)
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w |= IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, lsi);
     return true;
 }
 
 bool spapr_xive_irq_disable(sPAPRXive *xive, uint32_t lisn)
 {
     XiveIVE *ive = spapr_xive_get_ive(XIVE_FABRIC(xive), lisn);
+    XiveSource *xsrc = &xive->source;
 
     if (!ive) {
         return false;
     }
 
     ive->w &= ~IVE_VALID;
+    xive_source_irq_set(xsrc, lisn - xsrc->offset, false);
     return true;
 }
include/hw/ppc/spapr_xive.h +3 -0
index 1d966b5d3a96..4538c622b60a 100644
@@ -19,6 +19,9 @@
 typedef struct sPAPRXive {
     SysBusDevice parent;
 
+    /* Internal interrupt source for IPIs and virtual devices */
+    XiveSource   source;
+
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
include/hw/ppc/xive.h +6 -0
index 5b145816acdc..57295715a4a5 100644
@@ -16,6 +16,12 @@
 typedef struct XiveFabric XiveFabric;
 
 /*
+ * XIVE MMIO regions
+ */
+
+#define XIVE_VC_BASE   0x0006010000000000ull
 
 
 
+
+/*

* XIVE Interrupt Source
*/

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#43 lore raw

PATCH v3 6/35 spapr/xive: introduce a XIVE interrupt presenter model

In reply to Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +25 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +279 -0

298 lines not on this page · raw message

include/hw/ppc/spapr_xive.h +5 -0
index 4538c622b60a..25d78eec884d 100644
@@ -25,6 +25,11 @@ typedef struct sPAPRXive {
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
+
+    /* TIMA memory regions */
+    hwaddr       tm_base;
+    MemoryRegion tm_mmio_user;
+    MemoryRegion tm_mmio_os;
 } sPAPRXive;
 
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
include/hw/ppc/xive.h +31 -0
index 57295715a4a5..1a2da610d91c 100644
@@ -20,6 +20,7 @@ typedef struct XiveFabric XiveFabric;
  */
 
 #define XIVE_VC_BASE   0x0006010000000000ull
+#define XIVE_TM_BASE   0x0006030203180000ull
 
 /*
  * XIVE Interrupt Source
@@ -155,6 +156,34 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
 }
 
 /*
+ * XIVE Interrupt Presenter
+ */
+
+#define TYPE_XIVE_NVT "xive-nvt"
+#define XIVE_NVT(obj) OBJECT_CHECK(XiveNVT, (obj), TYPE_XIVE_NVT)
+
+#define TM_RING_COUNT           4
+#define TM_RING_SIZE            0x10
+
+typedef struct XiveNVT {
+    DeviceState parent_obj;
+
+    CPUState  *cs;
+    qemu_irq  output;
+
+    /* Thread interrupt Management (TM) registers */
+    uint8_t   regs[TM_RING_COUNT * TM_RING_SIZE];
+
+    /* Shortcuts to rings */
+    uint8_t   *ring_os;
+} XiveNVT;
+
+extern const MemoryRegionOps xive_tm_user_ops;
+extern const MemoryRegionOps xive_tm_os_ops;
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon);
+
+/*
  * XIVE Fabric
  */
 
@@ -175,8 +204,10 @@ typedef struct XiveFabricClass {
     void (*notify)(XiveFabric *xf, uint32_t lisn);
 
     XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
+    XiveNVT *(*get_nvt)(XiveFabric *xf, uint32_t server);
 } XiveFabricClass;
 
 XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server);
 
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h +84 -0

94 lines not on this page · raw message

Signature · 1 line
2.13.6
#44 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 49 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 9 lines
hw/intc/spapr_xive.c +6 -0
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 14 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 9 lines
hw/intc/spapr_xive.c +6 -0
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 30 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +9 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{

hw/intc/xive.c line 36

Quoted 10 lines
hw/intc/xive.c +10 -0
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 16 lines
hw/intc/xive.c +16 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }

hw/intc/xive.c line 62

Quoted 10 lines
hw/intc/xive.c +10 -0
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

hw/intc/xive.c line 74

Quoted 12 lines
hw/intc/xive.c +12 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

Quoted 26 lines
hw/intc/xive.c +26 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{

hw/intc/xive.c line 110

Quoted 10 lines
hw/intc/xive.c +10 -0
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

Quoted 105 lines
hw/intc/xive.c +105 -0
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return;
+    }
+
+    switch (size) {
+    case 1:
+        if (offset == TM_QW1_OS + TM_CPPR) {
+            xive_nvt_set_cppr(nvt, value & 0xff);
+        }
+        break;
+    case 4:
+    case 8:
+        for (i = 0; i < size; i++) {
+            if (!xive_tm_is_readonly(offset + i)) {
+                nvt->regs[offset + i] = (value >> (8 * (size - i - 1))) & 0xff;
+            }
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+const MemoryRegionOps xive_tm_os_ops = {
+    .read = xive_tm_os_read,
+    .write = xive_tm_os_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+/*
+ * User Thread Interrupt Management Area MMIO
+ */
+
+static uint64_t xive_tm_user_read(void *opaque, hwaddr offset,
+                                        unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%"
+                  HWADDR_PRIx"\n", offset);
+    return -1;
+}
+
+static void xive_tm_user_write(void *opaque, hwaddr offset,
+                                     uint64_t value, unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%"
+                  HWADDR_PRIx"\n", offset);
+}
+
+
+const MemoryRegionOps xive_tm_user_ops = {
+    .read = xive_tm_user_read,
+    .write = xive_tm_user_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+static char *xive_nvt_ring_print(uint8_t *ring)
+{
+    uint32_t w2 = be32_to_cpu(*((uint32_t *) &ring[TM_WORD2]));
+
+    return g_strdup_printf("%02x  %02x   %02x  %02x    %02x   "
+                   "%02x  %02x  %02x   %08x",
+                   ring[TM_NSR], ring[TM_CPPR], ring[TM_IPB], ring[TM_LSMFB],
+                   ring[TM_ACK_CNT], ring[TM_INC], ring[TM_AGE], ring[TM_PIPR],
+                   w2);
+}
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon)
+{
+    int cpu_index = nvt->cs ? nvt->cs->cpu_index : -1;
+    char *s;
+
+    monitor_printf(mon, "CPU[%04x]: QW    NSR CPPR IPB LSMFB ACK# INC AGE PIPR"
+                   " W2\n", cpu_index);
+
+    s = xive_nvt_ring_print(&nvt->regs[TM_QW1_OS]);
+    monitor_printf(mon, "CPU[%04x]: OS    %s\n", cpu_index, s);
+    g_free(s);
+    s = xive_nvt_ring_print(&nvt->regs[TM_QW0_USER]);
+    monitor_printf(mon, "CPU[%04x]: USER  %s\n", cpu_index, s);
+    g_free(s);
+}
+
+static void xive_nvt_reset(void *dev)
+{
+    XiveNVT *nvt = XIVE_NVT(dev);
+
+    memset(nvt->regs, 0, sizeof(nvt->regs));
+}

hw/intc/xive.c line 225

Quoted 10 lines
hw/intc/xive.c +10 -0
+
+static void xive_nvt_realize(DeviceState *dev, Error **errp)
+{
+    XiveNVT *nvt = XIVE_NVT(dev);
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    Object *obj;
+    Error *err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err);

Please get rid of the remaining "ICP" naming in the xive code.

Quoted 12 lines
hw/intc/xive.c +12 -0
+    if (!obj) {
+        error_propagate(errp, err);
+        error_prepend(errp, "required link '" ICP_PROP_CPU "' not found: ");
+        return;
+    }
+
+    cpu = POWERPC_CPU(obj);
+    nvt->cs = CPU(obj);
+
+    env = &cpu->env;
+    switch (PPC_INPUT(env)) {
+    case PPC_FLAGS_INPUT_POWER7:

hw/intc/xive.c line 247

Quoted 10 lines
hw/intc/xive.c +10 -0
+        nvt->output = env->irq_inputs[POWER7_INPUT_INT];
+        break;
+
+    default:
+        error_setg(errp, "XIVE interrupt controller does not support "
+                   "this CPU bus model");
+        return;
+    }
+
+    qemu_register_reset(xive_nvt_reset, dev);

If this is a sysbus device, which I think it is, you shouldn't need to explicitly register a reset handler. Instead you can set a device reset handler which will be called with the reset.

hw/intc/xive.c line 259

Quoted 12 lines
hw/intc/xive.c +12 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

Quoted 228 lines
hw/intc/xive.c +37 -0
+}
+
+static const VMStateDescription vmstate_xive_nvt = {
+    .name = TYPE_XIVE_NVT,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_BUFFER(regs, XiveNVT),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void xive_nvt_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_nvt_realize;
+    dc->unrealize = xive_nvt_unrealize;
+    dc->desc = "XIVE Interrupt Presenter";
+    dc->vmsd = &vmstate_xive_nvt;
+}
+
+static const TypeInfo xive_nvt_info = {
+    .name          = TYPE_XIVE_NVT,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(XiveNVT),
+    .instance_init = xive_nvt_init,
+    .class_init    = xive_nvt_class_init,
+};
 
 /*
  * XIVE Fabric
@@ -27,6 +298,13 @@ XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
     return xfc->get_ive(xf, lisn);
 }
 
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_nvt(xf, server);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
@@ -418,6 +696,7 @@ static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
     type_register_static(&xive_fabric_info);
+    type_register_static(&xive_nvt_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/spapr_xive.h +5 -0
index 4538c622b60a..25d78eec884d 100644
@@ -25,6 +25,11 @@ typedef struct sPAPRXive {
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
+
+    /* TIMA memory regions */
+    hwaddr       tm_base;
+    MemoryRegion tm_mmio_user;
+    MemoryRegion tm_mmio_os;
 } sPAPRXive;
 
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
include/hw/ppc/xive.h +31 -0
index 57295715a4a5..1a2da610d91c 100644
@@ -20,6 +20,7 @@ typedef struct XiveFabric XiveFabric;
  */
 
 #define XIVE_VC_BASE   0x0006010000000000ull
+#define XIVE_TM_BASE   0x0006030203180000ull
 
 /*
  * XIVE Interrupt Source
@@ -155,6 +156,34 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
 }
 
 /*
+ * XIVE Interrupt Presenter
+ */
+
+#define TYPE_XIVE_NVT "xive-nvt"
+#define XIVE_NVT(obj) OBJECT_CHECK(XiveNVT, (obj), TYPE_XIVE_NVT)
+
+#define TM_RING_COUNT           4
+#define TM_RING_SIZE            0x10
+
+typedef struct XiveNVT {
+    DeviceState parent_obj;
+
+    CPUState  *cs;
+    qemu_irq  output;
+
+    /* Thread interrupt Management (TM) registers */
+    uint8_t   regs[TM_RING_COUNT * TM_RING_SIZE];
+
+    /* Shortcuts to rings */
+    uint8_t   *ring_os;
+} XiveNVT;
+
+extern const MemoryRegionOps xive_tm_user_ops;
+extern const MemoryRegionOps xive_tm_os_ops;
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon);
+
+/*
  * XIVE Fabric
  */
 
@@ -175,8 +204,10 @@ typedef struct XiveFabricClass {
     void (*notify)(XiveFabric *xf, uint32_t lisn);
 
     XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
+    XiveNVT *(*get_nvt)(XiveFabric *xf, uint32_t server);
 } XiveFabricClass;
 
 XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server);
 
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h +84 -0
index 5903f29eb789..f2e2a1ac8f6e 100644
@@ -10,6 +10,88 @@
 #ifndef _PPC_XIVE_REGS_H
 #define _PPC_XIVE_REGS_H
 
+#define TM_SHIFT                16
+
+/* TM register offsets */
+#define TM_QW0_USER             0x000 /* All rings */
+#define TM_QW1_OS               0x010 /* Ring 0..2 */
+#define TM_QW2_HV_POOL          0x020 /* Ring 0..1 */
+#define TM_QW3_HV_PHYS          0x030 /* Ring 0..1 */
+
+/* Byte offsets inside a QW             QW0 QW1 QW2 QW3 */
+#define TM_NSR                  0x0  /*  +   +   -   +  */
+#define TM_CPPR                 0x1  /*  -   +   -   +  */
+#define TM_IPB                  0x2  /*  -   +   +   +  */
+#define TM_LSMFB                0x3  /*  -   +   +   +  */
+#define TM_ACK_CNT              0x4  /*  -   +   -   -  */
+#define TM_INC                  0x5  /*  -   +   -   +  */
+#define TM_AGE                  0x6  /*  -   +   -   +  */
+#define TM_PIPR                 0x7  /*  -   +   -   +  */
+
+#define TM_WORD0                0x0
+#define TM_WORD1                0x4
+
+/*
+ * QW word 2 contains the valid bit at the top and other fields
+ * depending on the QW.
+ */
+#define TM_WORD2                0x8
+#define   TM_QW0W2_VU           PPC_BIT32(0)
+#define   TM_QW0W2_LOGIC_SERV   PPC_BITMASK32(1, 31) /* XX 2,31 ? */
+#define   TM_QW1W2_VO           PPC_BIT32(0)
+#define   TM_QW1W2_OS_CAM       PPC_BITMASK32(8, 31)
+#define   TM_QW2W2_VP           PPC_BIT32(0)
+#define   TM_QW2W2_POOL_CAM     PPC_BITMASK32(8, 31)
+#define   TM_QW3W2_VT           PPC_BIT32(0)
+#define   TM_QW3W2_LP           PPC_BIT32(6)
+#define   TM_QW3W2_LE           PPC_BIT32(7)
+#define   TM_QW3W2_T            PPC_BIT32(31)
+
+/*
+ * In addition to normal loads to "peek" and writes (only when invalid)
+ * using 4 and 8 bytes accesses, the above registers support these
+ * "special" byte operations:
+ *
+ *   - Byte load from QW0[NSR] - User level NSR (EBB)
+ *   - Byte store to QW0[NSR] - User level NSR (EBB)
+ *   - Byte load/store to QW1[CPPR] and QW3[CPPR] - CPPR access
+ *   - Byte load from QW3[TM_WORD2] - Read VT||00000||LP||LE on thrd 0
+ *                                    otherwise VT||0000000
+ *   - Byte store to QW3[TM_WORD2] - Set VT bit (and LP/LE if present)
+ *
+ * Then we have all these "special" CI ops at these offset that trigger
+ * all sorts of side effects:
+ */
+#define TM_SPC_ACK_EBB          0x800   /* Load8 ack EBB to reg*/
+#define TM_SPC_ACK_OS_REG       0x810   /* Load16 ack OS irq to reg */
+#define TM_SPC_PUSH_USR_CTX     0x808   /* Store32 Push/Validate user context */
+#define TM_SPC_PULL_USR_CTX     0x808   /* Load32 Pull/Invalidate user
+                                         * context */
+#define TM_SPC_SET_OS_PENDING   0x812   /* Store8 Set OS irq pending bit */
+#define TM_SPC_PULL_OS_CTX      0x818   /* Load32/Load64 Pull/Invalidate OS
+                                         * context to reg */
+#define TM_SPC_PULL_POOL_CTX    0x828   /* Load32/Load64 Pull/Invalidate Pool
+                                         * context to reg*/
+#define TM_SPC_ACK_HV_REG       0x830   /* Load16 ack HV irq to reg */
+#define TM_SPC_PULL_USR_CTX_OL  0xc08   /* Store8 Pull/Inval usr ctx to odd
+                                         * line */
+#define TM_SPC_ACK_OS_EL        0xc10   /* Store8 ack OS irq to even line */
+#define TM_SPC_ACK_HV_POOL_EL   0xc20   /* Store8 ack HV evt pool to even
+                                         * line */
+#define TM_SPC_ACK_HV_EL        0xc30   /* Store8 ack HV irq to even line */
+/* XXX more... */
+
+/* NSR fields for the various QW ack types */
+#define TM_QW0_NSR_EB           PPC_BIT8(0)
+#define TM_QW1_NSR_EO           PPC_BIT8(0)
+#define TM_QW3_NSR_HE           PPC_BITMASK8(0, 1)
+#define  TM_QW3_NSR_HE_NONE     0
+#define  TM_QW3_NSR_HE_POOL     1
+#define  TM_QW3_NSR_HE_PHYS     2
+#define  TM_QW3_NSR_HE_LSI      3
+#define TM_QW3_NSR_I            PPC_BIT8(2)
+#define TM_QW3_NSR_GRP_LVL      PPC_BIT8(3, 7)
+
 /* IVE/EAS
  *
  * One per interrupt source. Targets that interrupt to a given EQ
@@ -30,4 +112,6 @@ typedef struct XiveIVE {
 #define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
 } XiveIVE;
 
+#define XIVE_PRIORITY_MAX  7
+
 #endif /* _INTC_XIVE_INTERNAL_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#45 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

Quoted 18 lines
Quoted 18 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

yes. In patch 9, when we introduce the nvt notify routine.

Quoted 22 lines
Quoted 22 lines
hw/intc/xive.c +22 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
Quoted 8 lines

hw/intc/xive.c line 62

Quoted 4 lines
hw/intc/xive.c +4 -0
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Yes, you did and I haven't studied the question yet. For the next version.

Quoted 6 lines
Quoted 6 lines
hw/intc/xive.c +6 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
Quoted 8 lines

hw/intc/xive.c line 74

Quoted 6 lines
hw/intc/xive.c +6 -0
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

ok.

Quoted 32 lines
Quoted 32 lines
hw/intc/xive.c +31 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

Quoted 2 lines

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

For some parts yes, but the special load/store addresses are different for each view, the read-only register also. It seemed easier to duplicate.

I think the problem will become clearer (or worse) with pnv which uses the HV mode.

Quoted 109 lines
Quoted 109 lines
hw/intc/xive.c +109 -0
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return;
+    }
+
+    switch (size) {
+    case 1:
+        if (offset == TM_QW1_OS + TM_CPPR) {
+            xive_nvt_set_cppr(nvt, value & 0xff);
+        }
+        break;
+    case 4:
+    case 8:
+        for (i = 0; i < size; i++) {
+            if (!xive_tm_is_readonly(offset + i)) {
+                nvt->regs[offset + i] = (value >> (8 * (size - i - 1))) & 0xff;
+            }
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+const MemoryRegionOps xive_tm_os_ops = {
+    .read = xive_tm_os_read,
+    .write = xive_tm_os_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+/*
+ * User Thread Interrupt Management Area MMIO
+ */
+
+static uint64_t xive_tm_user_read(void *opaque, hwaddr offset,
+                                        unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%"
+                  HWADDR_PRIx"\n", offset);
+    return -1;
+}
+
+static void xive_tm_user_write(void *opaque, hwaddr offset,
+                                     uint64_t value, unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%"
+                  HWADDR_PRIx"\n", offset);
+}
+
+
+const MemoryRegionOps xive_tm_user_ops = {
+    .read = xive_tm_user_read,
+    .write = xive_tm_user_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+static char *xive_nvt_ring_print(uint8_t *ring)
+{
+    uint32_t w2 = be32_to_cpu(*((uint32_t *) &ring[TM_WORD2]));
+
+    return g_strdup_printf("%02x  %02x   %02x  %02x    %02x   "
+                   "%02x  %02x  %02x   %08x",
+                   ring[TM_NSR], ring[TM_CPPR], ring[TM_IPB], ring[TM_LSMFB],
+                   ring[TM_ACK_CNT], ring[TM_INC], ring[TM_AGE], ring[TM_PIPR],
+                   w2);
+}
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon)
+{
+    int cpu_index = nvt->cs ? nvt->cs->cpu_index : -1;
+    char *s;
+
+    monitor_printf(mon, "CPU[%04x]: QW    NSR CPPR IPB LSMFB ACK# INC AGE PIPR"
+                   " W2\n", cpu_index);
+
+    s = xive_nvt_ring_print(&nvt->regs[TM_QW1_OS]);
+    monitor_printf(mon, "CPU[%04x]: OS    %s\n", cpu_index, s);
+    g_free(s);
+    s = xive_nvt_ring_print(&nvt->regs[TM_QW0_USER]);
+    monitor_printf(mon, "CPU[%04x]: USER  %s\n", cpu_index, s);
+    g_free(s);
+}
+
+static void xive_nvt_reset(void *dev)
+{
+    XiveNVT *nvt = XIVE_NVT(dev);
+
+    memset(nvt->regs, 0, sizeof(nvt->regs));
+}
+
+static void xive_nvt_realize(DeviceState *dev, Error **errp)
+{
+    XiveNVT *nvt = XIVE_NVT(dev);
Quoted 8 lines

hw/intc/xive.c line 225

Quoted 6 lines
hw/intc/xive.c +6 -0
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    Object *obj;
+    Error *err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err);

Please get rid of the remaining "ICP" naming in the xive code.

ok. I will kill the define.

Quoted 16 lines
Quoted 16 lines
hw/intc/xive.c +16 -0
+    if (!obj) {
+        error_propagate(errp, err);
+        error_prepend(errp, "required link '" ICP_PROP_CPU "' not found: ");
+        return;
+    }
+
+    cpu = POWERPC_CPU(obj);
+    nvt->cs = CPU(obj);
+
+    env = &cpu->env;
+    switch (PPC_INPUT(env)) {
+    case PPC_FLAGS_INPUT_POWER7:
+        nvt->output = env->irq_inputs[POWER7_INPUT_INT];
+        break;
+
+    default:
Quoted 8 lines

hw/intc/xive.c line 247

Quoted 6 lines
hw/intc/xive.c +6 -0
+        error_setg(errp, "XIVE interrupt controller does not support "
+                   "this CPU bus model");
+        return;
+    }
+
+    qemu_register_reset(xive_nvt_reset, dev);

If this is a sysbus device, which I think it is,

It is not. The TIMA MMIO region is in the sPAPRXive model but that might change if we use cpu->as. I agree it would look better to have a memory region per cpu.

Quoted 4 lines

you shouldn't need to
explicitly register a reset handler. Instead you can set a device reset handler which will be called with the reset.

Quoted 15 lines

hw/intc/xive.c line 259

Quoted 12 lines
hw/intc/xive.c +12 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

ok. I liked the idea but I agree it's overkill to have an init routine just for this. I will find something.

Quoted 229 lines
Quoted 228 lines
hw/intc/xive.c +37 -0
+}
+
+static const VMStateDescription vmstate_xive_nvt = {
+    .name = TYPE_XIVE_NVT,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_BUFFER(regs, XiveNVT),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void xive_nvt_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_nvt_realize;
+    dc->unrealize = xive_nvt_unrealize;
+    dc->desc = "XIVE Interrupt Presenter";
+    dc->vmsd = &vmstate_xive_nvt;
+}
+
+static const TypeInfo xive_nvt_info = {
+    .name          = TYPE_XIVE_NVT,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(XiveNVT),
+    .instance_init = xive_nvt_init,
+    .class_init    = xive_nvt_class_init,
+};
 
 /*
  * XIVE Fabric
@@ -27,6 +298,13 @@ XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
     return xfc->get_ive(xf, lisn);
 }
 
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_nvt(xf, server);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
@@ -418,6 +696,7 @@ static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
     type_register_static(&xive_fabric_info);
+    type_register_static(&xive_nvt_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/spapr_xive.h +5 -0
index 4538c622b60a..25d78eec884d 100644
@@ -25,6 +25,11 @@ typedef struct sPAPRXive {
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
+
+    /* TIMA memory regions */
+    hwaddr       tm_base;
+    MemoryRegion tm_mmio_user;
+    MemoryRegion tm_mmio_os;
 } sPAPRXive;
 
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
include/hw/ppc/xive.h +31 -0
index 57295715a4a5..1a2da610d91c 100644
@@ -20,6 +20,7 @@ typedef struct XiveFabric XiveFabric;
  */
 
 #define XIVE_VC_BASE   0x0006010000000000ull
+#define XIVE_TM_BASE   0x0006030203180000ull
 
 /*
  * XIVE Interrupt Source
@@ -155,6 +156,34 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
 }
 
 /*
+ * XIVE Interrupt Presenter
+ */
+
+#define TYPE_XIVE_NVT "xive-nvt"
+#define XIVE_NVT(obj) OBJECT_CHECK(XiveNVT, (obj), TYPE_XIVE_NVT)
+
+#define TM_RING_COUNT           4
+#define TM_RING_SIZE            0x10
+
+typedef struct XiveNVT {
+    DeviceState parent_obj;
+
+    CPUState  *cs;
+    qemu_irq  output;
+
+    /* Thread interrupt Management (TM) registers */
+    uint8_t   regs[TM_RING_COUNT * TM_RING_SIZE];
+
+    /* Shortcuts to rings */
+    uint8_t   *ring_os;
+} XiveNVT;
+
+extern const MemoryRegionOps xive_tm_user_ops;
+extern const MemoryRegionOps xive_tm_os_ops;
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon);
+
+/*
  * XIVE Fabric
  */
 
@@ -175,8 +204,10 @@ typedef struct XiveFabricClass {
     void (*notify)(XiveFabric *xf, uint32_t lisn);
 
     XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
+    XiveNVT *(*get_nvt)(XiveFabric *xf, uint32_t server);
 } XiveFabricClass;
 
 XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server);
 
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h +84 -0
index 5903f29eb789..f2e2a1ac8f6e 100644
@@ -10,6 +10,88 @@
 #ifndef _PPC_XIVE_REGS_H
 #define _PPC_XIVE_REGS_H
 
+#define TM_SHIFT                16
+
+/* TM register offsets */
+#define TM_QW0_USER             0x000 /* All rings */
+#define TM_QW1_OS               0x010 /* Ring 0..2 */
+#define TM_QW2_HV_POOL          0x020 /* Ring 0..1 */
+#define TM_QW3_HV_PHYS          0x030 /* Ring 0..1 */
+
+/* Byte offsets inside a QW             QW0 QW1 QW2 QW3 */
+#define TM_NSR                  0x0  /*  +   +   -   +  */
+#define TM_CPPR                 0x1  /*  -   +   -   +  */
+#define TM_IPB                  0x2  /*  -   +   +   +  */
+#define TM_LSMFB                0x3  /*  -   +   +   +  */
+#define TM_ACK_CNT              0x4  /*  -   +   -   -  */
+#define TM_INC                  0x5  /*  -   +   -   +  */
+#define TM_AGE                  0x6  /*  -   +   -   +  */
+#define TM_PIPR                 0x7  /*  -   +   -   +  */
+
+#define TM_WORD0                0x0
+#define TM_WORD1                0x4
+
+/*
+ * QW word 2 contains the valid bit at the top and other fields
+ * depending on the QW.
+ */
+#define TM_WORD2                0x8
+#define   TM_QW0W2_VU           PPC_BIT32(0)
+#define   TM_QW0W2_LOGIC_SERV   PPC_BITMASK32(1, 31) /* XX 2,31 ? */
+#define   TM_QW1W2_VO           PPC_BIT32(0)
+#define   TM_QW1W2_OS_CAM       PPC_BITMASK32(8, 31)
+#define   TM_QW2W2_VP           PPC_BIT32(0)
+#define   TM_QW2W2_POOL_CAM     PPC_BITMASK32(8, 31)
+#define   TM_QW3W2_VT           PPC_BIT32(0)
+#define   TM_QW3W2_LP           PPC_BIT32(6)
+#define   TM_QW3W2_LE           PPC_BIT32(7)
+#define   TM_QW3W2_T            PPC_BIT32(31)
+
+/*
+ * In addition to normal loads to "peek" and writes (only when invalid)
+ * using 4 and 8 bytes accesses, the above registers support these
+ * "special" byte operations:
+ *
+ *   - Byte load from QW0[NSR] - User level NSR (EBB)
+ *   - Byte store to QW0[NSR] - User level NSR (EBB)
+ *   - Byte load/store to QW1[CPPR] and QW3[CPPR] - CPPR access
+ *   - Byte load from QW3[TM_WORD2] - Read VT||00000||LP||LE on thrd 0
+ *                                    otherwise VT||0000000
+ *   - Byte store to QW3[TM_WORD2] - Set VT bit (and LP/LE if present)
+ *
+ * Then we have all these "special" CI ops at these offset that trigger
+ * all sorts of side effects:
+ */
+#define TM_SPC_ACK_EBB          0x800   /* Load8 ack EBB to reg*/
+#define TM_SPC_ACK_OS_REG       0x810   /* Load16 ack OS irq to reg */
+#define TM_SPC_PUSH_USR_CTX     0x808   /* Store32 Push/Validate user context */
+#define TM_SPC_PULL_USR_CTX     0x808   /* Load32 Pull/Invalidate user
+                                         * context */
+#define TM_SPC_SET_OS_PENDING   0x812   /* Store8 Set OS irq pending bit */
+#define TM_SPC_PULL_OS_CTX      0x818   /* Load32/Load64 Pull/Invalidate OS
+                                         * context to reg */
+#define TM_SPC_PULL_POOL_CTX    0x828   /* Load32/Load64 Pull/Invalidate Pool
+                                         * context to reg*/
+#define TM_SPC_ACK_HV_REG       0x830   /* Load16 ack HV irq to reg */
+#define TM_SPC_PULL_USR_CTX_OL  0xc08   /* Store8 Pull/Inval usr ctx to odd
+                                         * line */
+#define TM_SPC_ACK_OS_EL        0xc10   /* Store8 ack OS irq to even line */
+#define TM_SPC_ACK_HV_POOL_EL   0xc20   /* Store8 ack HV evt pool to even
+                                         * line */
+#define TM_SPC_ACK_HV_EL        0xc30   /* Store8 ack HV irq to even line */
+/* XXX more... */
+
+/* NSR fields for the various QW ack types */
+#define TM_QW0_NSR_EB           PPC_BIT8(0)
+#define TM_QW1_NSR_EO           PPC_BIT8(0)
+#define TM_QW3_NSR_HE           PPC_BITMASK8(0, 1)
+#define  TM_QW3_NSR_HE_NONE     0
+#define  TM_QW3_NSR_HE_POOL     1
+#define  TM_QW3_NSR_HE_PHYS     2
+#define  TM_QW3_NSR_HE_LSI      3
+#define TM_QW3_NSR_I            PPC_BIT8(2)
+#define TM_QW3_NSR_GRP_LVL      PPC_BIT8(3, 7)
+
 /* IVE/EAS
  *
  * One per interrupt source. Targets that interrupt to a given EQ
@@ -30,4 +112,6 @@ typedef struct XiveIVE {
 #define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
 } XiveIVE;
 
+#define XIVE_PRIORITY_MAX  7
+
 #endif /* _INTC_XIVE_INTERNAL_H */
#46 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 245 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

Quoted 18 lines
Quoted 18 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

yes. In patch 9, when we introduce the nvt notify routine.

Quoted 22 lines
Quoted 22 lines
hw/intc/xive.c +22 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
Quoted 8 lines

hw/intc/xive.c line 62

Quoted 4 lines
hw/intc/xive.c +4 -0
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Yes, you did and I haven't studied the question yet. For the next version.

Quoted 6 lines
Quoted 6 lines
hw/intc/xive.c +6 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
Quoted 8 lines

hw/intc/xive.c line 74

Quoted 6 lines
hw/intc/xive.c +6 -0
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

ok.

Quoted 32 lines
Quoted 32 lines
hw/intc/xive.c +31 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

Quoted 4 lines

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

So, I gave a deeper look at the specs and I understood a little more details of the concepts behind. You need to do frequent round-trips to this document ...

These registers are accessible through four aligned pages, each exposing a different view of the registers. First page (page address ending in 0b00) gives access to the entire context and is reserved for the ring 0 security monitor. The second (page address ending in 0b01) is for the hypervisor, ring 1. The third (page address ending in 0b10) is for the operating system, ring 2. The fourth (page address ending in 0b11) is for user level, ring 3.

The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings, 2 and 3. The others are for hypervisor levels.

I will try to come with a better implementation of the model and make sure the ring numbers are respected. I am not sure we should have only one memory region or four distinct ones with their own ops. There are some differences in the load/store of each view.

C.

Quoted 412 lines
Quoted 2 lines

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

For some parts yes, but the special load/store addresses are different for each view, the read-only register also. It seemed easier to duplicate.

I think the problem will become clearer (or worse) with pnv which uses the HV mode.

Quoted 109 lines
Quoted 109 lines
hw/intc/xive.c +109 -0
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return;
+    }
+
+    switch (size) {
+    case 1:
+        if (offset == TM_QW1_OS + TM_CPPR) {
+            xive_nvt_set_cppr(nvt, value & 0xff);
+        }
+        break;
+    case 4:
+    case 8:
+        for (i = 0; i < size; i++) {
+            if (!xive_tm_is_readonly(offset + i)) {
+                nvt->regs[offset + i] = (value >> (8 * (size - i - 1))) & 0xff;
+            }
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+const MemoryRegionOps xive_tm_os_ops = {
+    .read = xive_tm_os_read,
+    .write = xive_tm_os_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+/*
+ * User Thread Interrupt Management Area MMIO
+ */
+
+static uint64_t xive_tm_user_read(void *opaque, hwaddr offset,
+                                        unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%"
+                  HWADDR_PRIx"\n", offset);
+    return -1;
+}
+
+static void xive_tm_user_write(void *opaque, hwaddr offset,
+                                     uint64_t value, unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "XIVE: invalid access to User TIMA @%"
+                  HWADDR_PRIx"\n", offset);
+}
+
+
+const MemoryRegionOps xive_tm_user_ops = {
+    .read = xive_tm_user_read,
+    .write = xive_tm_user_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+static char *xive_nvt_ring_print(uint8_t *ring)
+{
+    uint32_t w2 = be32_to_cpu(*((uint32_t *) &ring[TM_WORD2]));
+
+    return g_strdup_printf("%02x  %02x   %02x  %02x    %02x   "
+                   "%02x  %02x  %02x   %08x",
+                   ring[TM_NSR], ring[TM_CPPR], ring[TM_IPB], ring[TM_LSMFB],
+                   ring[TM_ACK_CNT], ring[TM_INC], ring[TM_AGE], ring[TM_PIPR],
+                   w2);
+}
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon)
+{
+    int cpu_index = nvt->cs ? nvt->cs->cpu_index : -1;
+    char *s;
+
+    monitor_printf(mon, "CPU[%04x]: QW    NSR CPPR IPB LSMFB ACK# INC AGE PIPR"
+                   " W2\n", cpu_index);
+
+    s = xive_nvt_ring_print(&nvt->regs[TM_QW1_OS]);
+    monitor_printf(mon, "CPU[%04x]: OS    %s\n", cpu_index, s);
+    g_free(s);
+    s = xive_nvt_ring_print(&nvt->regs[TM_QW0_USER]);
+    monitor_printf(mon, "CPU[%04x]: USER  %s\n", cpu_index, s);
+    g_free(s);
+}
+
+static void xive_nvt_reset(void *dev)
+{
+    XiveNVT *nvt = XIVE_NVT(dev);
+
+    memset(nvt->regs, 0, sizeof(nvt->regs));
+}
+
+static void xive_nvt_realize(DeviceState *dev, Error **errp)
+{
+    XiveNVT *nvt = XIVE_NVT(dev);
Quoted 8 lines

hw/intc/xive.c line 225

Quoted 6 lines
hw/intc/xive.c +6 -0
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    Object *obj;
+    Error *err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err);

Please get rid of the remaining "ICP" naming in the xive code.

ok. I will kill the define.

Quoted 16 lines
Quoted 16 lines
hw/intc/xive.c +16 -0
+    if (!obj) {
+        error_propagate(errp, err);
+        error_prepend(errp, "required link '" ICP_PROP_CPU "' not found: ");
+        return;
+    }
+
+    cpu = POWERPC_CPU(obj);
+    nvt->cs = CPU(obj);
+
+    env = &cpu->env;
+    switch (PPC_INPUT(env)) {
+    case PPC_FLAGS_INPUT_POWER7:
+        nvt->output = env->irq_inputs[POWER7_INPUT_INT];
+        break;
+
+    default:
Quoted 8 lines

hw/intc/xive.c line 247

Quoted 6 lines
hw/intc/xive.c +6 -0
+        error_setg(errp, "XIVE interrupt controller does not support "
+                   "this CPU bus model");
+        return;
+    }
+
+    qemu_register_reset(xive_nvt_reset, dev);

If this is a sysbus device, which I think it is,

It is not. The TIMA MMIO region is in the sPAPRXive model but that might change if we use cpu->as. I agree it would look better to have a memory region per cpu.

Quoted 4 lines

you shouldn't need to
explicitly register a reset handler. Instead you can set a device reset handler which will be called with the reset.

Quoted 15 lines

hw/intc/xive.c line 259

Quoted 12 lines
hw/intc/xive.c +12 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

ok. I liked the idea but I agree it's overkill to have an init routine just for this. I will find something.

Quoted 229 lines
Quoted 228 lines
hw/intc/xive.c +37 -0
+}
+
+static const VMStateDescription vmstate_xive_nvt = {
+    .name = TYPE_XIVE_NVT,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_BUFFER(regs, XiveNVT),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void xive_nvt_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xive_nvt_realize;
+    dc->unrealize = xive_nvt_unrealize;
+    dc->desc = "XIVE Interrupt Presenter";
+    dc->vmsd = &vmstate_xive_nvt;
+}
+
+static const TypeInfo xive_nvt_info = {
+    .name          = TYPE_XIVE_NVT,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(XiveNVT),
+    .instance_init = xive_nvt_init,
+    .class_init    = xive_nvt_class_init,
+};
 
 /*
  * XIVE Fabric
@@ -27,6 +298,13 @@ XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn)
     return xfc->get_ive(xf, lisn);
 }
 
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xf);
+
+    return xfc->get_nvt(xf, server);
+}
+
 static void xive_fabric_route(XiveFabric *xf, int lisn)
 {
 
@@ -418,6 +696,7 @@ static void xive_register_types(void)
 {
     type_register_static(&xive_source_info);
     type_register_static(&xive_fabric_info);
+    type_register_static(&xive_nvt_info);
 }
 
 type_init(xive_register_types)
include/hw/ppc/spapr_xive.h +5 -0
index 4538c622b60a..25d78eec884d 100644
@@ -25,6 +25,11 @@ typedef struct sPAPRXive {
     /* Routing table */
     XiveIVE      *ivt;
     uint32_t     nr_irqs;
+
+    /* TIMA memory regions */
+    hwaddr       tm_base;
+    MemoryRegion tm_mmio_user;
+    MemoryRegion tm_mmio_os;
 } sPAPRXive;
 
 bool spapr_xive_irq_enable(sPAPRXive *xive, uint32_t lisn, bool lsi);
include/hw/ppc/xive.h +31 -0
index 57295715a4a5..1a2da610d91c 100644
@@ -20,6 +20,7 @@ typedef struct XiveFabric XiveFabric;
  */
 
 #define XIVE_VC_BASE   0x0006010000000000ull
+#define XIVE_TM_BASE   0x0006030203180000ull
 
 /*
  * XIVE Interrupt Source
@@ -155,6 +156,34 @@ static inline void xive_source_irq_set(XiveSource *xsrc, uint32_t srcno,
 }
 
 /*
+ * XIVE Interrupt Presenter
+ */
+
+#define TYPE_XIVE_NVT "xive-nvt"
+#define XIVE_NVT(obj) OBJECT_CHECK(XiveNVT, (obj), TYPE_XIVE_NVT)
+
+#define TM_RING_COUNT           4
+#define TM_RING_SIZE            0x10
+
+typedef struct XiveNVT {
+    DeviceState parent_obj;
+
+    CPUState  *cs;
+    qemu_irq  output;
+
+    /* Thread interrupt Management (TM) registers */
+    uint8_t   regs[TM_RING_COUNT * TM_RING_SIZE];
+
+    /* Shortcuts to rings */
+    uint8_t   *ring_os;
+} XiveNVT;
+
+extern const MemoryRegionOps xive_tm_user_ops;
+extern const MemoryRegionOps xive_tm_os_ops;
+
+void xive_nvt_pic_print_info(XiveNVT *nvt, Monitor *mon);
+
+/*
  * XIVE Fabric
  */
 
@@ -175,8 +204,10 @@ typedef struct XiveFabricClass {
     void (*notify)(XiveFabric *xf, uint32_t lisn);
 
     XiveIVE *(*get_ive)(XiveFabric *xf, uint32_t lisn);
+    XiveNVT *(*get_nvt)(XiveFabric *xf, uint32_t server);
 } XiveFabricClass;
 
 XiveIVE *xive_fabric_get_ive(XiveFabric *xf, uint32_t lisn);
+XiveNVT *xive_fabric_get_nvt(XiveFabric *xf, uint32_t server);
 
 #endif /* PPC_XIVE_H */
include/hw/ppc/xive_regs.h +84 -0
index 5903f29eb789..f2e2a1ac8f6e 100644
@@ -10,6 +10,88 @@
 #ifndef _PPC_XIVE_REGS_H
 #define _PPC_XIVE_REGS_H
 
+#define TM_SHIFT                16
+
+/* TM register offsets */
+#define TM_QW0_USER             0x000 /* All rings */
+#define TM_QW1_OS               0x010 /* Ring 0..2 */
+#define TM_QW2_HV_POOL          0x020 /* Ring 0..1 */
+#define TM_QW3_HV_PHYS          0x030 /* Ring 0..1 */
+
+/* Byte offsets inside a QW             QW0 QW1 QW2 QW3 */
+#define TM_NSR                  0x0  /*  +   +   -   +  */
+#define TM_CPPR                 0x1  /*  -   +   -   +  */
+#define TM_IPB                  0x2  /*  -   +   +   +  */
+#define TM_LSMFB                0x3  /*  -   +   +   +  */
+#define TM_ACK_CNT              0x4  /*  -   +   -   -  */
+#define TM_INC                  0x5  /*  -   +   -   +  */
+#define TM_AGE                  0x6  /*  -   +   -   +  */
+#define TM_PIPR                 0x7  /*  -   +   -   +  */
+
+#define TM_WORD0                0x0
+#define TM_WORD1                0x4
+
+/*
+ * QW word 2 contains the valid bit at the top and other fields
+ * depending on the QW.
+ */
+#define TM_WORD2                0x8
+#define   TM_QW0W2_VU           PPC_BIT32(0)
+#define   TM_QW0W2_LOGIC_SERV   PPC_BITMASK32(1, 31) /* XX 2,31 ? */
+#define   TM_QW1W2_VO           PPC_BIT32(0)
+#define   TM_QW1W2_OS_CAM       PPC_BITMASK32(8, 31)
+#define   TM_QW2W2_VP           PPC_BIT32(0)
+#define   TM_QW2W2_POOL_CAM     PPC_BITMASK32(8, 31)
+#define   TM_QW3W2_VT           PPC_BIT32(0)
+#define   TM_QW3W2_LP           PPC_BIT32(6)
+#define   TM_QW3W2_LE           PPC_BIT32(7)
+#define   TM_QW3W2_T            PPC_BIT32(31)
+
+/*
+ * In addition to normal loads to "peek" and writes (only when invalid)
+ * using 4 and 8 bytes accesses, the above registers support these
+ * "special" byte operations:
+ *
+ *   - Byte load from QW0[NSR] - User level NSR (EBB)
+ *   - Byte store to QW0[NSR] - User level NSR (EBB)
+ *   - Byte load/store to QW1[CPPR] and QW3[CPPR] - CPPR access
+ *   - Byte load from QW3[TM_WORD2] - Read VT||00000||LP||LE on thrd 0
+ *                                    otherwise VT||0000000
+ *   - Byte store to QW3[TM_WORD2] - Set VT bit (and LP/LE if present)
+ *
+ * Then we have all these "special" CI ops at these offset that trigger
+ * all sorts of side effects:
+ */
+#define TM_SPC_ACK_EBB          0x800   /* Load8 ack EBB to reg*/
+#define TM_SPC_ACK_OS_REG       0x810   /* Load16 ack OS irq to reg */
+#define TM_SPC_PUSH_USR_CTX     0x808   /* Store32 Push/Validate user context */
+#define TM_SPC_PULL_USR_CTX     0x808   /* Load32 Pull/Invalidate user
+                                         * context */
+#define TM_SPC_SET_OS_PENDING   0x812   /* Store8 Set OS irq pending bit */
+#define TM_SPC_PULL_OS_CTX      0x818   /* Load32/Load64 Pull/Invalidate OS
+                                         * context to reg */
+#define TM_SPC_PULL_POOL_CTX    0x828   /* Load32/Load64 Pull/Invalidate Pool
+                                         * context to reg*/
+#define TM_SPC_ACK_HV_REG       0x830   /* Load16 ack HV irq to reg */
+#define TM_SPC_PULL_USR_CTX_OL  0xc08   /* Store8 Pull/Inval usr ctx to odd
+                                         * line */
+#define TM_SPC_ACK_OS_EL        0xc10   /* Store8 ack OS irq to even line */
+#define TM_SPC_ACK_HV_POOL_EL   0xc20   /* Store8 ack HV evt pool to even
+                                         * line */
+#define TM_SPC_ACK_HV_EL        0xc30   /* Store8 ack HV irq to even line */
+/* XXX more... */
+
+/* NSR fields for the various QW ack types */
+#define TM_QW0_NSR_EB           PPC_BIT8(0)
+#define TM_QW1_NSR_EO           PPC_BIT8(0)
+#define TM_QW3_NSR_HE           PPC_BITMASK8(0, 1)
+#define  TM_QW3_NSR_HE_NONE     0
+#define  TM_QW3_NSR_HE_POOL     1
+#define  TM_QW3_NSR_HE_PHYS     2
+#define  TM_QW3_NSR_HE_LSI      3
+#define TM_QW3_NSR_I            PPC_BIT8(2)
+#define TM_QW3_NSR_GRP_LVL      PPC_BIT8(3, 7)
+
 /* IVE/EAS
  *
  * One per interrupt source. Targets that interrupt to a given EQ
@@ -30,4 +112,6 @@ typedef struct XiveIVE {
 #define IVE_EQ_DATA     PPC_BITMASK(33, 63)      /* Data written to the EQ */
 } XiveIVE;
 
+#define XIVE_PRIORITY_MAX  7
+
 #endif /* _INTC_XIVE_INTERNAL_H */
#47 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 3 lines · Cédric Le Goater
Quoted 2 lines · Cédric Le Goater
Quoted 1 line · David Gibson

On Thu, Apr 19, 2018 at 02:43:02PM +0200, Cédric Le Goater wrote:

[snip]

Quoted 34 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
hw/intc/xive.c +8 -0
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

Quoted 4 lines

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

So, I gave a deeper look at the specs and I understood a little more details of the concepts behind. You need to do frequent round-trips to this document ...

These registers are accessible through four aligned pages, each exposing a different view of the registers. First page (page address ending in 0b00) gives access to the entire context and is reserved for the ring 0 security monitor. The second (page address ending in 0b01) is for the hypervisor, ring 1. The third (page address ending in 0b10) is for the operating system, ring 2. The fourth (page address ending in 0b11) is for user level, ring 3.

Quoted 3 lines

The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings, 2 and 3. The others are for hypervisor levels.

Ok, that much is what I thought. What I'm less clear on is what each page looks like compared to the others. Previously I thought each one had the same registers, just manipulating the corresponding ring. Are you saying instead that each ring's page basically has a subset of the registers in the next most privileged page?

Quoted 4 lines

I will try to come with a better implementation of the model and make sure the ring numbers are respected. I am not sure we should have only one memory region or four distinct ones with their own ops. There are some differences in the load/store of each view.

Right. I'm not clear at this point if that's for good reasons, or just because IBM's hardware designers don't seem to have gotten the hang of Don't Repeat Yourself.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#48 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 39 lines · David Gibson
Quoted 3 lines · Cédric Le Goater
Quoted 2 lines · Cédric Le Goater
Quoted 1 line · David Gibson

On Thu, Apr 19, 2018 at 02:43:02PM +0200, Cédric Le Goater wrote:

[snip]

Quoted 34 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
hw/intc/xive.c +8 -0
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

Quoted 4 lines

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

So, I gave a deeper look at the specs and I understood a little more details of the concepts behind. You need to do frequent round-trips to this document ...

These registers are accessible through four aligned pages, each exposing a different view of the registers. First page (page address ending in 0b00) gives access to the entire context and is reserved for the ring 0 security monitor. The second (page address ending in 0b01) is for the hypervisor, ring 1. The third (page address ending in 0b10) is for the operating system, ring 2. The fourth (page address ending in 0b11) is for user level, ring 3.

Quoted 7 lines
Quoted 3 lines

The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings, 2 and 3. The others are for hypervisor levels.

Ok, that much is what I thought. What I'm less clear on is what each page looks like compared to the others. Previously I thought each one had the same registers,

yes.

Quoted 1 line

just manipulating the corresponding ring.

no.

Quoted 2 lines

Are you saying instead that each ring's page basically has a subset of the registers in the next most privileged page?

That's the idea.

The registers are defined as follow :

	QW-0 User      
	QW-1 O/S      
	QW-2 Pool   
	QW-3 Physical 

and the pages :

- 0006030203180000 security monitor
can access all registers

- 0006030203190000 hv
can access all registers minus the secure regs

- 00060302031a0000 os
can access some of the OS (QW1) and User (QW0) registers

- 00060302031b0000 user
can access NSR reg of User (QW0) registers

On sPAPR, we can remap the os/user pages to some other base address but we should keep the same page offset.

Quoted 9 lines
Quoted 4 lines

I will try to come with a better implementation of the model and make sure the ring numbers are respected. I am not sure we should have only one memory region or four distinct ones with their own ops. There are some differences in the load/store of each view.

Right. I'm not clear at this point if that's for good reasons, or just because IBM's hardware designers don't seem to have gotten the hang of Don't Repeat Yourself.

#49 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 50 lines · Cédric Le Goater
Quoted 39 lines · David Gibson
Quoted 3 lines · Cédric Le Goater
Quoted 2 lines · Cédric Le Goater
Quoted 1 line · David Gibson

On Thu, Apr 19, 2018 at 02:43:02PM +0200, Cédric Le Goater wrote:

[snip]

Quoted 34 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
hw/intc/xive.c +8 -0
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

Quoted 4 lines

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

So, I gave a deeper look at the specs and I understood a little more details of the concepts behind. You need to do frequent round-trips to this document ...

These registers are accessible through four aligned pages, each exposing a different view of the registers. First page (page address ending in 0b00) gives access to the entire context and is reserved for the ring 0 security monitor. The second (page address ending in 0b01) is for the hypervisor, ring 1. The third (page address ending in 0b10) is for the operating system, ring 2. The fourth (page address ending in 0b11) is for user level, ring 3.

Quoted 7 lines
Quoted 3 lines

The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings, 2 and 3. The others are for hypervisor levels.

Ok, that much is what I thought. What I'm less clear on is what each page looks like compared to the others. Previously I thought each one had the same registers,

yes.

Quoted 8 lines
Quoted 1 line

just manipulating the corresponding ring.

no.

Quoted 2 lines

Are you saying instead that each ring's page basically has a subset of the registers in the next most privileged page?

That's the idea.

Ah, ok.

Quoted 12 lines

The registers are defined as follow :

	QW-0 User      
	QW-1 O/S      
	QW-2 Pool   
	QW-3 Physical 

and the pages :

- 0006030203180000 security monitor
can access all registers

Quoted 8 lines

- 0006030203190000 hv
can access all registers minus the secure regs

- 00060302031a0000 os
can access some of the OS (QW1) and User (QW0) registers

- 00060302031b0000 user
can access NSR reg of User (QW0) registers

I can see two reasonable ways of doing this:

A)

Have a single set of read/write functions. These implement all the registers but take a "privilege level" parameter which controls which will actually work. Those could then be wired up in one of two ways:

A1) Single memory region. The accessor derives the priv level from the relevant address bits, before masking it down to a single register page. Then, as above

A2) Multiple memory regions with the same accessor functions but different opaque pointer. The accessor gets the priv level from its opaque pointer, then the address is just within a single ring's page.

B)

Separate memory regions with separate accessors. The ring-0 accessor implements the ring-0 registers, then calls the ring-1 accessor function for everything else. ring-1 calls ring-2 and so forth.

Quoted 2 lines

On sPAPR, we can remap the os/user pages to some other base address but we should keep the same page offset.

Sure.

Quoted 12 lines
Quoted 11 lines
Quoted 6 lines

I will try to come with a better implementation of the model and make sure the ring numbers are respected. I am not sure we should have only one memory region or four distinct ones with their own ops. There are some differences in the load/store of each view.

Right. I'm not clear at this point if that's for good reasons, or just because IBM's hardware designers don't seem to have gotten the hang of Don't Repeat Yourself.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#50 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 87 lines · David Gibson
Quoted 50 lines · Cédric Le Goater
Quoted 39 lines · David Gibson
Quoted 3 lines · Cédric Le Goater
Quoted 2 lines · Cédric Le Goater
Quoted 1 line · David Gibson

On Thu, Apr 19, 2018 at 02:43:02PM +0200, Cédric Le Goater wrote:

[snip]

Quoted 34 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
(quoted diff) +8 -0
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

Quoted 4 lines

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

So, I gave a deeper look at the specs and I understood a little more details of the concepts behind. You need to do frequent round-trips to this document ...

These registers are accessible through four aligned pages, each exposing a different view of the registers. First page (page address ending in 0b00) gives access to the entire context and is reserved for the ring 0 security monitor. The second (page address ending in 0b01) is for the hypervisor, ring 1. The third (page address ending in 0b10) is for the operating system, ring 2. The fourth (page address ending in 0b11) is for user level, ring 3.

Quoted 7 lines
Quoted 3 lines

The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings, 2 and 3. The others are for hypervisor levels.

Ok, that much is what I thought. What I'm less clear on is what each page looks like compared to the others. Previously I thought each one had the same registers,

yes.

Quoted 8 lines
Quoted 1 line

just manipulating the corresponding ring.

no.

Quoted 2 lines

Are you saying instead that each ring's page basically has a subset of the registers in the next most privileged page?

That's the idea.

Ah, ok.

Quoted 12 lines

The registers are defined as follow :

	QW-0 User      
	QW-1 O/S      
	QW-2 Pool   
	QW-3 Physical 

and the pages :

- 0006030203180000 security monitor
can access all registers

Quoted 8 lines

- 0006030203190000 hv
can access all registers minus the secure regs

- 00060302031a0000 os
can access some of the OS (QW1) and User (QW0) registers

- 00060302031b0000 user
can access NSR reg of User (QW0) registers

I can see two reasonable ways of doing this:

A)

Quoted 7 lines

Have a single set of read/write functions. These implement all the registers but take a "privilege level" parameter which controls which will actually work. Those could then be wired up in one of two ways:

A1) Single memory region. The accessor derives the priv level from the relevant address bits, before masking it down to a single register page. Then, as above

Yes. That's the goal behind the page ordering :

page address ending in 0b00 : ring 0, security monitor page address ending in 0b01 : ring 1, hypervisor page address ending in 0b10 : ring 2, operating system page address ending in 0b11 : ring 3, user level

I don't why the registers are ordered the other way around though.

That's would be the direction to take for the emulated mode, I think. It covers well the PowerNV (4 pages) and the sPAPR case (2 pages), in each case, the machine IC controller decides how much pages to map. The memory region ops do the rest.

For KVM, we need to populate the VMA with the host TIMA page associated with ring 2 (OS) and then ring 3 (USER).

This option looks better overall. I will see how ugly it gets with the implementation.

C.

Quoted 29 lines

A2) Multiple memory regions with the same accessor functions but different opaque pointer. The accessor gets the priv level from its opaque pointer, then the address is just within a single ring's page.

B)

Separate memory regions with separate accessors. The ring-0 accessor implements the ring-0 registers, then calls the ring-1 accessor function for everything else. ring-1 calls ring-2 and so forth.

Quoted 2 lines

On sPAPR, we can remap the os/user pages to some other base address but we should keep the same page offset.

Sure.

Quoted 12 lines
Quoted 11 lines
Quoted 6 lines

I will try to come with a better implementation of the model and make sure the ring numbers are respected. I am not sure we should have only one memory region or four distinct ones with their own ops. There are some differences in the load/store of each view.

Right. I'm not clear at this point if that's for good reasons, or just because IBM's hardware designers don't seem to have gotten the hang of Don't Repeat Yourself.

#51 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

In reply to Cédric Le Goater
Quoted 64 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 6 lines

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

I agree.

Quoted 80 lines
Quoted 17 lines
Quoted 17 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 1 line

yes. In patch 9, when we introduce the nvt notify routine.

Ok.

Quoted 31 lines
Quoted 22 lines
Quoted 22 lines
hw/intc/xive.c +22 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
Quoted 8 lines

hw/intc/xive.c line 62

Quoted 4 lines
hw/intc/xive.c +4 -0
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Quoted 1 line

Yes, you did and I haven't studied the question yet. For the next version.

So, it's possible that using the cpu->as thing will be more trouble that it's worth. I am a little concerned about using current_cpu though. First, will it work with KVM with kernel_irqchip=off - the cpus are running truly concurrently, but we still need to work out who's poking at the TIMA. Second, are there any cases where we might need to trip this "on behalf of" a specific cpu that's not the current one.

Quoted 63 lines
Quoted 6 lines
Quoted 6 lines
hw/intc/xive.c +6 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
Quoted 8 lines

hw/intc/xive.c line 74

Quoted 6 lines
hw/intc/xive.c +6 -0
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

ok.

Quoted 32 lines
Quoted 32 lines
hw/intc/xive.c +31 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

Quoted 8 lines
Quoted 2 lines

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

For some parts yes, but the special load/store addresses are different for each view, the read-only register also. It seemed easier to duplicate.

I think the problem will become clearer (or worse) with pnv which uses the HV mode.

Oh. I had the impression that each ring had a basically identical set of registers and you just had access to the region for your ring and the ones below. Are you saying instead it's basically a single block of registers with various different privilege levels for each of them?

[snip]

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
hw/intc/xive.c +7 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
Quoted 8 lines

hw/intc/xive.c line 259

Quoted 5 lines
hw/intc/xive.c +5 -0
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

Quoted 2 lines

ok. I liked the idea but I agree it's overkill to have an init routine just for this. I will find something.

That too, but it's also something that looks like an optimization but isn't, which is bad practice. On modern cpus math is cheap (and this is just a trivial offset), memory accesses are expensive. You're essentially caching this offset - raising all the usual invalidation questions for a cache - when caching it is *more* expensive than just computing it every time.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#52 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 189 lines · David Gibson
Quoted 64 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 6 lines

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

I agree.

Quoted 80 lines
Quoted 17 lines
Quoted 17 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 1 line

yes. In patch 9, when we introduce the nvt notify routine.

Ok.

Quoted 31 lines
Quoted 30 lines
Quoted 16 lines
hw/intc/xive.c +16 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }

hw/intc/xive.c line 62

Quoted 10 lines
hw/intc/xive.c +10 -0
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Quoted 4 lines
Quoted 1 line

Yes, you did and I haven't studied the question yet. For the next version.

So, it's possible that using the cpu->as thing will be more trouble that it's worth.

One of the trouble is the number of memory regions to use, one per cpu, and the KVM support. Having a single region is much easier.

Quoted 3 lines

I am a little concerned about using current_cpu though. First, will it work with KVM with kernel_irqchip=off - the cpus are running truly concurrently,

FWIW, I didn't see any issue yet while stressing.

Quoted 1 line

but we still need to work out who's poking at the TIMA.

I understand. The registers are accessed by the current cpu to set the CPPR and to ack an interrupt. But when we route an event, we also access and modify the registers. Do you suggest some locking ? I am not sure how are protected the TIMA region accesses vs. the routing, which is necessarily initiated by an ESB MMIO though.

Quoted 3 lines

Second, are there any cases where we might
need to trip this "on behalf of" a specific cpu that's not the current one.

ah. yes. sort of :) only in powernv, when the xive is reseted (and when dumping the state for debug).

The IC has a way to access indirectly the registers of a HW thread. It, first, sets the PC_TCTXT_INDIR_THRDID register with the PIR of the targeted thread and then loads on the indirect TIMA can be done as if it was the current thread. The indirect TIMA is mapped 4 pages after the IC BAR.

The resulting memory region op is a little ugly and might need some rework :

static uint64_t xive_tm_hv_read(void *opaque, hwaddr offset,
unsigned size)

{
    PowerPCCPU **cpuptr = opaque;
    PowerPCCPU *cpu = *cpuptr ? *cpuptr : POWERPC_CPU(current_cpu);
    ...
Quoted 69 lines
Quoted 69 lines
Quoted 6 lines
Quoted 6 lines
hw/intc/xive.c +6 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
Quoted 8 lines

hw/intc/xive.c line 74

Quoted 6 lines
hw/intc/xive.c +6 -0
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

ok.

Quoted 32 lines
Quoted 32 lines
hw/intc/xive.c +31 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

Quoted 2 lines

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

For some parts yes, but the special load/store addresses are different for each view, the read-only register also. It seemed easier to duplicate.

Quoted 7 lines
Quoted 2 lines

I think the problem will become clearer (or worse) with pnv which uses the HV mode.

Oh. I had the impression that each ring had a basically identical set of registers and you just had access to the region for your ring and the ones below. Are you saying instead it's basically a single block of registers with various different privilege levels for each of them?

yes. I think I answered this question more clearly in a previous email.

Quoted 20 lines

[snip]

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
hw/intc/xive.c +7 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
Quoted 8 lines

hw/intc/xive.c line 259

Quoted 5 lines
hw/intc/xive.c +5 -0
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

Quoted 2 lines

ok. I liked the idea but I agree it's overkill to have an init routine just for this. I will find something.

Quoted 6 lines

That too, but it's also something that looks like an optimization but isn't, which is bad practice. On modern cpus math is cheap (and this is just a trivial offset), memory accesses are expensive. You're essentially caching this offset - raising all the usual invalidation questions for a cache - when caching it is *more* expensive than just computing it every time.

ok. removing this offset was a good opportunity to generalize the routing algorithm and use a 'ring' parameter in all routines. Same for the accept path.

C.

#53 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 190 lines · Cédric Le Goater
Quoted 189 lines · David Gibson
Quoted 64 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 6 lines

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

I agree.

Quoted 80 lines
Quoted 17 lines
Quoted 17 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 1 line

yes. In patch 9, when we introduce the nvt notify routine.

Ok.

Quoted 31 lines
Quoted 30 lines
Quoted 16 lines
hw/intc/xive.c +16 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }

hw/intc/xive.c line 62

Quoted 10 lines
hw/intc/xive.c +10 -0
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Yes, you did and I haven't studied the question yet. For the next version.

So, it's possible that using the cpu->as thing will be more trouble that it's worth.

One of the trouble is the number of memory regions to use, one per cpu,

Well, we're already going to have an NVT object for each cpu, yes? So a memory region per-cpu doesn't seem like a big stretch.

Quoted 1 line

and the KVM support.

And I really don't see how the memory regions impacts KVM.

Quoted 7 lines

Having a single region is much easier.

Quoted 3 lines

I am a little concerned about using current_cpu though. First, will it work with KVM with kernel_irqchip=off - the cpus are running truly concurrently,

FWIW, I didn't see any issue yet while stressing.

Ok.

Quoted 7 lines
Quoted 1 line

but we still need to work out who's poking at the TIMA.

I understand. The registers are accessed by the current cpu to set the CPPR and to ack an interrupt. But when we route an event, we also access and modify the registers. Do you suggest some locking ? I am not sure how are protected the TIMA region accesses vs. the routing, which is necessarily initiated by an ESB MMIO though.

Locking isn't really the issue. I mean, we do need locking, but the BQL should provide that. The issue is what exactly does "current" mean in the context of multiple concurrently running cpus. Does it always mean what we need it to mean in every context we might call this from.

Quoted 137 lines
Quoted 3 lines

Second, are there any cases where we might
need to trip this "on behalf of" a specific cpu that's not the current one.

ah. yes. sort of :) only in powernv, when the xive is reseted (and when dumping the state for debug).

The IC has a way to access indirectly the registers of a HW thread. It, first, sets the PC_TCTXT_INDIR_THRDID register with the PIR of the targeted thread and then loads on the indirect TIMA can be done as if it was the current thread. The indirect TIMA is mapped 4 pages after the IC BAR.

The resulting memory region op is a little ugly and might need some rework :

static uint64_t xive_tm_hv_read(void *opaque, hwaddr offset,
unsigned size)

{
    PowerPCCPU **cpuptr = opaque;
    PowerPCCPU *cpu = *cpuptr ? *cpuptr : POWERPC_CPU(current_cpu);
    ...
Quoted 69 lines
Quoted 69 lines
Quoted 6 lines
Quoted 6 lines
hw/intc/xive.c +6 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
Quoted 8 lines

hw/intc/xive.c line 74

Quoted 6 lines
hw/intc/xive.c +6 -0
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

ok.

Quoted 32 lines
Quoted 32 lines
hw/intc/xive.c +31 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

Quoted 2 lines

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

For some parts yes, but the special load/store addresses are different for each view, the read-only register also. It seemed easier to duplicate.

Quoted 7 lines
Quoted 2 lines

I think the problem will become clearer (or worse) with pnv which uses the HV mode.

Oh. I had the impression that each ring had a basically identical set of registers and you just had access to the region for your ring and the ones below. Are you saying instead it's basically a single block of registers with various different privilege levels for each of them?

yes. I think I answered this question more clearly in a previous email.

Quoted 20 lines

[snip]

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
hw/intc/xive.c +7 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
Quoted 8 lines

hw/intc/xive.c line 259

Quoted 5 lines
hw/intc/xive.c +5 -0
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

Quoted 2 lines

ok. I liked the idea but I agree it's overkill to have an init routine just for this. I will find something.

Quoted 6 lines

That too, but it's also something that looks like an optimization but isn't, which is bad practice. On modern cpus math is cheap (and this is just a trivial offset), memory accesses are expensive. You're essentially caching this offset - raising all the usual invalidation questions for a cache - when caching it is *more* expensive than just computing it every time.

ok. removing this offset was a good opportunity to generalize the routing algorithm and use a 'ring' parameter in all routines. Same for the accept path.

C.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#54 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 196 lines · David Gibson
Quoted 195 lines · Cédric Le Goater
Quoted 193 lines · David Gibson
Quoted 64 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 6 lines

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

I agree.

Quoted 80 lines
Quoted 17 lines
Quoted 17 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 1 line

yes. In patch 9, when we introduce the nvt notify routine.

Ok.

Quoted 31 lines
Quoted 22 lines
Quoted 22 lines
hw/intc/xive.c +22 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
Quoted 8 lines

hw/intc/xive.c line 62

Quoted 4 lines
hw/intc/xive.c +4 -0
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Quoted 1 line

Yes, you did and I haven't studied the question yet. For the next version.

So, it's possible that using the cpu->as thing will be more trouble that it's worth.

Quoted 8 lines
Quoted 1 line

One of the trouble is the number of memory regions to use, one per cpu,

Well, we're already going to have an NVT object for each cpu, yes? So a memory region per-cpu doesn't seem like a big stretch.

Quoted 1 line

and the KVM support.

And I really don't see how the memory regions impacts KVM.

The TIMA is setup when the KVM device is initialized using some specific ioctl to get an fd on a MMIO region from the host. It is then passed to the guest as a 'ram_device', same for the ESBs.

This is not a common region.

Quoted 18 lines
Quoted 7 lines

Having a single region is much easier.

Quoted 3 lines

I am a little concerned about using current_cpu though. First, will it work with KVM with kernel_irqchip=off - the cpus are running truly concurrently,

FWIW, I didn't see any issue yet while stressing.

Ok.

Quoted 7 lines
Quoted 1 line

but we still need to work out who's poking at the TIMA.

I understand. The registers are accessed by the current cpu to set the CPPR and to ack an interrupt. But when we route an event, we also access and modify the registers. Do you suggest some locking ? I am not sure how are protected the TIMA region accesses vs. the routing, which is necessarily initiated by an ESB MMIO though.

Quoted 5 lines

Locking isn't really the issue. I mean, we do need locking, but the BQL should provide that. The issue is what exactly does "current" mean in the context of multiple concurrently running cpus. Does it always mean what we need it to mean in every context we might call this from.

I would say so.

C.

Quoted 138 lines
Quoted 137 lines
Quoted 3 lines

Second, are there any cases where we might
need to trip this "on behalf of" a specific cpu that's not the current one.

ah. yes. sort of :) only in powernv, when the xive is reseted (and when dumping the state for debug).

The IC has a way to access indirectly the registers of a HW thread. It, first, sets the PC_TCTXT_INDIR_THRDID register with the PIR of the targeted thread and then loads on the indirect TIMA can be done as if it was the current thread. The indirect TIMA is mapped 4 pages after the IC BAR.

The resulting memory region op is a little ugly and might need some rework :

static uint64_t xive_tm_hv_read(void *opaque, hwaddr offset,
unsigned size)

{
    PowerPCCPU **cpuptr = opaque;
    PowerPCCPU *cpu = *cpuptr ? *cpuptr : POWERPC_CPU(current_cpu);
    ...
Quoted 69 lines
Quoted 69 lines
Quoted 6 lines
Quoted 6 lines
hw/intc/xive.c +6 -0
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    uint64_t ret = -1;
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
+        return xive_tm_read_special(nvt, offset, size);
Quoted 8 lines

hw/intc/xive.c line 74

Quoted 6 lines
hw/intc/xive.c +6 -0
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid access to non-OS ring @%"
+                      HWADDR_PRIx"\n", offset);
+        return ret;

Just return -1 would be clearer here;

ok.

Quoted 32 lines
Quoted 32 lines
hw/intc/xive.c +31 -0
+    }
+
+    ret = 0;
+    for (i = 0; i < size; i++) {
+        ret |= (uint64_t) nvt->regs[offset + i] << (8 * (size - i - 1));
+    }
+
+    return ret;
+}
+
+static bool xive_tm_is_readonly(uint8_t offset)
+{
+    return offset != TM_QW1_OS + TM_CPPR;
+}
+
+static void xive_tm_write_special(XiveNVT *nvt, hwaddr offset,
+                                        uint64_t value, unsigned size)
+{
+    /* TODO: support TM_SPC_SET_OS_PENDING */
+
+    /* TODO: support TM_SPC_ACK_OS_EL */
+}
+
+static void xive_tm_os_write(void *opaque, hwaddr offset,
+                                   uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    XiveNVT *nvt = XIVE_NVT(cpu->intc);
+    int i;
+
+    if (offset >= TM_SPC_ACK_EBB) {
Quoted 8 lines

hw/intc/xive.c line 110

Quoted 5 lines
hw/intc/xive.c +5 -0
+        xive_tm_write_special(nvt, offset, value, size);
+        return;
+    }
+
+    if (TM_RING(offset) != TM_QW1_OS) {

Why have this if you have separate OS and user regions as you appear to do below?

This is another problem we are trying to solve.

The registers a CPU can access depends on the TIMA view it is using. The OS TIMA view only sees the OS ring registers. The HV view sees all.

Quoted 2 lines

Or to look at it another way, shouldn't it be possible to make the read/write accessors the same for the OS and user rings?

For some parts yes, but the special load/store addresses are different for each view, the read-only register also. It seemed easier to duplicate.

Quoted 7 lines
Quoted 2 lines

I think the problem will become clearer (or worse) with pnv which uses the HV mode.

Oh. I had the impression that each ring had a basically identical set of registers and you just had access to the region for your ring and the ones below. Are you saying instead it's basically a single block of registers with various different privilege levels for each of them?

yes. I think I answered this question more clearly in a previous email.

Quoted 20 lines

[snip]

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
hw/intc/xive.c +7 -0
+}
+
+static void xive_nvt_unrealize(DeviceState *dev, Error **errp)
+{
+    qemu_unregister_reset(xive_nvt_reset, dev);
+}
+
Quoted 8 lines

hw/intc/xive.c line 259

Quoted 5 lines
hw/intc/xive.c +5 -0
+static void xive_nvt_init(Object *obj)
+{
+    XiveNVT *nvt = XIVE_NVT(obj);
+
+    nvt->ring_os = &nvt->regs[TM_QW1_OS];

The ring_os field is basically pointless, being just an offset into a structure you already have. A macro or inline would be a better idea.

Quoted 2 lines

ok. I liked the idea but I agree it's overkill to have an init routine just for this. I will find something.

Quoted 6 lines

That too, but it's also something that looks like an optimization but isn't, which is bad practice. On modern cpus math is cheap (and this is just a trivial offset), memory accesses are expensive. You're essentially caching this offset - raising all the usual invalidation questions for a cache - when caching it is *more* expensive than just computing it every time.

ok. removing this offset was a good opportunity to generalize the routing algorithm and use a 'ring' parameter in all routines. Same for the accept path.

C.

#55 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 202 lines · Cédric Le Goater
Quoted 201 lines · David Gibson
Quoted 190 lines · Cédric Le Goater
Quoted 189 lines · David Gibson
Quoted 64 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 6 lines

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

I agree.

Quoted 80 lines
Quoted 17 lines
Quoted 17 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 1 line

yes. In patch 9, when we introduce the nvt notify routine.

Ok.

Quoted 31 lines
Quoted 30 lines
Quoted 16 lines
hw/intc/xive.c +16 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }

hw/intc/xive.c line 62

Quoted 10 lines
hw/intc/xive.c +10 -0
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Yes, you did and I haven't studied the question yet. For the next version.

So, it's possible that using the cpu->as thing will be more trouble that it's worth.

One of the trouble is the number of memory regions to use, one per cpu,

Well, we're already going to have an NVT object for each cpu, yes? So a memory region per-cpu doesn't seem like a big stretch.

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

and the KVM support.

And I really don't see how the memory regions impacts KVM.

The TIMA is setup when the KVM device is initialized using some specific ioctl to get an fd on a MMIO region from the host. It is then passed to the guest as a 'ram_device', same for the ESBs.

Ah, good point.

Quoted 1 line

This is not a common region.

I'm not sure what you mean by that.

Quoted 18 lines
Quoted 18 lines
Quoted 7 lines

Having a single region is much easier.

Quoted 3 lines

I am a little concerned about using current_cpu though. First, will it work with KVM with kernel_irqchip=off - the cpus are running truly concurrently,

FWIW, I didn't see any issue yet while stressing.

Ok.

Quoted 7 lines
Quoted 1 line

but we still need to work out who's poking at the TIMA.

I understand. The registers are accessed by the current cpu to set the CPPR and to ack an interrupt. But when we route an event, we also access and modify the registers. Do you suggest some locking ? I am not sure how are protected the TIMA region accesses vs. the routing, which is necessarily initiated by an ESB MMIO though.

Quoted 7 lines
Quoted 5 lines

Locking isn't really the issue. I mean, we do need locking, but the BQL should provide that. The issue is what exactly does "current" mean in the context of multiple concurrently running cpus. Does it always mean what we need it to mean in every context we might call this from.

I would say so.

Ok.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#56 lore raw

Re: [Qemu-devel] [PATCH v3 06/35] spapr/xive: introduce a XIVE interrupt presenter model

Quoted 211 lines · David Gibson
Quoted 202 lines · Cédric Le Goater
Quoted 201 lines · David Gibson
Quoted 190 lines · Cédric Le Goater
Quoted 189 lines · David Gibson
Quoted 64 lines · Cédric Le Goater
Quoted 51 lines · David Gibson
Quoted 50 lines · Cédric Le Goater

The XIVE presenter engine uses a set of registers to handle priority management and interrupt acknowledgment among other things. The most important ones being :

- Interrupt Priority Register (PIPR)
- Interrupt Pending Buffer (IPB)
- Current Processor Priority (CPPR)
- Notification Source Register (NSR)

There is one set of registers per level of privilege, four in all :
HW, HV pool, OS and User. These are called rings. All registers are accessible through a specific MMIO region called the Thread Interrupt Management Areas (TIMA) but, depending on the privilege level of the CPU, the view of the TIMA is filtered. The sPAPR machine runs at the OS privilege and therefore can only accesses the OS and the User rings. The others are for hypervisor levels.

The CPU interrupt state is modeled with a XiveNVT object which stores the values of the different registers. The different TIMA views are mapped at the same address for each CPU and 'current_cpu' is used to retrieve the XiveNVT holding the ring registers.

---

Changes since v2 :

- introduced the XiveFabric interface

 hw/intc/spapr_xive.c        |  25 ++++
 hw/intc/xive.c              | 279 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/spapr_xive.h |   5 +
 include/hw/ppc/xive.h       |  31 +++++
 include/hw/ppc/xive_regs.h  |  84 +++++++++++++
 5 files changed, 424 insertions(+)
hw/intc/spapr_xive.c +1 -0
index 90cde8a4082d..f07832bf0a00 100644
@@ -13,6 +13,7 @@
 #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"
@@ -95,6 +96,22 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
 
Quoted 11 lines

hw/intc/spapr_xive.c static void spapr_xive_realize(DeviceState *dev, Error **errp) · line 104

Quoted 8 lines
hw/intc/spapr_xive.c +6 -0
     /* Allocate the Interrupt Virtualization Table */
     xive->ivt = g_new0(XiveIVE, xive->nr_irqs);
+
+    /* The Thread Interrupt Management Area has the same address for
+     * each chip. On sPAPR, we only need to expose the User and OS
+     * level views of the TIMA.
+     */
+    xive->tm_base = XIVE_TM_BASE;

The constant should probably have PAPR in the name somewhere, since it's just for PAPR machines (same for the ESB mappings, actually).

Quoted 6 lines

ok.

I have also made 'tm_base' a property, like 'vc_base' for ESBs, in case we want to change the value when the guest is instantiated. I doubt it but this is an address in the global address space, so letting the machine have control is better I think.

I agree.

Quoted 80 lines
Quoted 17 lines
Quoted 17 lines
hw/intc/spapr_xive.c +10 -0
+
+    memory_region_init_io(&xive->tm_mmio_user, OBJECT(xive),
+                          &xive_tm_user_ops, xive, "xive.tima.user",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_user);
+
+    memory_region_init_io(&xive->tm_mmio_os, OBJECT(xive),
+                          &xive_tm_os_ops, xive, "xive.tima.os",
+                          1ull << TM_SHIFT);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &xive->tm_mmio_os);
 }
 
 static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
@@ -104,6 +121,13 @@ static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn)
     return lisn < xive->nr_irqs ? &xive->ivt[lisn] : NULL;
 }
 
Quoted 13 lines

hw/intc/spapr_xive.c static XiveIVE *spapr_xive_get_ive(XiveFabric *xf, uint32_t lisn) · line 129

Quoted 6 lines
hw/intc/spapr_xive.c +6 -0
+static XiveNVT *spapr_xive_get_nvt(XiveFabric *xf, uint32_t server)
+{
+    PowerPCCPU *cpu = spapr_find_cpu(server);
+
+    return cpu ? XIVE_NVT(cpu->intc) : NULL;
+}

So this is a bit of a tangent, but I've been thinking of implementing a scheme where there's an opaque pointer in the cpu structure for the use of the machine. I'm planning for that to replace the intc pointer (which isn't really used directly by the cpu). That would allow us to have spapr put a structure there and have both xics and xive pointers which could be useful later on.

ok. That should simplify the patchset at the end, in which we need to switch the 'intc' pointer.

Quoted 37 lines

I think we'd need something similar to correctly handle migration of the VPA state, which is currently horribly broken.

Quoted 34 lines
hw/intc/spapr_xive.c +2 -0
+
 static const VMStateDescription vmstate_spapr_xive_ive = {
     .name = TYPE_SPAPR_XIVE "/ive",
     .version_id = 1,
@@ -143,6 +167,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data)
     dc->vmsd = &vmstate_spapr_xive;
 
     xfc->get_ive = spapr_xive_get_ive;
+    xfc->get_nvt = spapr_xive_get_nvt;
 }
 
 static const TypeInfo spapr_xive_info = {
hw/intc/xive.c +13 -0
index dccad0318834..5691bb9474e4 100644
@@ -14,7 +14,278 @@
 #include "sysemu/cpus.h"
 #include "sysemu/dma.h"
 #include "monitor/monitor.h"
+#include "hw/ppc/xics.h" /* for ICP_PROP_CPU */
 #include "hw/ppc/xive.h"
+#include "hw/ppc/xive_regs.h"
+
+/*
+ * XIVE Interrupt Presenter
+ */
+
+static uint64_t xive_nvt_accept(XiveNVT *nvt)
+{
+    return 0;
+}
+
+static void xive_nvt_set_cppr(XiveNVT *nvt, uint8_t cppr)
Quoted 8 lines

hw/intc/xive.c line 36

Quoted 6 lines
hw/intc/xive.c +6 -0
+{
+    if (cppr > XIVE_PRIORITY_MAX) {
+        cppr = 0xff;
+    }
+
+    nvt->ring_os[TM_CPPR] = cppr;

Surely this needs to recheck if we should be interrupting the cpu?

Quoted 1 line

yes. In patch 9, when we introduce the nvt notify routine.

Ok.

Quoted 31 lines
Quoted 30 lines
Quoted 16 lines
hw/intc/xive.c +16 -0
+}
+
+/*
+ * OS Thread Interrupt Management Area MMIO
+ */
+static uint64_t xive_tm_read_special(XiveNVT *nvt, hwaddr offset,
+                                           unsigned size)
+{
+    uint64_t ret = -1;
+
+    if (offset == TM_SPC_ACK_OS_REG && size == 2) {
+        ret = xive_nvt_accept(nvt);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid TIMA read @%"
+                      HWADDR_PRIx" size %d\n", offset, size);
+    }

hw/intc/xive.c line 62

Quoted 10 lines
hw/intc/xive.c +10 -0
+
+    return ret;
+}
+
+#define TM_RING(offset) ((offset) & 0xf0)
+
+static uint64_t xive_tm_os_read(void *opaque, hwaddr offset,
+                                      unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);

So, as I said on a previous version of this, we can actually correctly represent different mappings in different cpu spaces, by exploiting cpu->as and not just having them all point to &address_space_memory.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Yes, you did and I haven't studied the question yet. For the next version.

So, it's possible that using the cpu->as thing will be more trouble that it's worth.

One of the trouble is the number of memory regions to use, one per cpu,

Well, we're already going to have an NVT object for each cpu, yes? So a memory region per-cpu doesn't seem like a big stretch.

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

and the KVM support.

And I really don't see how the memory regions impacts KVM.

The TIMA is setup when the KVM device is initialized using some specific ioctl to get an fd on a MMIO region from the host. It is then passed to the guest as a 'ram_device', same for the ESBs.

Quoted 5 lines

Ah, good point.

Quoted 1 line

This is not a common region.

I'm not sure what you mean by that.

I meant by that 'out of the ordinary', 'unusual'. Specially when under KVM.

C.

Quoted 28 lines
Quoted 18 lines
Quoted 18 lines
Quoted 7 lines

Having a single region is much easier.

Quoted 3 lines

I am a little concerned about using current_cpu though. First, will it work with KVM with kernel_irqchip=off - the cpus are running truly concurrently,

FWIW, I didn't see any issue yet while stressing.

Ok.

Quoted 7 lines
Quoted 1 line

but we still need to work out who's poking at the TIMA.

I understand. The registers are accessed by the current cpu to set the CPPR and to ack an interrupt. But when we route an event, we also access and modify the registers. Do you suggest some locking ? I am not sure how are protected the TIMA region accesses vs. the routing, which is necessarily initiated by an ESB MMIO though.

Quoted 7 lines
Quoted 5 lines

Locking isn't really the issue. I mean, we do need locking, but the BQL should provide that. The issue is what exactly does "current" mean in the context of multiple concurrently running cpus. Does it always mean what we need it to mean in every context we might call this from.

I would say so.

Ok.