[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-ppc] [RFC PATCH 07/26] ppc/xive: add MMIO handlers to the XIVE int
From: |
Cédric Le Goater |
Subject: |
[Qemu-ppc] [RFC PATCH 07/26] ppc/xive: add MMIO handlers to the XIVE interrupt source |
Date: |
Wed, 5 Jul 2017 19:13:20 +0200 |
Each interrupt source is associated with a 2-bit state machine called
an Event State Buffer (ESB). It is controlled by MMIO to trigger
events.
See code for more details on the states.
Signed-off-by: Cédric Le Goater <address@hidden>
---
hw/intc/xive.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/hw/ppc/xive.h | 3 +
2 files changed, 233 insertions(+)
diff --git a/hw/intc/xive.c b/hw/intc/xive.c
index 9ff14c0da595..816031b8ac81 100644
--- a/hw/intc/xive.c
+++ b/hw/intc/xive.c
@@ -32,6 +32,226 @@ static void xive_icp_irq(XiveICSState *xs, int lisn)
}
/*
+ * "magic" Event State Buffer (ESB) MMIO offsets.
+ *
+ * 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.
+ *
+ * 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_GET 0x800
+#define XIVE_ESB_SET_PQ_00 0xc00
+#define XIVE_ESB_SET_PQ_01 0xd00
+#define XIVE_ESB_SET_PQ_10 0xe00
+#define XIVE_ESB_SET_PQ_11 0xf00
+
+#define XIVE_ESB_VAL_P 0x2
+#define XIVE_ESB_VAL_Q 0x1
+
+#define XIVE_ESB_RESET 0x0
+#define XIVE_ESB_PENDING 0x2
+#define XIVE_ESB_QUEUED 0x3
+#define XIVE_ESB_OFF 0x1
+
+static uint8_t xive_pq_get(XIVE *x, uint32_t lisn)
+{
+ uint32_t idx = lisn;
+ uint32_t byte = idx / 4;
+ uint32_t bit = (idx % 4) * 2;
+ uint8_t* pqs = (uint8_t *) x->sbe;
+
+ return (pqs[byte] >> bit) & 0x3;
+}
+
+static void xive_pq_set(XIVE *x, uint32_t lisn, uint8_t pq)
+{
+ uint32_t idx = lisn;
+ uint32_t byte = idx / 4;
+ uint32_t bit = (idx % 4) * 2;
+ uint8_t* pqs = (uint8_t *) x->sbe;
+
+ pqs[byte] &= ~(0x3 << bit);
+ pqs[byte] |= (pq & 0x3) << bit;
+}
+
+static bool xive_pq_eoi(XIVE *x, uint32_t lisn)
+{
+ uint8_t old_pq = xive_pq_get(x, lisn);
+
+ switch (old_pq) {
+ case XIVE_ESB_RESET:
+ xive_pq_set(x, lisn, XIVE_ESB_RESET);
+ return false;
+ case XIVE_ESB_PENDING:
+ xive_pq_set(x, lisn, XIVE_ESB_RESET);
+ return false;
+ case XIVE_ESB_QUEUED:
+ xive_pq_set(x, lisn, XIVE_ESB_PENDING);
+ return true;
+ case XIVE_ESB_OFF:
+ xive_pq_set(x, lisn, XIVE_ESB_OFF);
+ return false;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static bool xive_pq_trigger(XIVE *x, uint32_t lisn)
+{
+ uint8_t old_pq = xive_pq_get(x, lisn);
+
+ switch (old_pq) {
+ case XIVE_ESB_RESET:
+ xive_pq_set(x, lisn, XIVE_ESB_PENDING);
+ return true;
+ case XIVE_ESB_PENDING:
+ xive_pq_set(x, lisn, XIVE_ESB_QUEUED);
+ return true;
+ case XIVE_ESB_QUEUED:
+ xive_pq_set(x, lisn, XIVE_ESB_QUEUED);
+ return true;
+ case XIVE_ESB_OFF:
+ xive_pq_set(x, lisn, XIVE_ESB_OFF);
+ return false;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/*
+ * XIVE Interrupt Source MMIOs
+ */
+static void xive_ics_eoi(XiveICSState *xs, uint32_t srcno)
+{
+ ICSIRQState *irq = &ICS_BASE(xs)->irqs[srcno];
+
+ if (irq->flags & XICS_FLAGS_IRQ_LSI) {
+ irq->status &= ~XICS_STATUS_SENT;
+ }
+}
+
+/* TODO: handle second page */
+static uint64_t xive_esb_read(void *opaque, hwaddr addr, unsigned size)
+{
+ XiveICSState *xs = ICS_XIVE(opaque);
+ XIVE *x = xs->xive;
+ uint32_t offset = addr & 0xF00;
+ uint32_t srcno = addr >> xs->esb_shift;
+ uint32_t lisn = srcno + ICS_BASE(xs)->offset;
+ XiveIVE *ive;
+ uint64_t ret = -1;
+
+ ive = xive_get_ive(x, lisn);
+ if (!ive || !(ive->w & IVE_VALID)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid LISN %d\n", lisn);
+ goto out;
+ }
+
+ if (srcno >= ICS_BASE(xs)->nr_irqs) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "XIVE: invalid IRQ number: %d/%d lisn: %d\n",
+ srcno, ICS_BASE(xs)->nr_irqs, lisn);
+ goto out;
+ }
+
+ switch (offset) {
+ case 0:
+ xive_ics_eoi(xs, srcno);
+
+ /* return TRUE or FALSE depending on PQ value */
+ ret = xive_pq_eoi(x, lisn);
+ break;
+
+ case XIVE_ESB_GET:
+ ret = xive_pq_get(x, lisn);
+ 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_pq_get(x, lisn);
+ xive_pq_set(x, lisn, (offset >> 8) & 0x3);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB addr %d\n", offset);
+ }
+
+out:
+ return ret;
+}
+
+static void xive_esb_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ XiveICSState *xs = ICS_XIVE(opaque);
+ XIVE *x = xs->xive;
+ uint32_t offset = addr & 0xF00;
+ uint32_t srcno = addr >> xs->esb_shift;
+ uint32_t lisn = srcno + ICS_BASE(xs)->offset;
+ XiveIVE *ive;
+ bool notify = false;
+
+ ive = xive_get_ive(x, lisn);
+ if (!ive || !(ive->w & IVE_VALID)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid LISN %d\n", lisn);
+ return;
+ }
+
+ if (srcno >= ICS_BASE(xs)->nr_irqs) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "XIVE: invalid IRQ number: %d/%d lisn: %d\n",
+ srcno, ICS_BASE(xs)->nr_irqs, lisn);
+ return;
+ }
+
+ switch (offset) {
+ case 0:
+ /* TODO: should we trigger even if the IVE is masked ? */
+ notify = xive_pq_trigger(x, lisn);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %d\n",
+ offset);
+ return;
+ }
+
+ if (notify && !(ive->w & IVE_MASKED)) {
+ qemu_irq_pulse(ICS_BASE(xs)->qirqs[srcno]);
+ }
+}
+
+static const MemoryRegionOps xive_esb_ops = {
+ .read = xive_esb_read,
+ .write = xive_esb_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 8,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 8,
+ .max_access_size = 8,
+ },
+};
+
+/*
* XIVE Interrupt Source
*/
static void xive_ics_set_irq_msi(XiveICSState *xs, int srcno, int val)
@@ -106,15 +326,25 @@ static void xive_ics_realize(ICSState *ics, Error **errp)
return;
}
+ if (!xs->esb_shift) {
+ error_setg(errp, "ESB page size needs to be greater 0");
+ return;
+ }
+
ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
ics->qirqs = qemu_allocate_irqs(xive_ics_set_irq, xs, ics->nr_irqs);
+ memory_region_init_io(&xs->esb_iomem, OBJECT(xs), &xive_esb_ops, xs,
+ "xive.esb",
+ (1ull << xs->esb_shift) * ICS_BASE(xs)->nr_irqs);
+
qemu_register_reset(xive_ics_reset, xs);
}
static Property xive_ics_properties[] = {
DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0),
DEFINE_PROP_UINT32("irq-base", ICSState, offset, 0),
+ DEFINE_PROP_UINT32("shift", XiveICSState, esb_shift, 0),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h
index 544cc6e0c796..5303d96f5f59 100644
--- a/include/hw/ppc/xive.h
+++ b/include/hw/ppc/xive.h
@@ -33,6 +33,9 @@ typedef struct XiveICSState XiveICSState;
struct XiveICSState {
ICSState parent_obj;
+ uint32_t esb_shift;
+ MemoryRegion esb_iomem;
+
XIVE *xive;
};
--
2.7.5
- Re: [Qemu-ppc] [RFC PATCH 05/26] ppc/xive: define XIVE internal tables, (continued)
[Qemu-ppc] [RFC PATCH 06/26] ppc/xive: introduce a XIVE interrupt source model, Cédric Le Goater, 2017/07/05
Re: [Qemu-ppc] [RFC PATCH 06/26] ppc/xive: introduce a XIVE interrupt source model, Cédric Le Goater, 2017/07/24
[Qemu-ppc] [RFC PATCH 07/26] ppc/xive: add MMIO handlers to the XIVE interrupt source,
Cédric Le Goater <=
Re: [Qemu-ppc] [Qemu-devel] [RFC PATCH 07/26] ppc/xive: add MMIO handlers to the XIVE interrupt source, Alexey Kardashevskiy, 2017/07/24
[Qemu-ppc] [RFC PATCH 08/26] ppc/xive: add flags to the XIVE interrupt source, Cédric Le Goater, 2017/07/05