[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
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, (continued)
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Peter Maydell, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
[Qemu-devel] [PATCH RFC V5 3/9] hw/intc: arm_gicv3_cpu_interface, Shlomo Pongratz, 2015/10/20
[Qemu-devel] [PATCH RFC V5 5/9] hw/intc arm_gicv3_redist,
Shlomo Pongratz <=
[Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Shlomo Pongratz, 2015/10/20
- Re: [Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Pavel Fedin, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Shlomo Pongratz, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Pavel Fedin, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Shlomo Pongratz, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Pavel Fedin, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 8/9] target-arm/cpu64 GICv3 system instructions support, Shlomo Pongratz, 2015/10/22
[Qemu-devel] [PATCH RFC V5 4/9] hw/intc: arm_gicv3_dist, Shlomo Pongratz, 2015/10/20
[Qemu-devel] [PATCH RFC V5 9/9] hw/arm: Add virt-v3 machine that uses GIC-500, Shlomo Pongratz, 2015/10/20