[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 26/45] intc/arm_gic: Implement the virtual interface
From: |
Peter Maydell |
Subject: |
[Qemu-devel] [PULL 26/45] intc/arm_gic: Implement the virtual interface registers |
Date: |
Tue, 14 Aug 2018 19:17:56 +0100 |
From: Luc Michel <address@hidden>
Implement the read and write functions for the virtual interface of the
virtualization extensions in the GICv2.
One mirror region per CPU is also created, which maps to that specific
CPU id. This is required by the GIC architecture specification.
Signed-off-by: Luc Michel <address@hidden>
Reviewed-by: Peter Maydell <address@hidden>
Message-id: address@hidden
Signed-off-by: Peter Maydell <address@hidden>
---
hw/intc/arm_gic.c | 235 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 233 insertions(+), 2 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 7ee2e6bcbb3..60631964876 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -147,6 +147,24 @@ static void gic_update(GICState *s)
}
}
+/* Return true if this LR is empty, i.e. the corresponding bit
+ * in ELRSR is set.
+ */
+static inline bool gic_lr_entry_is_free(uint32_t entry)
+{
+ return (GICH_LR_STATE(entry) == GICH_LR_STATE_INVALID)
+ && (GICH_LR_HW(entry) || !GICH_LR_EOI(entry));
+}
+
+/* Return true if this LR should trigger an EOI maintenance interrupt, i.e. the
+ * corrsponding bit in EISR is set.
+ */
+static inline bool gic_lr_entry_is_eoi(uint32_t entry)
+{
+ return (GICH_LR_STATE(entry) == GICH_LR_STATE_INVALID)
+ && !GICH_LR_HW(entry) && GICH_LR_EOI(entry);
+}
+
static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
int cm, int target)
{
@@ -1572,6 +1590,200 @@ static MemTxResult gic_thisvcpu_write(void *opaque,
hwaddr addr,
return gic_cpu_write(s, gic_get_current_vcpu(s), addr, value, attrs);
}
+static uint32_t gic_compute_eisr(GICState *s, int cpu, int lr_start)
+{
+ int lr_idx;
+ uint32_t ret = 0;
+
+ for (lr_idx = lr_start; lr_idx < s->num_lrs; lr_idx++) {
+ uint32_t *entry = &s->h_lr[lr_idx][cpu];
+ ret = deposit32(ret, lr_idx - lr_start, 1,
+ gic_lr_entry_is_eoi(*entry));
+ }
+
+ return ret;
+}
+
+static uint32_t gic_compute_elrsr(GICState *s, int cpu, int lr_start)
+{
+ int lr_idx;
+ uint32_t ret = 0;
+
+ for (lr_idx = lr_start; lr_idx < s->num_lrs; lr_idx++) {
+ uint32_t *entry = &s->h_lr[lr_idx][cpu];
+ ret = deposit32(ret, lr_idx - lr_start, 1,
+ gic_lr_entry_is_free(*entry));
+ }
+
+ return ret;
+}
+
+static void gic_vmcr_write(GICState *s, uint32_t value, MemTxAttrs attrs)
+{
+ int vcpu = gic_get_current_vcpu(s);
+ uint32_t ctlr;
+ uint32_t abpr;
+ uint32_t bpr;
+ uint32_t prio_mask;
+
+ ctlr = FIELD_EX32(value, GICH_VMCR, VMCCtlr);
+ abpr = FIELD_EX32(value, GICH_VMCR, VMABP);
+ bpr = FIELD_EX32(value, GICH_VMCR, VMBP);
+ prio_mask = FIELD_EX32(value, GICH_VMCR, VMPriMask) << 3;
+
+ gic_set_cpu_control(s, vcpu, ctlr, attrs);
+ s->abpr[vcpu] = MAX(abpr, GIC_VIRT_MIN_ABPR);
+ s->bpr[vcpu] = MAX(bpr, GIC_VIRT_MIN_BPR);
+ gic_set_priority_mask(s, vcpu, prio_mask, attrs);
+}
+
+static MemTxResult gic_hyp_read(void *opaque, int cpu, hwaddr addr,
+ uint64_t *data, MemTxAttrs attrs)
+{
+ GICState *s = ARM_GIC(opaque);
+ int vcpu = cpu + GIC_NCPU;
+
+ switch (addr) {
+ case A_GICH_HCR: /* Hypervisor Control */
+ *data = s->h_hcr[cpu];
+ break;
+
+ case A_GICH_VTR: /* VGIC Type */
+ *data = FIELD_DP32(0, GICH_VTR, ListRegs, s->num_lrs - 1);
+ *data = FIELD_DP32(*data, GICH_VTR, PREbits,
+ GIC_VIRT_MAX_GROUP_PRIO_BITS - 1);
+ *data = FIELD_DP32(*data, GICH_VTR, PRIbits,
+ (7 - GIC_VIRT_MIN_BPR) - 1);
+ break;
+
+ case A_GICH_VMCR: /* Virtual Machine Control */
+ *data = FIELD_DP32(0, GICH_VMCR, VMCCtlr,
+ extract32(s->cpu_ctlr[vcpu], 0, 10));
+ *data = FIELD_DP32(*data, GICH_VMCR, VMABP, s->abpr[vcpu]);
+ *data = FIELD_DP32(*data, GICH_VMCR, VMBP, s->bpr[vcpu]);
+ *data = FIELD_DP32(*data, GICH_VMCR, VMPriMask,
+ extract32(s->priority_mask[vcpu], 3, 5));
+ break;
+
+ case A_GICH_MISR: /* Maintenance Interrupt Status */
+ *data = s->h_misr[cpu];
+ break;
+
+ case A_GICH_EISR0: /* End of Interrupt Status 0 and 1 */
+ case A_GICH_EISR1:
+ *data = gic_compute_eisr(s, cpu, (addr - A_GICH_EISR0) * 8);
+ break;
+
+ case A_GICH_ELRSR0: /* Empty List Status 0 and 1 */
+ case A_GICH_ELRSR1:
+ *data = gic_compute_elrsr(s, cpu, (addr - A_GICH_ELRSR0) * 8);
+ break;
+
+ case A_GICH_APR: /* Active Priorities */
+ *data = s->h_apr[cpu];
+ break;
+
+ case A_GICH_LR0 ... A_GICH_LR63: /* List Registers */
+ {
+ int lr_idx = (addr - A_GICH_LR0) / 4;
+
+ if (lr_idx > s->num_lrs) {
+ *data = 0;
+ } else {
+ *data = s->h_lr[lr_idx][cpu];
+ }
+ break;
+ }
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gic_hyp_read: Bad offset %" HWADDR_PRIx "\n", addr);
+ return MEMTX_OK;
+ }
+
+ return MEMTX_OK;
+}
+
+static MemTxResult gic_hyp_write(void *opaque, int cpu, hwaddr addr,
+ uint64_t value, MemTxAttrs attrs)
+{
+ GICState *s = ARM_GIC(opaque);
+ int vcpu = cpu + GIC_NCPU;
+
+ switch (addr) {
+ case A_GICH_HCR: /* Hypervisor Control */
+ s->h_hcr[cpu] = value & GICH_HCR_MASK;
+ break;
+
+ case A_GICH_VMCR: /* Virtual Machine Control */
+ gic_vmcr_write(s, value, attrs);
+ break;
+
+ case A_GICH_APR: /* Active Priorities */
+ s->h_apr[cpu] = value;
+ s->running_priority[vcpu] = gic_get_prio_from_apr_bits(s, vcpu);
+ break;
+
+ case A_GICH_LR0 ... A_GICH_LR63: /* List Registers */
+ {
+ int lr_idx = (addr - A_GICH_LR0) / 4;
+
+ if (lr_idx > s->num_lrs) {
+ return MEMTX_OK;
+ }
+
+ s->h_lr[lr_idx][cpu] = value & GICH_LR_MASK;
+ break;
+ }
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gic_hyp_write: Bad offset %" HWADDR_PRIx "\n", addr);
+ return MEMTX_OK;
+ }
+
+ return MEMTX_OK;
+}
+
+static MemTxResult gic_thiscpu_hyp_read(void *opaque, hwaddr addr, uint64_t
*data,
+ unsigned size, MemTxAttrs attrs)
+{
+ GICState *s = (GICState *)opaque;
+
+ return gic_hyp_read(s, gic_get_current_cpu(s), addr, data, attrs);
+}
+
+static MemTxResult gic_thiscpu_hyp_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ GICState *s = (GICState *)opaque;
+
+ return gic_hyp_write(s, gic_get_current_cpu(s), addr, value, attrs);
+}
+
+static MemTxResult gic_do_hyp_read(void *opaque, hwaddr addr, uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
+{
+ GICState **backref = (GICState **)opaque;
+ GICState *s = *backref;
+ int id = (backref - s->backref);
+
+ return gic_hyp_read(s, id, addr, data, attrs);
+}
+
+static MemTxResult gic_do_hyp_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ GICState **backref = (GICState **)opaque;
+ GICState *s = *backref;
+ int id = (backref - s->backref);
+
+ return gic_hyp_write(s, id + GIC_NCPU, addr, value, attrs);
+
+}
+
static const MemoryRegionOps gic_ops[2] = {
{
.read_with_attrs = gic_dist_read,
@@ -1593,8 +1805,8 @@ static const MemoryRegionOps gic_cpu_ops = {
static const MemoryRegionOps gic_virt_ops[2] = {
{
- .read_with_attrs = NULL,
- .write_with_attrs = NULL,
+ .read_with_attrs = gic_thiscpu_hyp_read,
+ .write_with_attrs = gic_thiscpu_hyp_write,
.endianness = DEVICE_NATIVE_ENDIAN,
},
{
@@ -1604,6 +1816,12 @@ static const MemoryRegionOps gic_virt_ops[2] = {
}
};
+static const MemoryRegionOps gic_viface_ops = {
+ .read_with_attrs = gic_do_hyp_read,
+ .write_with_attrs = gic_do_hyp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
static void arm_gic_realize(DeviceState *dev, Error **errp)
{
/* Device instance realize function for the GIC sysbus device */
@@ -1645,6 +1863,19 @@ static void arm_gic_realize(DeviceState *dev, Error
**errp)
&s->backref[i], "gic_cpu", 0x100);
sysbus_init_mmio(sbd, &s->cpuiomem[i+1]);
}
+
+ /* Extra core-specific regions for virtual interfaces. This is required by
+ * the GICv2 specification.
+ */
+ if (s->virt_extn) {
+ for (i = 0; i < s->num_cpu; i++) {
+ memory_region_init_io(&s->vifaceiomem[i + 1], OBJECT(s),
+ &gic_viface_ops, &s->backref[i],
+ "gic_viface", 0x1000);
+ sysbus_init_mmio(sbd, &s->vifaceiomem[i + 1]);
+ }
+ }
+
}
static void arm_gic_class_init(ObjectClass *klass, void *data)
--
2.18.0
- [Qemu-devel] [PULL 42/45] target/arm: Fix sign of sve_cmpeq_ppzw/sve_cmpne_ppzw, (continued)
- [Qemu-devel] [PULL 42/45] target/arm: Fix sign of sve_cmpeq_ppzw/sve_cmpne_ppzw, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 27/45] intc/arm_gic: Implement gic_update_virt() function, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 44/45] target/arm: Reorganize SVE WHILE, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 05/45] accel/tcg: Pass read access type through to io_readx(), Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 02/45] nvic: Handle ARMv6-M SCS reserved registers, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 09/45] accel/tcg: Return -1 for execution from MMIO regions in get_page_addr_code(), Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 17/45] intc/arm_gic: Add virtual interface register definitions, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 18/45] intc/arm_gic: Add virtualization extensions helper macros and functions, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 23/45] intc/arm_gic: Implement virtualization extensions in gic_(deactivate|complete_irq), Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 25/45] intc/arm_gic: Wire the vCPU interface, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 26/45] intc/arm_gic: Implement the virtual interface registers,
Peter Maydell <=
- [Qemu-devel] [PULL 30/45] xlnx-zynqmp: Improve GIC wiring and MMIO mapping, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 29/45] intc/arm_gic: Improve traces, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 33/45] target/arm: Mask virtual interrupts if HCR_EL2.TGE is set, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 37/45] target/arm: Treat SCTLR_EL1.M as if it were zero when HCR_EL2.TGE is set, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 40/45] target/arm: Restore M-profile CONTROL.SPSEL before any tailchaining, Peter Maydell, 2018/08/14
- [Qemu-devel] [PULL 43/45] target/arm: Fix typo in do_sat_addsub_64, Peter Maydell, 2018/08/14
- Re: [Qemu-devel] [PULL 00/45] target-arm queue, Peter Maydell, 2018/08/15