diff --git a/hypervisor/arch/arm-common/gic-common.c b/hypervisor/arch/arm-common/gic-common.c index 67dac8b0c..0fef7745c 100644 --- a/hypervisor/arch/arm-common/gic-common.c +++ b/hypervisor/arch/arm-common/gic-common.c @@ -24,7 +24,7 @@ #define REG_RANGE(base, n, size) \ (base) ... ((base) + (n - 1) * (size)) -static DEFINE_SPINLOCK(dist_lock); +DEFINE_SPINLOCK(dist_lock); /* The GICv2 interface numbering does not necessarily match the logical map */ u8 gicv2_target_cpu_map[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -103,76 +103,6 @@ restrict_bitmask_access(struct mmio_access *mmio, unsigned int reg_index, return MMIO_HANDLED; } -/* - * GICv2 uses 8bit values for each IRQ in the ITARGETRs registers - */ -static enum mmio_result handle_irq_target(struct mmio_access *mmio, - unsigned int irq) -{ - /* - * ITARGETSR contain one byte per IRQ, so the first one affected by this - * access corresponds to the reg index - */ - unsigned int irq_base = irq & ~0x3; - struct cell *cell = this_cell(); - unsigned int offset; - u32 access_mask = 0; - unsigned int n; - u8 targets; - - /* - * Let the guest freely access its SGIs and PPIs, which may be used to - * fill its CPU interface map. - */ - if (!is_spi(irq)) { - mmio_perform_access(gicd_base, mmio); - return MMIO_HANDLED; - } - - /* - * The registers are byte-accessible, but we always do word accesses. - */ - offset = irq % 4; - mmio->address &= ~0x3; - mmio->value <<= 8 * offset; - mmio->size = 4; - - for (n = 0; n < 4; n++) { - if (irqchip_irq_in_cell(cell, irq_base + n)) - access_mask |= 0xff << (8 * n); - else - continue; - - if (!mmio->is_write) - continue; - - targets = (mmio->value >> (8 * n)) & 0xff; - - if (!gic_targets_in_cell(cell, targets)) { - printk("Attempt to route IRQ%d outside of cell\n", - irq_base + n); - return MMIO_ERROR; - } - } - - if (mmio->is_write) { - spin_lock(&dist_lock); - u32 itargetsr = - mmio_read32(gicd_base + GICD_ITARGETSR + irq_base); - mmio->value &= access_mask; - /* Combine with external SPIs */ - mmio->value |= (itargetsr & ~access_mask); - /* And do the access */ - mmio_perform_access(gicd_base, mmio); - spin_unlock(&dist_lock); - } else { - mmio_perform_access(gicd_base, mmio); - mmio->value &= access_mask; - } - - return MMIO_HANDLED; -} - static enum mmio_result handle_sgir_access(struct mmio_access *mmio) { struct sgi sgi; @@ -269,7 +199,7 @@ enum mmio_result gic_handle_dist_access(void *arg, struct mmio_access *mmio) break; case REG_RANGE(GICD_ITARGETSR, 1024, 1): - ret = handle_irq_target(mmio, reg - GICD_ITARGETSR); + ret = irqchip.handle_irq_target(mmio, reg - GICD_ITARGETSR); break; case REG_RANGE(GICD_ICENABLER, 32, 4): diff --git a/hypervisor/arch/arm-common/gic-v2.c b/hypervisor/arch/arm-common/gic-v2.c index c3d858196..51c5c7e4a 100644 --- a/hypervisor/arch/arm-common/gic-v2.c +++ b/hypervisor/arch/arm-common/gic-v2.c @@ -11,7 +11,7 @@ */ #include -#include +#include #include #include #include @@ -291,6 +291,76 @@ enum mmio_result gic_handle_irq_route(struct mmio_access *mmio, return MMIO_HANDLED; } +/* + * GICv2 uses 8bit values for each IRQ in the ITARGETSR registers + */ +static enum mmio_result gic_handle_irq_target(struct mmio_access *mmio, + unsigned int irq) +{ + /* + * ITARGETSR contain one byte per IRQ, so the first one affected by this + * access corresponds to the reg index + */ + unsigned int irq_base = irq & ~0x3; + struct cell *cell = this_cell(); + unsigned int offset; + u32 access_mask = 0; + unsigned int n; + u8 targets; + + /* + * Let the guest freely access its SGIs and PPIs, which may be used to + * fill its CPU interface map. + */ + if (!is_spi(irq)) { + mmio_perform_access(gicd_base, mmio); + return MMIO_HANDLED; + } + + /* + * The registers are byte-accessible, but we always do word accesses. + */ + offset = irq % 4; + mmio->address &= ~0x3; + mmio->value <<= 8 * offset; + mmio->size = 4; + + for (n = 0; n < 4; n++) { + if (irqchip_irq_in_cell(cell, irq_base + n)) + access_mask |= 0xff << (8 * n); + else + continue; + + if (!mmio->is_write) + continue; + + targets = (mmio->value >> (8 * n)) & 0xff; + + if (!gic_targets_in_cell(cell, targets)) { + printk("Attempt to route IRQ%d outside of cell\n", + irq_base + n); + return MMIO_ERROR; + } + } + + if (mmio->is_write) { + spin_lock(&dist_lock); + u32 itargetsr = + mmio_read32(gicd_base + GICD_ITARGETSR + irq_base); + mmio->value &= access_mask; + /* Combine with external SPIs */ + mmio->value |= (itargetsr & ~access_mask); + /* And do the access */ + mmio_perform_access(gicd_base, mmio); + spin_unlock(&dist_lock); + } else { + mmio_perform_access(gicd_base, mmio); + mmio->value &= access_mask; + } + + return MMIO_HANDLED; +} + unsigned int irqchip_mmio_count_regions(struct cell *cell) { return 1; @@ -309,4 +379,6 @@ struct irqchip_ops irqchip = { .enable_maint_irq = gic_enable_maint_irq, .has_pending_irqs = gic_has_pending_irqs, .eoi_irq = gic_eoi_irq, + + .handle_irq_target = gic_handle_irq_target, }; diff --git a/hypervisor/arch/arm-common/include/asm/gic.h b/hypervisor/arch/arm-common/include/asm/gic.h index 44c50f94c..a1ee60edb 100644 --- a/hypervisor/arch/arm-common/include/asm/gic.h +++ b/hypervisor/arch/arm-common/include/asm/gic.h @@ -47,9 +47,12 @@ #define is_spi(irqn) ((irqn) > 31 && (irqn) < 1020) #ifndef __ASSEMBLY__ +extern struct irqchip_ops irqchip; + extern u8 gicv2_target_cpu_map[8]; extern void *gicd_base; +extern spinlock_t dist_lock; int gic_probe_cpu_id(unsigned int cpu); enum mmio_result gic_handle_dist_access(void *arg, struct mmio_access *mmio); diff --git a/hypervisor/arch/arm-common/include/asm/irqchip.h b/hypervisor/arch/arm-common/include/asm/irqchip.h index d9d1f6157..40984e031 100644 --- a/hypervisor/arch/arm-common/include/asm/irqchip.h +++ b/hypervisor/arch/arm-common/include/asm/irqchip.h @@ -52,6 +52,9 @@ struct irqchip_ops { int (*inject_irq)(struct per_cpu *cpu_data, u16 irq_id); void (*enable_maint_irq)(bool enable); bool (*has_pending_irqs)(void); + + enum mmio_result (*handle_irq_target)(struct mmio_access *mmio, + unsigned int irq); }; unsigned int irqchip_mmio_count_regions(struct cell *cell); diff --git a/hypervisor/arch/arm-common/irqchip.c b/hypervisor/arch/arm-common/irqchip.c index 28e453e36..794259d1e 100644 --- a/hypervisor/arch/arm-common/irqchip.c +++ b/hypervisor/arch/arm-common/irqchip.c @@ -32,8 +32,6 @@ (counter) < (config)->num_irqchips; \ (chip)++, (counter)++) -extern struct irqchip_ops irqchip; - void *gicd_base; /* diff --git a/hypervisor/arch/arm/gic-v3.c b/hypervisor/arch/arm/gic-v3.c index 3b10d0d87..9b17df965 100644 --- a/hypervisor/arch/arm/gic-v3.c +++ b/hypervisor/arch/arm/gic-v3.c @@ -432,6 +432,13 @@ static bool gicv3_has_pending_irqs(void) return false; } +static enum mmio_result gicv3_handle_irq_target(struct mmio_access *mmio, + unsigned int irq) +{ + /* ignore writes, we are in affinity routing mode */ + return MMIO_HANDLED; +} + unsigned int irqchip_mmio_count_regions(struct cell *cell) { return 2; @@ -448,4 +455,5 @@ struct irqchip_ops irqchip = { .enable_maint_irq = gicv3_enable_maint_irq, .has_pending_irqs = gicv3_has_pending_irqs, .eoi_irq = gic_eoi_irq, + .handle_irq_target = gicv3_handle_irq_target, };