qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH RFC V5 5/9] hw/intc arm_gicv3_redist


From: Shlomo Pongratz
Subject: [Qemu-devel] [PATCH RFC V5 5/9] hw/intc arm_gicv3_redist
Date: Tue, 20 Oct 2015 20:22:08 +0300

From: Shlomo Pongratz <address@hidden>

This patch includes the code that implements the functionality of
the redisributer, which is the main enhancment of GICv3 over GICv3

Signed-off-by: Shlomo Pongratz <address@hidden>
---
 hw/intc/Makefile.objs      |   1 +
 hw/intc/arm_gicv3_redist.c | 460 +++++++++++++++++++++++++++++++++++++++++++++
 hw/intc/arm_gicv3_redist.h |   9 +
 3 files changed, 470 insertions(+)
 create mode 100644 hw/intc/arm_gicv3_redist.c
 create mode 100644 hw/intc/arm_gicv3_redist.h

diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index cdfb877..5e56acc 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -16,6 +16,7 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o
 common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_interrupts.o
 common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpu_interface.o
 common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_dist.o
+common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o
 common-obj-$(CONFIG_OPENPIC) += openpic.o
 
 obj-$(CONFIG_APIC) += apic.o apic_common.o
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
new file mode 100644
index 0000000..cbac184
--- /dev/null
+++ b/hw/intc/arm_gicv3_redist.c
@@ -0,0 +1,460 @@
+#include "gicv3_internal.h"
+#include "qom/cpu.h"
+#include "arm_gicv3_redist.h"
+#include "arm_gicv3_interrupts.h"
+
+static const uint8_t gic_redist_ids[] = {
+    0x44, 0x00, 0x00, 0x00, 0x093, 0xB4, 0x3B, 0x00, 0x0D, 0xF0, 0x05, 0xB1
+};
+
+static uint64_t gic_redist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
+{
+    GICv3State *s = (GICv3State *)opaque;
+    uint64_t res = 0;
+    uint64_t sgi_ppi, core, off;
+    uint64_t irq, i;
+
+    /* GIC-500 Table 3-2 page 3-4 (Rev r0p0)
+     * [          1<bits-for-core#>x<16-bits-offset>]
+     * x = 0: LPIs
+     * x = 1: SGIs & PPIs
+     */
+    off = offset;
+    sgi_ppi = off & (1 << 16);
+    core = (off >> 17) & s->cpu_mask;
+    offset = off & 0xFFFF;
+
+    if (sgi_ppi) {
+        /* SGIs, PPIs */
+        /* Interrupt Set/Clear Enable.  */
+        if (offset < 0x100) {
+            if (offset >= 0x80) {
+                /* Interrupt Group Registers: these RAZ/WI if this is an NS
+                 * access to a GIC with the security extensions, or if the GIC
+                 * doesn't have groups at all.
+                 */
+                res = 0;
+                if (!attrs.secure) {
+                    /* GIC-500 comment 'a' */
+                    goto bad_reg;
+                }
+                /* Every byte offset holds 8 group status bits */
+                irq = (offset - 0x080) * 8 + GICV3_BASE_IRQ;
+                if (irq >= s->num_irq) {
+                    goto bad_reg;
+                }
+                if (irq >= GICV3_INTERNAL) {
+                    DPRINTF("non Internal should be only in dist %lu\n", irq);
+                    goto bad_reg;
+                }
+                for (i = 0; i < 8; i++) {
+                    if (GIC_TEST_GROUP(irq + i, core)) {
+                        res |= (1 << i);
+                    }
+                }
+                return res;
+            }
+        } else if (offset < 0x200) {
+            /* Interrupt Set/Clear Enable.  */
+            if (offset < 0x180)
+                irq = (offset - 0x100) * 8;
+            else
+                irq = (offset - 0x180) * 8;
+            irq += GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in dist %lu\n", irq);
+                goto bad_reg;
+            }
+            res = 0;
+            for (i = 0; i < 8; i++) {
+                if (GIC_TEST_ENABLED(irq + i, core)) {
+                    res |= (1 << i);
+                }
+            }
+        } else if (offset < 0x300) {
+            /* Interrupt Set/Clear Pending.  */
+            if (offset < 0x280)
+                irq = (offset - 0x200) * 8;
+            else
+                irq = (offset - 0x280) * 8;
+            irq += GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in dist %lu\n", irq);
+                goto bad_reg;
+            }
+            res = 0;
+            for (i = 0; i < 8; i++) {
+                if (gic_test_pending(s, irq + i, core)) {
+                    res |= (1 << i);
+                }
+            }
+        } else if (offset < 0x400) {
+            /* Interrupt Set/Clear Active.  */
+            if (offset < 0x380)
+                irq = (offset - 0x300) * 8 + GICV3_BASE_IRQ;
+            else
+                irq = (offset - 0x380) * 8 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL)
+                goto bad_reg;
+            res = 0;
+            for (i = 0; i < 8; i++) {
+                if (GIC_TEST_ACTIVE(irq + i, core)) {
+                    res |= (1 << i);
+                }
+            }
+        } else if (offset < 0x800) {
+            /* Interrupt Priority.  */
+            irq = (offset - 0x400) + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL * 8) {
+                DPRINTF("non Internal should be only in dist %lu\n", irq);
+                goto bad_reg;
+            }
+            res = GIC_GET_PRIORITY(irq, core);
+        } else if (offset < 0xc00) {
+                goto bad_reg;
+        } else if (offset < 0xd00) {
+            /* Interrupt Configuration.  */
+            irq = (offset - 0xc00) * 4 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in dist %lu\n", irq);
+                goto bad_reg;
+            }
+            res = 0;
+            for (i = 0; i < 4; i++) {
+                /* Even bits are reserved */
+                if (GIC_TEST_EDGE_TRIGGER(irq + i))
+                    res |= (2 << (i * 2));
+            }
+        }
+    } else {
+        /* LPIs */
+        if (offset & 3)
+            return 0;
+        if (offset < 0x100) {
+            if (offset == 0) {/* GICR_CTLR */
+                DPRINTF("Redist-GICR_CTLR-CPU caller cpu(%d) core(%lu)\n",
+                    gic_get_current_cpu(s), core);
+                return 0;
+            }
+            if (offset == 4)
+                return 0x43B; /* GICR_IIDR */
+            if (offset == 0x8) { /* GICR_TYPER */
+                uint64_t mp_affinity = s->mp_affinity[core];
+                res = core << 8; /* Linear cpu number */
+                /* See GICv3 section 5.4.8 */
+                res |= (mp_affinity & ARM_AFF0_MASK) << (32 - ARM_AFF0_SHIFT);
+                res |= (mp_affinity & ARM_AFF1_MASK) << (40 - ARM_AFF1_SHIFT);
+                res |= (mp_affinity & ARM_AFF2_MASK) << (48 - ARM_AFF2_SHIFT);
+                res |= (mp_affinity & ARM_AFF3_MASK) << (56 - ARM_AFF3_SHIFT);
+                if (core == s->num_cpu - 1) {
+                    /* Mark last redistributer */
+                    res |= 1 << 4;
+                }
+                return res;
+            }
+            if (offset == 0xc) { /* GICR_TYPER */
+                /* should write readll */
+                return 0;
+            }
+            if (offset == 0x14) { /* GICR_WAKER */
+                if (test_bit(core, s->cpu_enabled))
+                    return 0;
+                else
+                    return GICR_WAKER_ProcessorSleep;
+                DPRINTF("Redist-CPU (%d) is enabled(%lu)\n",
+                        gic_get_current_cpu(s), test_bit(core, 
s->cpu_enabled));
+
+            }
+            if (offset >= 0x80 && offset < 0xFFD0)
+                return 0;
+            goto bad_reg;
+        }
+        if (offset < 0xffd0) {
+            goto bad_reg;
+        } else /* offset >= 0xffd0 */ {
+            if (offset & 3) {
+                res = 0;
+            } else {
+                res = gic_redist_ids[(offset - 0xffd0) >> 2];
+            }
+        }
+    }
+    return res;
+bad_reg:
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "%s: Bad offset %x\n", __func__, (int)offset);
+    return 0;
+}
+
+MemTxResult gic_redist_read(void *opaque, hwaddr offset, uint64_t *data,
+                            unsigned size, MemTxAttrs attrs)
+{
+    switch (size) {
+    case 1:
+        *data = gic_redist_readb(opaque, offset, attrs);
+        return MEMTX_OK;
+    case 2:
+        *data = gic_redist_readb(opaque, offset, attrs);
+        *data |= gic_redist_readb(opaque, offset + 1, attrs) << 8;
+        return MEMTX_OK;
+    case 4:
+        *data = gic_redist_readb(opaque, offset, attrs);
+        *data |= gic_redist_readb(opaque, offset + 1, attrs) << 8;
+        *data |= gic_redist_readb(opaque, offset + 2, attrs) << 16;
+        *data |= gic_redist_readb(opaque, offset + 3, attrs) << 24;
+        return MEMTX_OK;
+    case 8:
+        *data = gic_redist_readb(opaque, offset, attrs);
+        *data |= gic_redist_readb(opaque, offset + 1, attrs) << 8;
+        *data |= gic_redist_readb(opaque, offset + 2, attrs) << 16;
+        *data |= gic_redist_readb(opaque, offset + 3, attrs) << 24;
+        *data |= gic_redist_readb(opaque, offset + 4, attrs) << 32;
+        *data |= gic_redist_readb(opaque, offset + 5, attrs) << 40;
+        *data |= gic_redist_readb(opaque, offset + 6, attrs) << 48;
+        *data |= gic_redist_readb(opaque, offset + 7, attrs) << 56;
+        return MEMTX_OK;
+    default:
+        return MEMTX_ERROR;
+    }
+}
+
+static void gic_redist_writeb(void *opaque, hwaddr offset,
+                            uint64_t value, MemTxAttrs attrs)
+{
+    GICv3State *s = (GICv3State *)opaque;
+    uint64_t sgi_ppi, core, off;
+    int irq, i;
+    /* GIC-500 Table 3-2 page 3-4 (Rev r0p0)
+     * [          1<bits-for-core#>x<16-bits-offset>]
+     * x = 0: LPIs
+     * x = 1: SGIs & PPIs
+     */
+    off = offset;
+    sgi_ppi = off & (1 << 16);
+    core = (off >> 17) & s->cpu_mask;
+    offset = off & 0xFFFF;
+
+    if (sgi_ppi) {
+        /* SGIs, PPIs */
+        if (offset < 0x100) {
+            if (offset >= 0x80) {
+                /* Interrupt Group Registers: RAZ/WI for NS access to secure
+                 * GIC, or for GICs without groups.
+                 */
+                if (!attrs.secure) {
+                    /* GIC-500 comment 'f' */
+                    goto bad_reg;
+                }
+                /* Every byte offset holds 8 group status bits */
+                irq = (offset - 0x80) * 8 + GICV3_BASE_IRQ;
+                if (irq >= s->num_irq) {
+                    goto bad_reg;
+                }
+                if (irq >= GICV3_INTERNAL) {
+                    DPRINTF("IGROUPR0 non Internal should be only in 
distributer %d\n", irq);
+                    return;
+                }
+                for (i = 0; i < 8; i++) {
+                    /* Group bits are banked for private interrupts */
+                    if (value & (1 << i)) {
+                        /* Group1 (Non-secure) */
+                        GIC_SET_GROUP(irq + i, core);
+                    } else {
+                        /* Group0 (Secure) */
+                        GIC_CLEAR_GROUP(irq + i, core);
+                    }
+                }
+            }
+        } else if (offset < 0x180) {
+            /* Interrupt Set Enable.  */
+            irq = (offset - 0x100) * 8 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("ISENABLERn non Internal should be only in distributer 
%d\n", irq);
+                /* The registers after 0x100 are reserved */
+                return;
+            }
+            if (irq < GICV3_NR_SGIS) {
+                value = 0xff;
+            }
+
+            for (i = 0; i < 8; i++) {
+                if (value & (1 << i)) {
+                    /* This is redistributer ALL doesn't apply */
+                    if (!GIC_TEST_ENABLED(irq + i, core)) {
+                        DPRINTF("Enabled IRQ %d\n", irq + i);
+                    }
+                    GIC_SET_ENABLED(irq + i, core);
+                    /* If a raised level triggered IRQ enabled then mark
+                       is as pending.  */
+                    if (GIC_TEST_LEVEL(irq + i, core)
+                            && !GIC_TEST_EDGE_TRIGGER(irq + i)) {
+                        DPRINTF("Set %d pending core %lx\n", irq + i, core);
+                        GIC_SET_PENDING(irq + i, core);
+                    }
+                }
+            }
+        } else if (offset < 0x200) {
+            /* Interrupt Clear Enable.  */
+            irq = (offset - 0x180) * 8 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("ICENABLERn non Internal should be only in distributer 
%d\n", irq);
+                /* The registers after 0x180 are reserved */
+                return;
+            }
+            if (irq < GICV3_NR_SGIS) {
+                value = 0;
+            }
+
+            for (i = 0; i < 8; i++) {
+                if (value & (1 << i)) {
+                    /* This is redistributer ALL doesn't apply */
+                    if (GIC_TEST_ENABLED(irq + i, core)) {
+                        DPRINTF("Disabled IRQ %d\n", irq + i);
+                    }
+                    GIC_CLEAR_ENABLED(irq + i, core);
+                }
+            }
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in distributer %d\n", 
irq);
+                goto bad_reg;
+            }
+        } else if (offset < 0x280) {
+            /* Interrupt Set Pending.  */
+            irq = (offset - 0x200) * 8 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in distributer %d\n", 
irq);
+                goto bad_reg;
+            }
+            for (i = 0; i < 8; i++) {
+                if (value & (1 << i)) {
+                    GIC_SET_PENDING_MASK(irq + i, GIC_TARGET(irq + i));
+                }
+            }
+        } else if (offset < 0x300) {
+            /* Interrupt Clear Pending.  */
+            irq = (offset - 0x280) * 8 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in distributer %d\n", 
irq);
+                goto bad_reg;
+            }
+            for (i = 0; i < 8; i++) {
+                if (value & (1 << i)) {
+                    GIC_CLEAR_PENDING(irq + i, core);
+                }
+            }
+        } else if (offset < 0x400) {
+            /* Interrupt Active.  */
+            goto bad_reg;
+        } else if (offset < 0x800) {
+            /* Interrupt Priority. */
+            irq = (offset - 0x400) + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL * 8) {
+                DPRINTF("IPRIORITYRn non Internal should be only in 
distributer %d\n", irq);
+                /* The registers after 0x180 are reserved */
+                return;
+            }
+            gicv3_set_priority(s, core, irq, value, attrs);
+        } else if (offset < 0xc00) {
+            goto bad_reg;
+        } else if (offset < 0xd00) {
+            /* Interrupt Configuration.  */
+            irq = (offset - 0xc00) * 4 + GICV3_BASE_IRQ;
+            if (irq >= s->num_irq)
+                goto bad_reg;
+            if (irq >= GICV3_INTERNAL) {
+                DPRINTF("non Internal should be only in distributer %d\n", 
irq);
+                goto bad_reg;
+            }
+            if (irq < GICV3_NR_SGIS)
+                value |= 0xaa; /* 10101010 */
+            /* Even bits are reserved */
+            for (i = 0; i < 4; i++) {
+                if (value & (2 << (i * 2))) {
+                    GIC_SET_EDGE_TRIGGER(irq + i);
+                } else {
+                    GIC_CLEAR_EDGE_TRIGGER(irq + i);
+                }
+            }
+        }
+    } else {
+        /* LPIs */
+        if (offset == 0x14) { /* GICR_WAKER */
+            if (value & GICR_WAKER_ProcessorSleep)
+                clear_bit(core, s->cpu_enabled);
+            else
+                set_bit(core, s->cpu_enabled);
+            DPRINTF("Redist-CPU (%d) core(%lu) set enabled(%d)\n",
+                    gic_get_current_cpu(s), core, test_bit(core, 
s->cpu_enabled));
+       }
+    }
+    gicv3_update(s);
+    return;
+
+bad_reg:
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "%s: Bad offset %x\n", __func__, (int)offset);
+}
+
+static void gic_redist_writew(void *opaque, hwaddr offset,
+                            uint64_t value, MemTxAttrs attrs)
+{
+    gic_redist_writeb(opaque, offset, value & 0xff, attrs);
+    gic_redist_writeb(opaque, offset + 1, value >> 8, attrs);
+}
+
+static void gic_redist_writel(void *opaque, hwaddr offset,
+                            uint64_t value, MemTxAttrs attrs)
+{
+    gic_redist_writew(opaque, offset, value & 0xffff, attrs);
+    gic_redist_writew(opaque, offset + 2, value >> 16, attrs);
+}
+
+static void gic_redist_writell(void *opaque, hwaddr offset,
+                            uint64_t value, MemTxAttrs attrs)
+{
+    gic_redist_writel(opaque, offset, value & 0xffffffff, attrs);
+    gic_redist_writel(opaque, offset + 4, value >> 32, attrs);
+}
+
+MemTxResult gic_redist_write(void *opaque, hwaddr addr, uint64_t data,
+                             unsigned size, MemTxAttrs attrs)
+{
+    switch (size) {
+    case 1:
+        gic_redist_writeb(opaque, addr, data, attrs);
+        return MEMTX_OK;
+    case 2:
+        gic_redist_writew(opaque, addr, data, attrs);
+        return MEMTX_OK;
+    case 4:
+        gic_redist_writel(opaque, addr, data, attrs);
+        return MEMTX_OK;
+    case 8:
+        gic_redist_writell(opaque, addr, data, attrs);
+        return MEMTX_OK;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: size %u\n", __func__, size);
+        return MEMTX_ERROR;
+    }
+}
diff --git a/hw/intc/arm_gicv3_redist.h b/hw/intc/arm_gicv3_redist.h
new file mode 100644
index 0000000..cc65b8f
--- /dev/null
+++ b/hw/intc/arm_gicv3_redist.h
@@ -0,0 +1,9 @@
+#ifndef QEMU_ARM_GICV3_REDIST_H
+#define QEMU_ARM_GICV3_REDIST_H
+
+MemTxResult gic_redist_read(void *opaque, hwaddr offset, uint64_t *data,
+                            unsigned size, MemTxAttrs attrs);
+MemTxResult gic_redist_write(void *opaque, hwaddr addr, uint64_t data,
+                             unsigned size, MemTxAttrs attrs);
+
+#endif /* !QEMU_ARM_GIC_REDIST_H */
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]