From: Shlomo Pongratz <shlomo.pongr...@huawei.com> This class is to be used by both software and KVM implementations of GICv3
Signed-off-by: Shlomo Pongratz <shlomo.pongr...@huawei.com> Signed-off-by: Pavel Fedin <p.fe...@samsung.com> --- hw/intc/Makefile.objs | 1 + hw/intc/arm_gicv3_common.c | 249 +++++++++++++++++++++++++++++++++++++ hw/intc/gicv3_internal.h | 156 +++++++++++++++++++++++ include/hw/intc/arm_gicv3_common.h | 112 +++++++++++++++++ 4 files changed, 518 insertions(+) create mode 100644 hw/intc/arm_gicv3_common.c create mode 100644 hw/intc/gicv3_internal.h create mode 100644 include/hw/intc/arm_gicv3_common.h diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 092d8a8..1317e5a 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -12,6 +12,7 @@ common-obj-$(CONFIG_IOAPIC) += ioapic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o common-obj-$(CONFIG_OPENPIC) += openpic.o obj-$(CONFIG_APIC) += apic.o apic_common.o diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c new file mode 100644 index 0000000..5a10784 --- /dev/null +++ b/hw/intc/arm_gicv3_common.c @@ -0,0 +1,249 @@ +/* + * ARM GICv3 support - common bits of emulated and KVM kernel model + * + * Copyright (c) 2012 Linaro Limited + * Copyright (c) 2015 Huawei. + * Written by Peter Maydell + * Extended to 64 cores by Shlomo Pongratz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "gicv3_internal.h" + +static void gicv3_pre_save(void *opaque) +{ + GICv3State *s = (GICv3State *)opaque; + ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s); + + if (c->pre_save) { + c->pre_save(s); + } +} + +static int gicv3_post_load(void *opaque, int version_id) +{ + GICv3State *s = (GICv3State *)opaque; + ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s); + + if (c->post_load) { + c->post_load(s); + } + return 0; +} + +static const VMStateDescription vmstate_gicv3_irq_state = { + .name = "arm_gicv3_irq_state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(pending, gicv3_irq_state), + VMSTATE_UINT64(active, gicv3_irq_state), + VMSTATE_UINT64(level, gicv3_irq_state), + VMSTATE_UINT64(group, gicv3_irq_state), + VMSTATE_BOOL(edge_trigger, gicv3_irq_state), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_gicv3_sgi_state = { + .name = "arm_gicv3_sgi_state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(pending, gicv3_sgi_state, GICV3_NCPU), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_gicv3 = { + .name = "arm_gicv3", + .version_id = 7, + .minimum_version_id = 7, + .pre_save = gicv3_pre_save, + .post_load = gicv3_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctlr, GICv3State), + VMSTATE_UINT32_ARRAY(cpu_ctlr, GICv3State, GICV3_NCPU), + VMSTATE_STRUCT_ARRAY(irq_state, GICv3State, GICV3_MAXIRQ, 1, + vmstate_gicv3_irq_state, gicv3_irq_state), + VMSTATE_UINT64_ARRAY(irq_target, GICv3State, GICV3_MAXIRQ), + VMSTATE_UINT8_2DARRAY(priority1, GICv3State, GIC_INTERNAL, + GICV3_NCPU), + VMSTATE_UINT8_ARRAY(priority2, GICv3State, + GICV3_MAXIRQ - GIC_INTERNAL), + VMSTATE_UINT16_2DARRAY(last_active, GICv3State, GICV3_MAXIRQ, + GICV3_NCPU), + VMSTATE_STRUCT_ARRAY(sgi_state, GICv3State, GIC_NR_SGIS, 1, + vmstate_gicv3_sgi_state, gicv3_sgi_state), + VMSTATE_UINT16_ARRAY(priority_mask, GICv3State, GICV3_NCPU), + VMSTATE_UINT16_ARRAY(running_irq, GICv3State, GICV3_NCPU), + VMSTATE_UINT16_ARRAY(running_priority, GICv3State, GICV3_NCPU), + VMSTATE_UINT16_ARRAY(current_pending, GICv3State, GICV3_NCPU), + VMSTATE_END_OF_LIST() + } +}; + +void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, + const MemoryRegionOps *ops) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int i; + + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. + * GPIO array layout is thus: + * [0..N-1] spi + * [N..N+31] PPIs for CPU 0 + * [N+32..N+63] PPIs for CPU 1 + * ... + */ + i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu; + qdev_init_gpio_in(DEVICE(s), handler, i); + + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_irq[i]); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_fiq[i]); + } + + memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, + "gicv3_dist", 0x10000); + memory_region_init_io(&s->iomem_lpi, OBJECT(s), ops ? &ops[1] : NULL, s, + "gicv3_redist", 0x800000); + + sysbus_init_mmio(sbd, &s->iomem_dist); + sysbus_init_mmio(sbd, &s->iomem_lpi); +} + +static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) +{ + GICv3State *s = ARM_GICV3_COMMON(dev); + int num_irq = s->num_irq; + + if (s->num_cpu > GICV3_NCPU) { + error_setg(errp, "requested %u CPUs exceeds GIC maximum %d", + s->num_cpu, GICV3_NCPU); + return; + } + s->num_irq += GICV3_BASE_IRQ; + if (s->num_irq > GICV3_MAXIRQ) { + error_setg(errp, + "requested %u interrupt lines exceeds GIC maximum %d", + num_irq, GICV3_MAXIRQ); + return; + } + /* ITLinesNumber is represented as (N / 32) - 1 (see + * gic_dist_readb) so this is an implementation imposed + * restriction, not an architectural one: + */ + if (s->num_irq < 32 || (s->num_irq % 32)) { + error_setg(errp, + "%d interrupt lines unsupported: not divisible by 32", + num_irq); + return; + } +} + +static void arm_gicv3_common_reset(DeviceState *dev) +{ + GICv3State *s = ARM_GICV3_COMMON(dev); + int i; + + /* Note num_cpu and num_irq are properties */ + + /* Don't reset anything assigned in arm_gic_realize or any property */ + + /* No GICv2 backwards computability support */ + for (i = 0; i < s->num_cpu; i++) { + s->priority_mask[i] = 0; + s->current_pending[i] = 1023; + s->running_irq[i] = 1023; + s->running_priority[i] = 0x100; + s->cpu_ctlr[i] = 0; + } + + memset(s->irq_state, 0, sizeof(s->irq_state)); + /* GIC-500 comment 'j' SGI are always enabled */ + for (i = 0; i < GIC_NR_SGIS; i++) { + GIC_SET_ENABLED(i, ALL_CPU_MASK); + GIC_SET_EDGE_TRIGGER(i); + } + memset(s->sgi_state, 0, sizeof(s->sgi_state)); + memset(s->irq_target, 0, sizeof(s->irq_target)); + if (s->num_cpu == 1) { + /* For uniprocessor GICs all interrupts always target the sole CPU */ + for (i = 0; i < GICV3_MAXIRQ; i++) { + s->irq_target[i] = 1; + } + } + memset(s->priority1, 0, sizeof(s->priority1)); + memset(s->priority2, 0, sizeof(s->priority2)); + memset(s->last_active, 0, sizeof(s->last_active)); + + /* With all configuration we don't support GICv2 backwards computability */ + if (s->security_levels > 1) { + /* GICv3 5.3.20 With two security So DS is RAZ/WI ARE_NS is RAO/WI + * and ARE_S is RAO/WI + */ + s->ctlr = GICD_CTLR_ARE_S | GICD_CTLR_ARE_NS; + } else { + /* GICv3 5.3.20 With one security So DS is RAO/WI ARE is RAO/WI + */ + s->ctlr = GICD_CTLR_DS | GICD_CTLR_ARE; + } + /* Workaround! + * Linux (drivers/irqchip/irq-gic-v3.c) is enabling only group one, + * in gic_cpu_sys_reg_init it calls gic_write_grpen1(1); + * but it doesn't conigure any interrupt to be in group one + */ + for (i = 0; i < s->num_irq; i++) { + GIC_SET_GROUP(i, ALL_CPU_MASK); + } +} + +static Property arm_gicv3_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1), + DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), + DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), + /* How many security levels are supported */ + DEFINE_PROP_BOOL("security-levels", GICv3State, security_levels, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = arm_gicv3_common_reset; + dc->realize = arm_gicv3_common_realize; + dc->props = arm_gicv3_common_properties; + dc->vmsd = &vmstate_gicv3; +} + +static const TypeInfo arm_gicv3_common_type = { + .name = TYPE_ARM_GICV3_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GICv3State), + .class_size = sizeof(ARMGICv3CommonClass), + .class_init = arm_gicv3_common_class_init, + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&arm_gicv3_common_type); +} + +type_init(register_types) diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h new file mode 100644 index 0000000..589baca --- /dev/null +++ b/hw/intc/gicv3_internal.h @@ -0,0 +1,156 @@ +/* + * ARM GIC support - internal interfaces + * + * Copyright (c) 2012 Linaro Limited + * Copyright (c) 2015 Huawei. + * Written by Peter Maydell + * Extended to 64 cores by Shlomo Pongratz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QEMU_ARM_GICV3_INTERNAL_H +#define QEMU_ARM_GICV3_INTERNAL_H + +#include "hw/intc/arm_gicv3_common.h" + +#define ALL_CPU_MASK ((uint64_t) (0xffffffffffffffff)) + +/* Keep this macro so it will be easy to compare the code to GICv2 code. + * The compiler will optimize any +/- operation involving this macro + */ +#define GICV3_BASE_IRQ (0) + +#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) +#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) +#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) +#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) +#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) +#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) +#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) +#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) +#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level |= (cm) +#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) +#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) +#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true +#define GIC_CLEAR_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = false +#define GIC_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger) +#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ + s->priority1[irq][cpu] : \ + s->priority2[(irq) - GIC_INTERNAL]) +#define GIC_TARGET(irq) s->irq_target[irq] +#define GIC_CLEAR_GROUP(irq, cm) (s->irq_state[irq].group &= ~(cm)) +#define GIC_SET_GROUP(irq, cm) (s->irq_state[irq].group |= (cm)) +#define GIC_TEST_GROUP(irq, cm) ((s->irq_state[irq].group & (cm)) != 0) + +static inline bool gic_test_pending(GICv3State *s, int irq, uint64_t cm) +{ + /* Edge-triggered interrupts are marked pending on a rising edge, but + * level-triggered interrupts are either considered pending when the + * level is active or if software has explicitly written to + * GICD_ISPENDR to set the state pending. + */ + return (s->irq_state[irq].pending & cm) || + (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm)); +} + +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_STATUSR 0x0010 +#define GICD_SETSPI_NSR 0x0040 +#define GICD_CLRSPI_NSR 0x0048 +#define GICD_SETSPI_SR 0x0050 +#define GICD_CLRSPI_SR 0x0058 +#define GICD_SEIR 0x0068 +#define GICD_ISENABLER 0x0100 +#define GICD_ICENABLER 0x0180 +#define GICD_ISPENDR 0x0200 +#define GICD_ICPENDR 0x0280 +#define GICD_ISACTIVER 0x0300 +#define GICD_ICACTIVER 0x0380 +#define GICD_IPRIORITYR 0x0400 +#define GICD_ICFGR 0x0C00 +#define GICD_IROUTER 0x6000 +#define GICD_PIDR2 0xFFE8 + +/* GICD_CTLR fields */ +#define GICD_CTLR_EN_GRP0 (1U << 0) +#define GICD_CTLR_EN_GRP1NS (1U << 1) /* GICv3 5.3.20 */ +#define GICD_CTLR_EN_GRP1S (1U << 2) +#define GICD_CTLR_EN_GRP1_ALL (GICD_CTLR_EN_GRP1NS | GICD_CTLR_EN_GRP1S) +#define GICD_CTLR_ARE (1U << 4) +#define GICD_CTLR_ARE_S (1U << 4) +#define GICD_CTLR_ARE_NS (1U << 5) +#define GICD_CTLR_DS (1U << 6) +#define GICD_CTLR_RWP (1U << 31) + +#define GICD_IROUTER_SPI_MODE_ONE (0U << 31) +#define GICD_IROUTER_SPI_MODE_ANY (1U << 31) + +#define GIC_PIDR2_ARCH_MASK 0xf0 +#define GIC_PIDR2_ARCH_GICv3 0x30 +#define GIC_PIDR2_ARCH_GICv4 0x40 + +/* + * Re-Distributor registers, offsets from RD_base + */ +#define GICR_CTLR GICD_CTLR +#define GICR_IIDR 0x0004 +#define GICR_TYPER 0x0008 +#define GICR_STATUSR GICD_STATUSR +#define GICR_WAKER 0x0014 +#define GICR_SETLPIR 0x0040 +#define GICR_CLRLPIR 0x0048 +#define GICR_SEIR GICD_SEIR +#define GICR_PROPBASER 0x0070 +#define GICR_PENDBASER 0x0078 +#define GICR_INVLPIR 0x00A0 +#define GICR_INVALLR 0x00B0 +#define GICR_SYNCR 0x00C0 +#define GICR_MOVLPIR 0x0100 +#define GICR_MOVALLR 0x0110 +#define GICR_PIDR2 GICD_PIDR2 + +#define GICR_WAKER_ProcessorSleep (1U << 1) +#define GICR_WAKER_ChildrenAsleep (1U << 2) + +/* + * Re-Distributor registers, offsets from SGI_base + */ +#define GICR_ISENABLER0 GICD_ISENABLER +#define GICR_ICENABLER0 GICD_ICENABLER +#define GICR_ISPENDR0 GICD_ISPENDR +#define GICR_ICPENDR0 GICD_ICPENDR +#define GICR_ISACTIVER0 GICD_ISACTIVER +#define GICR_ICACTIVER0 GICD_ICACTIVER +#define GICR_IPRIORITYR0 GICD_IPRIORITYR +#define GICR_ICFGR0 GICD_ICFGR + +#define GICR_TYPER_VLPIS (1U << 1) +#define GICR_TYPER_LAST (1U << 4) + +/* + * Simulated used system registers + */ +#define GICC_CTLR_EN_GRP0 (1U << 0) +#define GICC_CTLR_EN_GRP1 (1U << 1) +#define GICC_CTLR_ACK_CTL (1U << 2) +#define GICC_CTLR_FIQ_EN (1U << 3) +#define GICC_CTLR_CBPR (1U << 4) /* GICv1: SBPR */ +#define GICC_CTLR_EOIMODE (1U << 9) +#define GICC_CTLR_EOIMODE_NS (1U << 10) + +#endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h new file mode 100644 index 0000000..c8cc24e --- /dev/null +++ b/include/hw/intc/arm_gicv3_common.h @@ -0,0 +1,112 @@ +/* + * ARM GIC support + * + * Copyright (c) 2012 Linaro Limited + * Copyright (c) 2015 Huawei. + * Written by Peter Maydell + * Extended to 64 cores by Shlomo Pongratz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_ARM_GICV3_COMMON_H +#define HW_ARM_GICV3_COMMON_H + +#include "hw/sysbus.h" +#include "hw/intc/arm_gic_common.h" + +/* Maximum number of possible interrupts, determined by the GIC architecture */ +#define GICV3_MAXIRQ 1020 +#define GICV3_NCPU 64 + +typedef struct gicv3_irq_state { + /* The enable bits are only banked for per-cpu interrupts. */ + uint64_t enabled; + uint64_t pending; + uint64_t active; + uint64_t level; + uint64_t group; + bool edge_trigger; /* true: edge-triggered, false: level-triggered */ +} gicv3_irq_state; + +typedef struct gicv3_sgi_state { + uint64_t pending[GICV3_NCPU]; +} gicv3_sgi_state; + +typedef struct GICv3State { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq parent_irq[GICV3_NCPU]; + qemu_irq parent_fiq[GICV3_NCPU]; + /* GICD_CTLR; for a GIC with the security extensions the NS banked version + * of this register is just an alias of bit 1 of the S banked version. + */ + uint32_t ctlr; + /* Sim GICC_CTLR; again, the NS banked version is just aliases of bits of + * the S banked register, so our state only needs to store the S version. + */ + uint32_t cpu_ctlr[GICV3_NCPU]; + bool cpu_enabled[GICV3_NCPU]; + + gicv3_irq_state irq_state[GICV3_MAXIRQ]; + uint64_t irq_target[GICV3_MAXIRQ]; + uint8_t priority1[GIC_INTERNAL][GICV3_NCPU]; + uint8_t priority2[GICV3_MAXIRQ - GIC_INTERNAL]; + uint16_t last_active[GICV3_MAXIRQ][GICV3_NCPU]; + /* For each SGI on the target CPU, we store 64 bits + * indicating which source CPUs have made this SGI + * pending on the target CPU. These correspond to + * the bytes in the GIC_SPENDSGIR* registers as + * read by the target CPU. + */ + gicv3_sgi_state sgi_state[GIC_NR_SGIS]; + + uint16_t priority_mask[GICV3_NCPU]; + uint16_t running_irq[GICV3_NCPU]; + uint16_t running_priority[GICV3_NCPU]; + uint16_t current_pending[GICV3_NCPU]; + + uint32_t cpu_mask; /* For redistributor */ + uint32_t num_cpu; + MemoryRegion iomem_dist; /* Distributor */ + MemoryRegion iomem_lpi; /* Redistributor */ + uint32_t num_irq; + uint32_t revision; + bool security_levels; + int dev_fd; /* kvm device fd if backed by kvm vgic support */ +} GICv3State; + +#define TYPE_ARM_GICV3_COMMON "arm_gicv3_common" +#define ARM_GICV3_COMMON(obj) \ + OBJECT_CHECK(GICv3State, (obj), TYPE_ARM_GICV3_COMMON) +#define ARM_GICV3_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICv3CommonClass, (klass), TYPE_ARM_GICV3_COMMON) +#define ARM_GICV3_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICv3CommonClass, (obj), TYPE_ARM_GICV3_COMMON) + +typedef struct ARMGICv3CommonClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + void (*pre_save)(GICv3State *s); + void (*post_load)(GICv3State *s); +} ARMGICv3CommonClass; + +void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, + const MemoryRegionOps *ops); + +#endif -- 1.9.5.msysgit.0