From dda4c03ff0932c54feedad7e34b700eb9b424040 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 5 Aug 2015 12:05:20 +0200 Subject: [PATCH] arm: Migrate irqchips to generic MMIO dispatcher Register the GIC distributor and, for the GICv3, also the redistributor regions with the generic MMIO dispatcher. This allows to drop the GIC- specific MMIO dispatching from arch_handle_dabt. Signed-off-by: Jan Kiszka --- hypervisor/arch/arm/gic-common.c | 86 ++++++++++---------- hypervisor/arch/arm/gic-v2.c | 28 ++++--- hypervisor/arch/arm/gic-v3.c | 46 ++++------- hypervisor/arch/arm/include/asm/gic_common.h | 2 +- hypervisor/arch/arm/include/asm/irqchip.h | 4 +- hypervisor/arch/arm/include/asm/mmio.h | 2 +- hypervisor/arch/arm/irqchip.c | 8 -- hypervisor/arch/arm/mmio.c | 17 ++-- 8 files changed, 83 insertions(+), 110 deletions(-) diff --git a/hypervisor/arch/arm/gic-common.c b/hypervisor/arch/arm/gic-common.c index 87c1102..c51238c 100644 --- a/hypervisor/arch/arm/gic-common.c +++ b/hypervisor/arch/arm/gic-common.c @@ -40,10 +40,9 @@ static u8 target_cpu_map[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; * Others, such as the priority registers, will need to be read and written back * with a restricted value, by using the distributor lock. */ -static int restrict_bitmask_access(struct mmio_access *mmio, - unsigned int reg_index, - unsigned int bits_per_irq, - bool is_poke) +static enum mmio_result +restrict_bitmask_access(struct mmio_access *mmio, unsigned int reg_index, + unsigned int bits_per_irq, bool is_poke) { struct cell *cell = this_cell(); unsigned int spi; @@ -58,8 +57,10 @@ static int restrict_bitmask_access(struct mmio_access *mmio, unsigned int first_irq = reg_index * irqs_per_reg; /* For SGIs or PPIs, let the caller do the mmio access */ - if (!is_spi(first_irq)) - return TRAP_UNHANDLED; + if (!is_spi(first_irq)) { + arm_mmio_perform_access((unsigned long)gicd_base, mmio); + return MMIO_HANDLED; + } /* For SPIs, compare against the cell config mask */ first_irq -= 32; @@ -71,9 +72,9 @@ static int restrict_bitmask_access(struct mmio_access *mmio, if (!mmio->is_write) { /* Restrict the read value */ - arm_mmio_perform_access(mmio); + arm_mmio_perform_access((unsigned long)gicd_base, mmio); mmio->value &= access_mask; - return TRAP_HANDLED; + return MMIO_HANDLED; } if (!is_poke) { @@ -87,39 +88,38 @@ static int restrict_bitmask_access(struct mmio_access *mmio, spin_lock(&dist_lock); mmio->is_write = false; - arm_mmio_perform_access(mmio); + arm_mmio_perform_access((unsigned long)gicd_base, mmio); mmio->is_write = true; /* Clear 0 bits */ mmio->value &= ~(access_mask & ~access_val); mmio->value |= access_val; - arm_mmio_perform_access(mmio); + arm_mmio_perform_access((unsigned long)gicd_base, mmio); spin_unlock(&dist_lock); - - return TRAP_HANDLED; } else { mmio->value &= access_mask; - /* Do the access */ - return TRAP_UNHANDLED; + arm_mmio_perform_access((unsigned long)gicd_base, mmio); } + return MMIO_HANDLED; } /* * GICv3 uses a 64bit register IROUTER for each IRQ */ -static int handle_irq_route(struct mmio_access *mmio, unsigned int irq) +static enum mmio_result handle_irq_route(struct mmio_access *mmio, + unsigned int irq) { struct cell *cell = this_cell(); unsigned int cpu; /* Ignore aff3 on AArch32 (return 0) */ if (mmio->size == 4 && (mmio->address % 8)) - return TRAP_HANDLED; + return MMIO_HANDLED; /* SGIs and PPIs are res0 */ if (!is_spi(irq)) - return TRAP_HANDLED; + return MMIO_HANDLED; /* * Ignore accesses to SPIs that do not belong to the cell. This isn't @@ -127,28 +127,28 @@ static int handle_irq_route(struct mmio_access *mmio, unsigned int irq) * registers at initialisation */ if (!spi_in_cell(cell, irq - 32)) - return TRAP_HANDLED; + return MMIO_HANDLED; /* Translate the virtual cpu id into the physical one */ if (mmio->is_write) { mmio->value = arm_cpu_virt2phys(cell, mmio->value); if (mmio->value == -1) { printk("Attempt to route IRQ%d outside of cell\n", irq); - return TRAP_FORBIDDEN; + return MMIO_ERROR; } - /* And do the access */ - return TRAP_UNHANDLED; + arm_mmio_perform_access((unsigned long)gicd_base, mmio); } else { cpu = mmio_read32(gicd_base + GICD_IROUTER + 8 * irq); mmio->value = arm_cpu_phys2virt(cpu); - return TRAP_HANDLED; } + return MMIO_HANDLED; } /* * GICv2 uses 8bit values for each IRQ in the ITARGETRs registers */ -static int handle_irq_target(struct mmio_access *mmio, unsigned int reg) +static enum mmio_result handle_irq_target(struct mmio_access *mmio, + unsigned int reg) { /* * ITARGETSR contain one byte per IRQ, so the first one affected by this @@ -165,8 +165,10 @@ static int handle_irq_target(struct mmio_access *mmio, unsigned int reg) * Let the guest freely access its SGIs and PPIs, which may be used to * fill its CPU interface map. */ - if (!is_spi(reg)) - return TRAP_UNHANDLED; + if (!is_spi(reg)) { + arm_mmio_perform_access((unsigned long)gicd_base, mmio); + return MMIO_HANDLED; + } /* * The registers are byte-accessible, extend the access to a word if @@ -197,7 +199,7 @@ static int handle_irq_target(struct mmio_access *mmio, unsigned int reg) continue; printk("Attempt to route SPI%d outside of cell\n", spi); - return TRAP_FORBIDDEN; + return MMIO_ERROR; } } @@ -209,23 +211,23 @@ static int handle_irq_target(struct mmio_access *mmio, unsigned int reg) /* Combine with external SPIs */ mmio->value |= (itargetsr & ~access_mask); /* And do the access */ - arm_mmio_perform_access(mmio); + arm_mmio_perform_access((unsigned long)gicd_base, mmio); spin_unlock(&dist_lock); } else { - arm_mmio_perform_access(mmio); + arm_mmio_perform_access((unsigned long)gicd_base, mmio); mmio->value &= access_mask; } - return TRAP_HANDLED; + return MMIO_HANDLED; } -static int handle_sgir_access(struct mmio_access *mmio) +static enum mmio_result handle_sgir_access(struct mmio_access *mmio) { struct sgi sgi; unsigned long val = mmio->value; if (!mmio->is_write) - return TRAP_HANDLED; + return MMIO_HANDLED; sgi.targets = (val >> 16) & 0xff; sgi.routing_mode = (val >> 24) & 0x3; @@ -235,7 +237,7 @@ static int handle_sgir_access(struct mmio_access *mmio) sgi.id = val & 0xf; gic_handle_sgir_write(&sgi, false); - return TRAP_HANDLED; + return MMIO_HANDLED; } /* @@ -299,10 +301,10 @@ void gic_handle_sgir_write(struct sgi *sgi, bool virt_input) irqchip_send_sgi(sgi); } -int gic_handle_dist_access(struct mmio_access *mmio) +enum mmio_result gic_handle_dist_access(void *arg, struct mmio_access *mmio) { - int ret; - unsigned long reg = mmio->address - (unsigned long)gicd_base; + unsigned long reg = mmio->address; + enum mmio_result ret; switch (reg) { case REG_RANGE(GICD_IROUTER, 1024, 8): @@ -346,18 +348,12 @@ int gic_handle_dist_access(struct mmio_access *mmio) case REG_RANGE(GICD_PIDR4, 4, 4): case REG_RANGE(GICD_CIDR0, 4, 4): /* Allow read access, ignore write */ - ret = (mmio->is_write ? TRAP_HANDLED : TRAP_UNHANDLED); - break; - + if (!mmio->is_write) + arm_mmio_perform_access((unsigned long)gicd_base, mmio); + /* fall through */ default: /* Ignore access. */ - ret = TRAP_HANDLED; - } - - /* The sub-handlers return TRAP_UNHANDLED to allow the access */ - if (ret == TRAP_UNHANDLED) { - arm_mmio_perform_access(mmio); - ret = TRAP_HANDLED; + ret = MMIO_HANDLED; } return ret; diff --git a/hypervisor/arch/arm/gic-v2.c b/hypervisor/arch/arm/gic-v2.c index 02f89cd..9eb6bde 100644 --- a/hypervisor/arch/arm/gic-v2.c +++ b/hypervisor/arch/arm/gic-v2.c @@ -171,6 +171,8 @@ static void gic_eoi_irq(u32 irq_id, bool deactivate) static int gic_cell_init(struct cell *cell) { + int err; + /* * target_cpu_map has not been populated by all available CPUs when the * setup code initialises the root cell. It is assumed that the kernel @@ -189,11 +191,17 @@ static int gic_cell_init(struct cell *cell) * here. * As for now, none of them seem to have virtualization extensions. */ - return paging_create(&cell->arch.mm, (unsigned long)gicv_base, - gicc_size, (unsigned long)gicc_base, - (PTE_FLAG_VALID | PTE_ACCESS_FLAG | - S2_PTE_ACCESS_RW | S2_PTE_FLAG_DEVICE), - PAGING_NON_COHERENT); + err = paging_create(&cell->arch.mm, (unsigned long)gicv_base, + gicc_size, (unsigned long)gicc_base, + (PTE_FLAG_VALID | PTE_ACCESS_FLAG | + S2_PTE_ACCESS_RW | S2_PTE_FLAG_DEVICE), + PAGING_NON_COHERENT); + if (err) + return err; + + mmio_region_register(cell, (unsigned long)gicd_base, gicd_size, + gic_handle_dist_access, NULL); + return 0; } static void gic_cell_exit(struct cell *cell) @@ -271,14 +279,9 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq) return 0; } -static int gic_mmio_access(struct mmio_access *mmio) +unsigned int irqchip_mmio_count_regions(struct cell *cell) { - void *address = (void *)mmio->address; - - if (address >= gicd_base && address < gicd_base + gicd_size) - return gic_handle_dist_access(mmio); - - return TRAP_UNHANDLED; + return 1; } struct irqchip_ops gic_irqchip = { @@ -292,5 +295,4 @@ struct irqchip_ops gic_irqchip = { .handle_irq = gic_handle_irq, .inject_irq = gic_inject_irq, .eoi_irq = gic_eoi_irq, - .mmio_access = gic_mmio_access, }; diff --git a/hypervisor/arch/arm/gic-v3.c b/hypervisor/arch/arm/gic-v3.c index a0f60cb..3a759f9 100644 --- a/hypervisor/arch/arm/gic-v3.c +++ b/hypervisor/arch/arm/gic-v3.c @@ -213,17 +213,16 @@ static void gic_route_spis(struct cell *config_cell, struct cell *dest_cell) } } -static int gic_handle_redist_access(struct mmio_access *mmio) +static enum mmio_result gic_handle_redist_access(void *arg, + struct mmio_access *mmio) { struct cell *cell = this_cell(); unsigned int cpu; - unsigned int reg; - int ret = TRAP_UNHANDLED; unsigned int virt_id; void *virt_redist = 0; void *phys_redist = 0; unsigned int redist_size = (gic_version == 4) ? 0x40000 : 0x20000; - void *address = (void *)mmio->address; + void *address = (void *)(mmio->address + (unsigned long)gicr_base); /* * The redistributor accessed by the cell is not the one stored in these @@ -241,14 +240,13 @@ static int gic_handle_redist_access(struct mmio_access *mmio) } if (phys_redist == NULL) - return TRAP_FORBIDDEN; + return MMIO_ERROR; - reg = address - virt_redist; - mmio->address = (unsigned long)phys_redist + reg; + mmio->address = address - virt_redist; /* Change the ID register, all other accesses are allowed. */ if (!mmio->is_write) { - switch (reg) { + switch (mmio->address) { case GICR_TYPER: if (virt_id == cell->arch.last_virt_id) mmio->value = GICR_TYPER_Last; @@ -258,25 +256,26 @@ static int gic_handle_redist_access(struct mmio_access *mmio) if (mmio->size == 8) mmio->value |= (u64)virt_id << 32; - ret = TRAP_HANDLED; - break; + return MMIO_HANDLED; case GICR_TYPER + 4: /* Upper bits contain the affinity */ mmio->value = virt_id; - ret = TRAP_HANDLED; - break; + return MMIO_HANDLED; } } - if (ret == TRAP_HANDLED) - return ret; - - arm_mmio_perform_access(mmio); - return TRAP_HANDLED; + arm_mmio_perform_access((unsigned long)phys_redist, mmio); + return MMIO_HANDLED; } static int gic_cell_init(struct cell *cell) { gic_route_spis(cell, cell); + + mmio_region_register(cell, (unsigned long)gicd_base, gicd_size, + gic_handle_dist_access, NULL); + mmio_region_register(cell, (unsigned long)gicr_base, gicr_size, + gic_handle_redist_access, NULL); + return 0; } @@ -400,17 +399,9 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq) return 0; } -static int gic_mmio_access(struct mmio_access *mmio) +unsigned int irqchip_mmio_count_regions(struct cell *cell) { - void *address = (void *)mmio->address; - - if (address >= gicd_base && address < gicd_base + gicd_size) - return gic_handle_dist_access(mmio); - - if (address >= gicr_base && address < gicr_base + gicr_size) - return gic_handle_redist_access(mmio); - - return TRAP_UNHANDLED; + return 2; } struct irqchip_ops gic_irqchip = { @@ -423,5 +414,4 @@ struct irqchip_ops gic_irqchip = { .handle_irq = gic_handle_irq, .inject_irq = gic_inject_irq, .eoi_irq = gic_eoi_irq, - .mmio_access = gic_mmio_access, }; diff --git a/hypervisor/arch/arm/include/asm/gic_common.h b/hypervisor/arch/arm/include/asm/gic_common.h index a49fbfe..0381194 100644 --- a/hypervisor/arch/arm/include/asm/gic_common.h +++ b/hypervisor/arch/arm/include/asm/gic_common.h @@ -49,7 +49,7 @@ struct per_cpu; struct sgi; int gic_probe_cpu_id(unsigned int cpu); -int gic_handle_dist_access(struct mmio_access *access); +enum mmio_result gic_handle_dist_access(void *arg, struct mmio_access *mmio); void gic_handle_sgir_write(struct sgi *sgi, bool virt_input); void gic_handle_irq(struct per_cpu *cpu_data); void gic_target_spis(struct cell *config_cell, struct cell *dest_cell); diff --git a/hypervisor/arch/arm/include/asm/irqchip.h b/hypervisor/arch/arm/include/asm/irqchip.h index cf12956..43c42ad 100644 --- a/hypervisor/arch/arm/include/asm/irqchip.h +++ b/hypervisor/arch/arm/include/asm/irqchip.h @@ -81,6 +81,8 @@ struct pending_irq { struct pending_irq *prev; } __attribute__((packed)); +unsigned int irqchip_mmio_count_regions(struct cell *cell); + int irqchip_init(void); int irqchip_cpu_init(struct per_cpu *cpu_data); int irqchip_cpu_reset(struct per_cpu *cpu_data); @@ -94,8 +96,6 @@ int irqchip_send_sgi(struct sgi *sgi); void irqchip_handle_irq(struct per_cpu *cpu_data); void irqchip_eoi_irq(u32 irqn, bool deactivate); -int irqchip_mmio_access(struct mmio_access *access); - int irqchip_inject_pending(struct per_cpu *cpu_data); int irqchip_insert_pending(struct per_cpu *cpu_data, struct pending_irq *irq); int irqchip_remove_pending(struct per_cpu *cpu_data, struct pending_irq *irq); diff --git a/hypervisor/arch/arm/include/asm/mmio.h b/hypervisor/arch/arm/include/asm/mmio.h index fbae2b1..1d44541 100644 --- a/hypervisor/arch/arm/include/asm/mmio.h +++ b/hypervisor/arch/arm/include/asm/mmio.h @@ -12,4 +12,4 @@ struct mmio_access; -void arm_mmio_perform_access(struct mmio_access *mmio); +void arm_mmio_perform_access(unsigned long base, struct mmio_access *mmio); diff --git a/hypervisor/arch/arm/irqchip.c b/hypervisor/arch/arm/irqchip.c index 5c07421..2d7840e 100644 --- a/hypervisor/arch/arm/irqchip.c +++ b/hypervisor/arch/arm/irqchip.c @@ -237,14 +237,6 @@ void irqchip_cpu_shutdown(struct per_cpu *cpu_data) irqchip.cpu_reset(cpu_data, true); } -int irqchip_mmio_access(struct mmio_access *access) -{ - if (irqchip.mmio_access) - return irqchip.mmio_access(access); - - return TRAP_UNHANDLED; -} - static const struct jailhouse_irqchip * irqchip_find_config(struct jailhouse_cell_desc *config) { diff --git a/hypervisor/arch/arm/mmio.c b/hypervisor/arch/arm/mmio.c index b89ffba..61f2adc 100644 --- a/hypervisor/arch/arm/mmio.c +++ b/hypervisor/arch/arm/mmio.c @@ -18,7 +18,7 @@ unsigned int arch_mmio_count_regions(struct cell *cell) { - return smp_mmio_regions; + return irqchip_mmio_count_regions(cell) + smp_mmio_regions; } /* Taken from the ARM ARM pseudocode for taking a data abort */ @@ -60,9 +60,9 @@ static void arch_inject_dabt(struct trap_context *ctx, unsigned long addr) arm_write_sysreg(DFAR, addr); } -void arm_mmio_perform_access(struct mmio_access *mmio) +void arm_mmio_perform_access(unsigned long base, struct mmio_access *mmio) { - void *addr = (void *)mmio->address; + void *addr = (void *)(base + mmio->address); if (mmio->is_write) switch (mmio->size) { @@ -99,7 +99,6 @@ int arch_handle_dabt(struct trap_context *ctx) struct mmio_access mmio; unsigned long hpfar; unsigned long hdfar; - int ret = TRAP_UNHANDLED; /* Decode the syndrome fields */ u32 icc = ESR_ICC(ctx->esr); u32 isv = icc >> 24; @@ -146,14 +145,8 @@ int arch_handle_dabt(struct trap_context *ctx) mmio_result = mmio_handle_access(&mmio); if (mmio_result == MMIO_ERROR) return TRAP_FORBIDDEN; - if (mmio_result == MMIO_UNHANDLED) { - ret = irqchip_mmio_access(&mmio); - - if (ret == TRAP_FORBIDDEN) - return TRAP_FORBIDDEN; - if (ret == TRAP_UNHANDLED) - goto error_unhandled; - } + if (mmio_result == MMIO_UNHANDLED) + goto error_unhandled; /* Put the read value into the dest register */ if (!is_write) { -- 2.39.2