From mboxrd@z Thu Jan 1 00:00:00 1970 From: vijay.kilari@gmail.com Subject: [PATCH v2 14/15] xen/arm: Add vgic support for GIC v3 Date: Fri, 4 Apr 2014 17:26:32 +0530 Message-ID: <1396612593-443-15-git-send-email-vijay.kilari@gmail.com> References: <1396612593-443-1-git-send-email-vijay.kilari@gmail.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1396612593-443-1-git-send-email-vijay.kilari@gmail.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: Ian.Campbell@citrix.com, julien.grall@linaro.org, stefano.stabellini@eu.citrix.com, stefano.stabellini@citrix.com, xen-devel@lists.xen.org Cc: Prasun.Kapoor@caviumnetworks.com, Vijaya Kumar K , vijay.kilari@gmail.com List-Id: xen-devel@lists.xenproject.org From: Vijaya Kumar K Add vgic driver support for GIC v3 version. Signed-off-by: Vijaya Kumar K --- xen/arch/arm/Makefile | 1 + xen/arch/arm/vgic-v3.c | 927 +++++++++++++++++++++++++++++++++++++++++++++ xen/include/asm-arm/gic.h | 5 +- 3 files changed, 932 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 52f707c..31c47a0 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -29,6 +29,7 @@ obj-y += smp.o obj-y += shutdown.o obj-y += traps.o obj-y += vgic.o +obj-y += vgic-v3.o obj-y += vgic-v2.o obj-y += vtimer.o obj-y += vuart.o diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c new file mode 100644 index 0000000..e7ebad0 --- /dev/null +++ b/xen/arch/arm/vgic-v3.c @@ -0,0 +1,927 @@ +/* + * xen/arch/arm/vgic-v3.c + * + * ARM Virtual Generic Interrupt Controller v3 support + * based on xen/arch/arm/vgic.c + * + * Vijaya Kumar K + * Copyright (c) 2014 Cavium Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "io.h" +#include +#include + +#define SZ_64K 0x00010000 +#define REG(n) (n) + +/* Number of ranks of interrupt registers for a domain */ +#define DOMAIN_NR_RANKS(d) (((d)->arch.vgic.nr_lines+31)/32) + +/* + * Rank containing GICD_ for GICD_ with + * -bits-per-interrupt + */ +static inline int REG_RANK_NR(int b, uint32_t n) +{ + switch ( b ) + { + case 64: return n >> 6; + case 32: return n >> 5; + case 16: return n >> 4; + case 8: return n >> 3; + case 4: return n >> 2; + case 2: return n >> 1; + case 1: return n; + default: BUG(); + } +} + +/* + * Offset of GICD_ with its rank, for GICD_ with + * -bits-per-interrupt. + */ +/* Shift n >> 2 to make it byte register diff */ +#define REG_RANK_INDEX(b, n) (((n) >> 2) & ((b)-1)) + +/* + * Returns rank corresponding to a GICD_ register for + * GICD_ with -bits-per-interrupt. + */ +static struct vgic_irq_rank *vgic_irq_rank(struct vcpu *v, int b, int n) +{ + int rank; + + n = n >> 2; + rank = REG_RANK_NR(b, n); + + if ( rank == 0 ) /* Rank 0 is nothing but GICR registers in GICv3 */ + return v->arch.vgic.private_irqs; + else if ( rank <= DOMAIN_NR_RANKS(v->domain) ) + return &v->domain->arch.vgic.shared_irqs[rank - 1]; + else + return NULL; +} + +#define vgic_lock(v) spin_lock_irq(&(v)->domain->arch.vgic.lock) +#define vgic_unlock(v) spin_unlock_irq(&(v)->domain->arch.vgic.lock) + +#define vgic_lock_rank(v, r) spin_lock(&(r)->lock) +#define vgic_unlock_rank(v, r) spin_unlock(&(r)->lock) + +static uint32_t byte_read(uint32_t val, int sign, int offset) +{ + int byte = offset & 0x3; + + val = val >> (8*byte); + if ( sign && (val & 0x80) ) + val |= 0xffffff00; + else + val &= 0x000000ff; + return val; +} + +static void byte_write(uint32_t *reg, uint32_t var, int offset) +{ + int byte = offset & 0x3; + + var &= (0xff << (8*byte)); + + *reg &= ~(0xff << (8*byte)); + *reg |= var; +} + +static int vgic_read_priority(struct vcpu *v, int irq) +{ + struct vgic_irq_rank *rank = vgic_irq_rank(v, 8, irq); + return byte_read(rank->ipriority[REG_RANK_INDEX(8, irq)], 0, irq & 0x3); +} + +static int __vgic_rdistr_mmio_read(struct vcpu *v, mmio_info_t *info, uint32_t offset) +{ + struct hsr_dabt dabt = info->dabt; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + register_t *r = select_user_reg(regs, dabt.reg); + int gicr_reg = REG(offset); + u64 mpidr; + u64 aff; + + switch ( gicr_reg ) + { + case GICR_CTLR: + /* We have implemented LPI's, read zero */ + goto read_as_zero; + case GICR_IIDR: + *r = 0x34; + return 1; + case GICR_TYPER: + mpidr = cpu_logical_map(smp_processor_id()); + aff = mpidr & ((1 << 24) - 1); + aff |= (mpidr >> 8) & (0xffUL << 24); + aff = aff << 32; + aff = (aff | ((u64)smp_processor_id() << 8)); + aff = (aff | (0x9UL << 0)); + *r = aff; + return 1; + case GICR_STATUSR: + /* Not implemented */ + goto read_as_zero; + case GICR_WAKER: + /* Not implemented */ + goto read_as_zero; + case GICR_SETLPIR: + case GICR_CLRLPIR: + /* WO */ + return 0; + case GICR_PROPBASER: + /* LPI's not implemented */ + goto read_as_zero; + case GICR_PENDBASER: + /* LPI's not implemented */ + goto read_as_zero; + case GICR_INVLPIR: + case GICR_INVALLR: + /* WO */ + return 0; + case GICR_SYNCR: + if ( dabt.size != 2 ) goto bad_width; + /* RO */ + /* Return as not busy */ + *r = 1; + return 1; + case GICR_MOVLPIR: + case GICR_MOVALLR: + /* WO */ + return 0; + case GICR_PIDR7... GICR_PIDR0: + *r = 0x93; + return 1; + default: + printk("vGICR: read r%d offset %#08x\n not found", dabt.reg, offset); + + } +bad_width: + printk("vGICD: bad read width %d r%d offset %#08x\n", + dabt.size, dabt.reg, offset); + domain_crash_synchronous(); + return 0; + +read_as_zero: + if ( dabt.size != 2 ) goto bad_width; + *r = 0; + return 1; +} + +static int __vgic_rdistr_mmio_write(struct vcpu *v, mmio_info_t *info, uint32_t offset) +{ + struct hsr_dabt dabt = info->dabt; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + register_t *r = select_user_reg(regs, dabt.reg); + int gicr_reg = REG(offset); + + switch ( gicr_reg ) + { + case GICR_CTLR: + /* We do not implement LPI's, read zero */ + goto write_ignore; + case GICR_IIDR: + case GICR_TYPER: + /* RO */ + goto write_ignore; + case GICR_STATUSR: + /* Not implemented */ + goto write_ignore; + case GICR_WAKER: + /* Not implemented */ + goto write_ignore; + case GICR_SETLPIR: + return 1; + case GICR_CLRLPIR: + return 1; + case GICR_PROPBASER: + /* LPI is not implemented */ + goto write_ignore; + case GICR_PENDBASER: + /* LPI is not implemented */ + goto write_ignore; + case GICR_INVLPIR: + return 1; + case GICR_INVALLR: + return 1; + case GICR_SYNCR: + /* RO */ + goto write_ignore; + case GICR_MOVLPIR: + return 1; + case GICR_MOVALLR: + return 1; + case GICR_PIDR7... GICR_PIDR0: + /* RO */ + goto write_ignore; + default: + printk("vGICR: write r%d offset %#08x\n not found", dabt.reg, offset); + } +bad_width: + printk("vGICD: bad write width %d r%d=%"PRIregister" offset %#08x\n", + dabt.size, dabt.reg, *r, offset); + domain_crash_synchronous(); + return 0; + +write_ignore: + if ( dabt.size != 2 ) goto bad_width; + return 1; +} + +static int vgic_rdistr_sgi_mmio_read(struct vcpu *v, mmio_info_t *info, uint32_t offset) +{ + struct hsr_dabt dabt = info->dabt; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + register_t *r = select_user_reg(regs, dabt.reg); + struct vgic_irq_rank *rank; + int gicr_reg = REG(offset); + + switch ( gicr_reg ) + { + case GICR_IGROUPR0: + /* We do not implement security extensions for guests, read zero */ + goto read_as_zero; + case GICR_IGRPMODR0: + /* We do not implement security extensions for guests, read zero */ + goto read_as_zero; + case GICR_ISENABLER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ISENABLER0); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->ienable; + vgic_unlock_rank(v, rank); + return 1; + case GICR_ICENABLER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ICENABLER0); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->ienable; + vgic_unlock_rank(v, rank); + return 1; + case GICR_ISPENDR0: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ISPENDR0); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = byte_read(rank->ipend, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICR_ICPENDR0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ICPENDR0); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = byte_read(rank->ipend, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICR_ISACTIVER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ISACTIVER0); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->iactive; + vgic_unlock_rank(v, rank); + return 1; + case GICR_ICACTIVER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ICACTIVER0); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->iactive; + vgic_unlock_rank(v, rank); + return 1; + case GICR_IPRIORITYR0...GICR_IPRIORITYR7: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 8, gicr_reg - GICR_IPRIORITYR0); + if ( rank == NULL ) goto read_as_zero; + + vgic_lock_rank(v, rank); + *r = rank->ipriority[REG_RANK_INDEX(8, gicr_reg - GICR_IPRIORITYR0)]; + if ( dabt.size == 0 ) + *r = byte_read(*r, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICR_ICFGR0... GICR_ICFGR1: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 2, gicr_reg - GICR_ICFGR0); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->icfg[REG_RANK_INDEX(2, gicr_reg - GICR_ICFGR0)]; + vgic_unlock_rank(v, rank); + return 1; + case GICR_NSACR: + if ( dabt.size != 2 ) goto bad_width; + return 1; + default: + printk("vGICR: read r%d offset %#08x\n not found", dabt.reg, offset); + + } +bad_width: + printk("vGICD: bad read width %d r%d offset %#08x\n", + dabt.size, dabt.reg, offset); + domain_crash_synchronous(); + return 0; + +read_as_zero: + if ( dabt.size != 2 ) goto bad_width; + *r = 0; + return 1; +} + +static int vgic_rdistr_sgi_mmio_write(struct vcpu *v, mmio_info_t *info, uint32_t offset) +{ + struct hsr_dabt dabt = info->dabt; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + register_t *r = select_user_reg(regs, dabt.reg); + struct vgic_irq_rank *rank; + int gicr_reg = REG(offset); + uint32_t tr; + + switch ( gicr_reg ) + { + case GICR_IGROUPR0: + /* We do not implement security extensions for guests, write ignore */ + goto write_ignore; + case GICR_IGRPMODR0: + /* We do not implement security extensions for guests, write ignore */ + goto write_ignore; + case GICR_ISENABLER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ISENABLER0); + if ( rank == NULL ) goto write_ignore; + vgic_lock_rank(v, rank); + tr = rank->ienable; + rank->ienable |= *r; + vgic_unlock_rank(v, rank); + vgic_enable_irqs(v, (*r) & (~tr), (gicr_reg - GICR_ISENABLER0) >> 2); + return 1; + case GICR_ICENABLER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ICENABLER0); + if ( rank == NULL ) goto write_ignore; + vgic_lock_rank(v, rank); + rank->ienable &= ~*r; + vgic_unlock_rank(v, rank); + return 1; + case GICR_ISPENDR0: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + return 0; + case GICR_ICPENDR0: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + return 0; + case GICR_ISACTIVER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ISACTIVER0); + if ( rank == NULL ) goto write_ignore; + vgic_lock_rank(v, rank); + rank->iactive &= ~*r; + vgic_unlock_rank(v, rank); + return 1; + case GICR_ICACTIVER0: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicr_reg - GICR_ICACTIVER0); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + rank->iactive &= ~*r; + vgic_unlock_rank(v, rank); + return 1; + case GICR_IPRIORITYR0...GICR_IPRIORITYR7: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 8, gicr_reg - GICR_IPRIORITYR0); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + if ( dabt.size == 2 ) + rank->ipriority[REG_RANK_INDEX(8, gicr_reg - GICR_IPRIORITYR0)] = *r; + else + byte_write(&rank->ipriority[REG_RANK_INDEX(8, gicr_reg - GICR_IPRIORITYR0)], + *r, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICR_ICFGR0: + /* RO */ + case GICR_ICFGR1: + goto write_ignore; + case GICR_NSACR: + /* We do not implement security extensions for guests, write ignore */ + goto write_ignore; + default: + printk("vGICR: write r%d offset %#08x\n not found", dabt.reg, offset); + } +bad_width: + printk("vGICD: bad write width %d r%d=%"PRIregister" offset %#08x\n", + dabt.size, dabt.reg, *r, offset); + domain_crash_synchronous(); + return 0; + +write_ignore: + if ( dabt.size != 2 ) goto bad_width; + return 1; +} + +static int vgic_rdistr_mmio_read(struct vcpu *v, mmio_info_t *info) +{ + uint32_t offset; + + if ( !v->domain->arch.vgic.info->rdist_stride ) + offset = info->gpa & (v->domain->arch.vgic.info->rdist_stride - 1); + else + offset = info->gpa & 0x1FFFF; + + if ( offset < SZ_64K ) + return __vgic_rdistr_mmio_read(v, info, offset); + else if ( (offset - SZ_64K) < SZ_64K ) + return vgic_rdistr_sgi_mmio_read(v, info, (offset - SZ_64K)); + else + printk("vGICR: wrong gpa read %"PRIpaddr"\n",info->gpa); + return 0; +} + +static int vgic_rdistr_mmio_write(struct vcpu *v, mmio_info_t *info) +{ + uint32_t offset; + + if ( !v->domain->arch.vgic.info->rdist_stride ) + offset = info->gpa & (v->domain->arch.vgic.info->rdist_stride - 1); + else + offset = info->gpa & 0x1FFFF; + + if ( offset < SZ_64K ) + return __vgic_rdistr_mmio_write(v, info, offset); + else if ( (offset - SZ_64K) < SZ_64K ) + return vgic_rdistr_sgi_mmio_write(v, info, (offset - SZ_64K)); + else + printk("vGICR: wrong gpa write %"PRIpaddr"\n",info->gpa); + return 0; +} + +static int vgic_distr_mmio_read(struct vcpu *v, mmio_info_t *info) +{ + struct hsr_dabt dabt = info->dabt; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + register_t *r = select_user_reg(regs, dabt.reg); + struct vgic_irq_rank *rank; + int offset = (int)(info->gpa - v->domain->arch.vgic.info->dbase); + int gicd_reg = REG(offset); + + switch ( gicd_reg ) + { + case GICD_CTLR: + if ( dabt.size != 2 ) goto bad_width; + vgic_lock(v); + *r = v->domain->arch.vgic.ctlr; + vgic_unlock(v); + return 1; + case GICD_TYPER: + if ( dabt.size != 2 ) goto bad_width; + /* No secure world support for guests. */ + vgic_lock(v); + *r = ( (v->domain->max_vcpus<<5) & GICD_TYPE_CPUS ) + |( ((v->domain->arch.vgic.nr_lines/32)) & GICD_TYPE_LINES ); + vgic_unlock(v); + return 1; + case GICD_STATUSR: + /* Not implemented */ + goto read_as_zero; + case GICD_IIDR: + if ( dabt.size != 2 ) goto bad_width; + /* + * XXX Do we need a JEP106 manufacturer ID? + * Just use the physical h/w value for now + */ + *r = 0x0000034c; + return 1; + /* Implementation defined -- read as zero */ + case REG(0x020) ... REG(0x03c): + goto read_as_zero; + case GICD_IGROUPR ... GICD_IGROUPRN: + /* We do not implement security extensions for guests, read zero */ + goto read_as_zero; + case GICD_ISENABLER ... GICD_ISENABLERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ISENABLER); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->ienable; + vgic_unlock_rank(v, rank); + return 1; + case GICD_ICENABLER ... GICD_ICENABLERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ICENABLER); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->ienable; + vgic_unlock_rank(v, rank); + return 1; + case GICD_ISPENDR ... GICD_ISPENDRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ISPENDR); + if ( rank == NULL ) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = byte_read(rank->ipend, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICD_ICPENDR ... GICD_ICPENDRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ICPENDR); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = byte_read(rank->ipend, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICD_ISACTIVER ... GICD_ISACTIVERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ISACTIVER); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->iactive; + vgic_unlock_rank(v, rank); + return 1; + case GICD_ICACTIVER ... GICD_ICACTIVERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ICACTIVER); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->iactive; + vgic_unlock_rank(v, rank); + return 1; + case GICD_IROUTER ... GICD_IROUTERN: + rank = vgic_irq_rank(v, 64, gicd_reg - GICD_IROUTERN); + if ( rank == NULL) goto read_as_zero; + + vgic_lock_rank(v, rank); + /* IROUTER is 64 bit so, to make it byte size right shift by 3. + Here once. macro REG_RANK_INDEX will do it twice */ + *r = rank->irouter[REG_RANK_INDEX(64, (gicd_reg - GICD_IROUTER)>>1)]; + if ( dabt.size == 0 ) + *r = byte_read(*r, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICD_IPRIORITYR ... GICD_IPRIORITYRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 8, gicd_reg - GICD_IPRIORITYR); + if ( rank == NULL) goto read_as_zero; + + vgic_lock_rank(v, rank); + *r = rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR)]; + if ( dabt.size == 0 ) + *r = byte_read(*r, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICD_ICFGR ... GICD_ICFGRN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 2, gicd_reg - GICD_ICFGR); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->icfg[REG_RANK_INDEX(2, gicd_reg - GICD_ICFGR)]; + vgic_unlock_rank(v, rank); + return 1; + case GICD_NSACR ... GICD_NSACRN: + /* We do not implement security extensions for guests, read zero */ + goto read_as_zero; + case GICD_SGIR: + if ( dabt.size != 2 ) goto bad_width; + /* Write only -- read unknown */ + *r = 0xdeadbeef; + return 1; + case GICD_CPENDSGIR ... GICD_CPENDSGIRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_CPENDSGIR); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = byte_read(rank->pendsgi, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICD_SPENDSGIR ... GICD_SPENDSGIRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_SPENDSGIR); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = byte_read(rank->pendsgi, dabt.sign, offset); + vgic_unlock_rank(v, rank); + return 1; + case GICD_PIDR7... GICD_PIDR0: + /* GICv3 identification value */ + *r = 0x92; + return 1; + case REG(0x00c): + case REG(0x044): + case REG(0x04c): + case REG(0x05c) ... REG(0x07c): + case REG(0xf30) ... REG(0x5fcc): + case REG(0x8000) ... REG(0xbfcc): + case REG(0xc000) ... REG(0xffcc): + printk("vGICD: read unknown 0x00c .. 0xfcc r%d offset %#08x\n", dabt.reg, offset); + goto read_as_zero; + + default: + printk("vGICD: unhandled read r%d offset %#08x\n", + dabt.reg, offset); + return 0; + } + +bad_width: + printk("vGICD: bad read width %d r%d offset %#08x\n", + dabt.size, dabt.reg, offset); + domain_crash_synchronous(); + return 0; + +read_as_zero: + printk("vGICD: read as zero %d r%d offset %#08x\n", + dabt.size, dabt.reg, offset); + if ( dabt.size != 2 ) goto bad_width; + *r = 0; + return 1; +} + +static int vgic_distr_mmio_write(struct vcpu *v, mmio_info_t *info) +{ + struct hsr_dabt dabt = info->dabt; + struct cpu_user_regs *regs = guest_cpu_user_regs(); + register_t *r = select_user_reg(regs, dabt.reg); + struct vgic_irq_rank *rank; + int offset = (int)(info->gpa - v->domain->arch.vgic.info->dbase); + int gicd_reg = REG(offset); + uint32_t tr; + + switch ( gicd_reg ) + { + case GICD_CTLR: + if ( dabt.size != 2 ) goto bad_width; + /* Ignore all but the enable bit */ + v->domain->arch.vgic.ctlr = (*r) & GICD_CTL_ENABLE; + return 1; + + /* R/O -- write ignored */ + case GICD_TYPER: + case GICD_IIDR: + goto write_ignore; + + case GICD_STATUSR: + goto write_ignore; + case GICD_SETSPI_NSR: + goto write_ignore; + case GICD_CLRSPI_NSR: + goto write_ignore; + case GICD_SETSPI_SR: + goto write_ignore; + case GICD_CLRSPI_SR: + goto write_ignore; + /* Implementation defined -- write ignored */ + case REG(0x020) ... REG(0x03c): + printk("vGICD: write unknown 0x020 - 0x03c r%d offset %#08x\n", dabt.reg, offset); + goto write_ignore; + + case GICD_IGROUPR ... GICD_IGROUPRN: + /* We do not implement security extensions for guests, write ignore */ + goto write_ignore; + + case GICD_ISENABLER ... GICD_ISENABLERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ISENABLER); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + tr = rank->ienable; + rank->ienable |= *r; + vgic_unlock_rank(v, rank); + vgic_enable_irqs(v, (*r) & (~tr), (gicd_reg - GICD_ISENABLER) >> 2); + return 1; + + case GICD_ICENABLER ... GICD_ICENABLERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ICENABLER); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + tr = rank->ienable; + rank->ienable &= ~*r; + vgic_unlock_rank(v, rank); + vgic_disable_irqs(v, (*r) & tr, gicd_reg - GICD_ICENABLER); + return 1; + + case GICD_ISPENDR ... GICD_ISPENDRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + return 0; + + case GICD_ICPENDR ... GICD_ICPENDRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + return 0; + + case GICD_ISACTIVER ... GICD_ISACTIVERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ISACTIVER); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + rank->iactive &= ~*r; + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ICACTIVER ... GICD_ICACTIVERN: + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 1, gicd_reg - GICD_ICACTIVER); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + rank->iactive &= ~*r; + vgic_unlock_rank(v, rank); + return 1; + + case GICD_IROUTER ... GICD_IROUTER + 8*7: + /* SGI/PPI target is read only */ + goto write_ignore; + + case GICD_IROUTER + 8*8 ... GICD_IROUTERN: + rank = vgic_irq_rank(v, 64, gicd_reg - GICD_IROUTER); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + rank->irouter[REG_RANK_INDEX(64, (gicd_reg - GICD_IROUTER)>>1)] = *r; + vgic_unlock_rank(v, rank); + return 1; + + case GICD_IPRIORITYR ... GICD_IPRIORITYRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 8, gicd_reg - GICD_IPRIORITYR); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + if ( dabt.size == 2 ) + rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR)] = *r; + else + byte_write(&rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR)], + *r, offset); + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ICFGR: /* SGIs */ + goto write_ignore; + case GICD_ICFGR + 4: /* PPIs */ + /* It is implementation defined if these are writeable. We chose not */ + goto write_ignore; + case GICD_ICFGR + 8 ... GICD_ICFGRN: /* SPIs */ + if ( dabt.size != 2 ) goto bad_width; + rank = vgic_irq_rank(v, 2, gicd_reg - GICD_ICFGR); + vgic_lock_rank(v, rank); + if ( rank == NULL) goto write_ignore; + rank->icfg[REG_RANK_INDEX(2, gicd_reg - GICD_ICFGR)] = *r; + vgic_unlock_rank(v, rank); + return 1; + + case GICD_NSACR ... GICD_NSACRN: + /* We do not implement security extensions for guests, write ignore */ + goto write_ignore; + + case GICD_SGIR: + /* it is accessed as system register */ + goto write_ignore; + + case GICD_CPENDSGIR ... GICD_CPENDSGIRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + return 0; + + case GICD_SPENDSGIR ... GICD_SPENDSGIRN: + if ( dabt.size != 0 && dabt.size != 2 ) goto bad_width; + return 0; + + case REG(0x00c): + case REG(0x044): + case REG(0x04c): + case REG(0x05c) ... REG(0x07c): + case REG(0xf30) ... REG(0x5fcc): + case REG(0x8000) ... REG(0xbfcc): + case REG(0xc000) ... REG(0xffcc): + printk("vGICD: write unknown 0x00c 0xfcc r%d offset %#08x\n", dabt.reg, offset); + goto write_ignore; + + default: + printk("vGICD: unhandled write r%d=%"PRIregister" offset %#08x\n", + dabt.reg, *r, offset); + return 0; + } + +bad_width: + printk("vGICD: bad write width %d r%d=%"PRIregister" offset %#08x\n", + dabt.size, dabt.reg, *r, offset); + domain_crash_synchronous(); + return 0; + +write_ignore: + if ( dabt.size != 2 ) goto bad_width; + return 1; +} + + +static int vgic_rdistr_mmio_check(struct vcpu *v, paddr_t addr) +{ + struct domain *d = v->domain; + + /* Compare with global rdist base. the addr should fall in this region */ + return (addr >= (d->arch.vgic.info->rbase)) && (addr < (d->arch.vgic.info->rbase + d->arch.vgic.info->rbase_size)); +} + +static struct mmio_handler vgic_rdistr_mmio_handler = { + .check_handler = vgic_rdistr_mmio_check, + .read_handler = vgic_rdistr_mmio_read, + .write_handler = vgic_rdistr_mmio_write, +}; + +static int vgic_distr_mmio_check(struct vcpu *v, paddr_t addr) +{ + struct domain *d = v->domain; + + return (addr >= (d->arch.vgic.info->dbase)) && (addr < (d->arch.vgic.info->dbase + d->arch.vgic.info->dbase_size)); +} + +static struct mmio_handler vgic_distr_mmio_handler = { + .check_handler = vgic_distr_mmio_check, + .read_handler = vgic_distr_mmio_read, + .write_handler = vgic_distr_mmio_write, +}; + + +static int vgic_vcpu_init(struct vcpu *v) +{ + int i; + u64 affinity; + + v->arch.vgic.private_irqs = xzalloc(struct vgic_irq_rank); + + memset(v->arch.vgic.private_irqs, 0, sizeof(struct vgic_irq_rank)); + + spin_lock_init(&v->arch.vgic.private_irqs->lock); + + /* For SGI and PPI the target is always this CPU */ + affinity = cpu_logical_map(v->vcpu_id); + for ( i = 0 ; i < 32 ; i++ ) + v->arch.vgic.private_irqs->irouter[i] = affinity; + return 0; +} + +static int vgic_domain_init(struct domain *d) +{ + int i; + + d->arch.vgic.shared_irqs = + xzalloc_array(struct vgic_irq_rank, DOMAIN_NR_RANKS(d)); + + for ( i = 0; i < DOMAIN_NR_RANKS(d); i++) + spin_lock_init(&d->arch.vgic.shared_irqs[i].lock); + + register_mmio_handler(d, &vgic_distr_mmio_handler); + register_mmio_handler(d, &vgic_rdistr_mmio_handler); + return 0; +} + +static struct vgic_ops ops = { + .vgic_vcpu_init = vgic_vcpu_init, + .vgic_domain_init = vgic_domain_init, + .read_priority = vgic_read_priority, +}; + +static int __init vgic_v3_init(struct dt_device_node *dev, const void *data) +{ + register_vgic_ops(&ops); + return 0; +} + +static const char * const vgicv3_dt_compat[] __initconst = +{ + "arm,gic-v3", + NULL +}; + +DT_DEVICE_START(vgicv3, "VGIC", DEVICE_VGIC) + .compatible = vgicv3_dt_compat, + .init = vgic_v3_init, +DT_DEVICE_END + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 201f534..eadbfcc 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -63,7 +63,10 @@ struct vgic_irq_rank { uint32_t ienable, iactive, ipend, pendsgi; uint32_t icfg[2]; uint32_t ipriority[8]; - uint32_t itargets[8]; + union { + uint32_t itargets[8]; + uint64_t irouter[32]; + }; }; struct vgic_info { -- 1.7.9.5