[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v10 4/7] xen/arm: Add virtual GICv3 support
On Tue, 9 Sep 2014, vijay.kilari@xxxxxxxxx wrote: > From: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx> > > Add virtual GICv3 driver support. > Also, with this patch vgic_irq_rank structure is modified to > hold GICv2 GICD_TARGET and GICv3 GICD_ROUTER registers under > union. > > This patch adds only basic GICv3 support. > Does not support Interrupt Translation support (ITS) > > Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx> > --- > v10:- Implement callbacks get_target_vcpu and get_irq_priority > for vgic-v3 > - Fix REG_RANK_NR for 64 bit > v9: - Use 128K mask to compute GICR register offset if stride > is not set > - Fix alignment errors in vgic-v.c > - Updated vgic_{lock,unlock}_rank > v8: - Fixed printk coding styles > - Moved GICD_PIDRn and GICR_PIDRn macros to vgic-v3.c > from header file > - Check is made on return value of vgic_v3_init() > v7: Fixed coding style. > v6: Removed byte read access for IROUTERN register. > --- > xen/arch/arm/Makefile | 1 + > xen/arch/arm/vgic-v2.c | 23 +- > xen/arch/arm/vgic-v3.c | 954 > ++++++++++++++++++++++++++++++++++++++++++++ > xen/arch/arm/vgic.c | 6 + > xen/include/asm-arm/vgic.h | 18 +- > 5 files changed, 989 insertions(+), 13 deletions(-) > > diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile > index 33765f6..e557cac 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 vgic-v2.o > +obj-$(CONFIG_ARM_64) += vgic-v3.o > obj-y += vtimer.o > obj-y += vuart.o > obj-y += hvm.o > diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c > index ef18804..d10e139 100644 > --- a/xen/arch/arm/vgic-v2.c > +++ b/xen/arch/arm/vgic-v2.c > @@ -132,10 +132,9 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, > mmio_info_t *info) > if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto > bad_width; > rank = vgic_rank_offset(v, 8, gicd_reg - GICD_ITARGETSR, DABT_WORD); > if ( rank == NULL) goto read_as_zero; > - > vgic_lock_rank(v, rank, flags); > - *r = rank->itargets[REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, > - DABT_WORD)]; > + *r = rank->v2.itargets[REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, > + DABT_WORD)]; > if ( dabt.size == DABT_BYTE ) > *r = vgic_byte_read(*r, dabt.sign, gicd_reg); > vgic_unlock_rank(v, rank, flags); > @@ -390,7 +389,7 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, > mmio_info_t *info) > struct vcpu *v_target, *v_old; > > new_target = i % 8; > - old_target_mask = vgic_byte_read(rank->itargets[REG_RANK_INDEX(8, > + old_target_mask = > vgic_byte_read(rank->v2.itargets[REG_RANK_INDEX(8, > gicd_reg - GICD_ITARGETSR, > DABT_WORD)], 0, i/8); > old_target = find_first_bit(&old_target_mask, 8); > > @@ -404,11 +403,11 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, > mmio_info_t *info) > i += 8 - new_target; > } > if ( dabt.size == DABT_WORD ) > - rank->itargets[REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, > - DABT_WORD)] = target; > + rank->v2.itargets[REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, > + DABT_WORD)] = target; > else > - vgic_byte_write(&rank->itargets[REG_RANK_INDEX(8, > - gicd_reg - GICD_ITARGETSR, DABT_WORD)], target, > gicd_reg); > + vgic_byte_write(&rank->v2.itargets[REG_RANK_INDEX(8, > + gicd_reg - GICD_ITARGETSR, DABT_WORD)], target, > gicd_reg); > vgic_unlock_rank(v, rank, flags); > return 1; > } > @@ -511,7 +510,7 @@ static struct vcpu *vgic_v2_get_target_vcpu(struct vcpu > *v, unsigned int irq) > struct vgic_irq_rank *rank = vgic_rank_irq(v, irq); > ASSERT(spin_is_locked(&rank->lock)); > > - target = vgic_byte_read(rank->itargets[(irq%32)/4], 0, irq % 4); > + target = vgic_byte_read(rank->v2.itargets[(irq%32)/4], 0, irq % 4); > /* 1-N SPI should be delivered as pending to all the vcpus in the > * mask, but here we just return the first vcpu for simplicity and > * because it would be too slow to do otherwise. */ > @@ -541,7 +540,7 @@ static int vgic_v2_vcpu_init(struct vcpu *v) > > /* For SGI and PPI the target is always this CPU */ > for ( i = 0 ; i < 8 ; i++ ) > - v->arch.vgic.private_irqs->itargets[i] = > + v->arch.vgic.private_irqs->v2.itargets[i] = > (1<<(v->vcpu_id+0)) > | (1<<(v->vcpu_id+8)) > | (1<<(v->vcpu_id+16)) > @@ -556,8 +555,8 @@ static int vgic_v2_domain_init(struct domain *d) > > /* By default deliver to CPU0 */ > for ( i = 0; i < DOMAIN_NR_RANKS(d); i++ ) > - memset(d->arch.vgic.shared_irqs[i].itargets, 0x1, > - sizeof(d->arch.vgic.shared_irqs[i].itargets)); > + memset(d->arch.vgic.shared_irqs[i].v2.itargets, 0x1, > + sizeof(d->arch.vgic.shared_irqs[i].v2.itargets)); > > /* We rely on gicv_setup() to initialize dbase(vGIC distributor base) */ > register_mmio_handler(d, &vgic_v2_distr_mmio_handler, d->arch.vgic.dbase, > diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c > new file mode 100644 > index 0000000..374a226 > --- /dev/null > +++ b/xen/arch/arm/vgic-v3.c > @@ -0,0 +1,954 @@ > +/* > + * xen/arch/arm/vgic-v3.c > + * > + * ARM Virtual Generic Interrupt Controller v3 support > + * based on xen/arch/arm/vgic.c > + * > + * Vijaya Kumar K <vijaya.kumar@xxxxxxxxxxxxxxxxxx> > + * 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 <xen/bitops.h> > +#include <xen/config.h> > +#include <xen/lib.h> > +#include <xen/init.h> > +#include <xen/softirq.h> > +#include <xen/irq.h> > +#include <xen/sched.h> > +#include <xen/sizes.h> > +#include <asm/current.h> > +#include <asm/device.h> > +#include <asm/mmio.h> > +#include <asm/gic_v3_defs.h> > +#include <asm/gic.h> > +#include <asm/vgic.h> > + > +/* GICD_PIDRn register values for ARM implementations */ > +#define GICV3_GICD_PIDR0 0x92 > +#define GICV3_GICD_PIDR1 0xb4 > +#define GICV3_GICD_PIDR2 0x3b > +#define GICV3_GICD_PIDR4 0x04 > + > +/* GICR_PIDRn register values for ARM implementations */ > +#define GICV3_GICR_PIDR0 0x93 > +#define GICV3_GICR_PIDR1 GICV3_GICD_PIDR1 > +#define GICV3_GICR_PIDR2 GICV3_GICD_PIDR2 > +#define GICV3_GICR_PIDR4 GICV3_GICD_PIDR4 > + > +static struct vcpu *vgicv3_get_target_vcpu(struct vcpu *v, unsigned int irq) > +{ > + /* TODO: Return vcpu0 always */ > + return v->domain->vcpu[0]; > +} > + > +static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info, > + uint32_t gicr_reg) > +{ > + struct hsr_dabt dabt = info->dabt; > + struct cpu_user_regs *regs = guest_cpu_user_regs(); > + register_t *r = select_user_reg(regs, dabt.reg); > + uint64_t aff; > + > + switch ( gicr_reg ) > + { > + case GICR_CTLR: > + /* We have not implemented LPI's, read zero */ > + goto read_as_zero; > + case GICR_IIDR: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICR_IIDR_VAL; > + return 1; > + case GICR_TYPER: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + /* TBD: Update processor id in [23:8] when ITS support is added */ > + aff = (MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 3) << 56 | > + MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 2) << 48 | > + MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 1) << 40 | > + MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 0) << 32); > + *r = aff; > + return 1; > + case GICR_STATUSR: > + /* Not implemented */ > + goto read_as_zero; > + case GICR_WAKER: > + /* Power management is not implemented */ > + goto read_as_zero; > + case GICR_SETLPIR: > + /* WO. Read as zero */ > + goto read_as_zero_64; > + case GICR_CLRLPIR: > + /* WO. Read as zero */ > + goto read_as_zero_64; > + case GICR_PROPBASER: > + /* LPI's not implemented */ > + goto read_as_zero_64; > + case GICR_PENDBASER: > + /* LPI's not implemented */ > + goto read_as_zero_64; > + case GICR_INVLPIR: > + /* WO. Read as zero */ > + goto read_as_zero_64; > + case GICR_INVALLR: > + /* WO. Read as zero */ > + goto read_as_zero_64; > + return 0; > + case GICR_SYNCR: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + /* RO . But when read it always returns busy bito bit[0] */ > + *r = GICR_SYNCR_NOT_BUSY; > + return 1; > + case GICR_MOVLPIR: > + /* WO Read as zero */ > + goto read_as_zero_64; > + case GICR_MOVALLR: > + /* WO Read as zero */ > + goto read_as_zero_64; > + case GICR_PIDR0: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICR_PIDR0; > + return 1; > + case GICR_PIDR1: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICR_PIDR1; > + return 1; > + case GICR_PIDR2: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICR_PIDR2; > + return 1; > + case GICR_PIDR3: > + /* Manufacture/customer defined */ > + goto read_as_zero; > + case GICR_PIDR4: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICR_PIDR4; > + return 1; > + case GICR_PIDR5 ... GICR_PIDR7: > + /* Reserved0 */ > + goto read_as_zero; > + default: > + printk("vGICv3: vGICR: read r%d offset %#08x\n not found", > + dabt.reg, gicr_reg); > + return 0; > + } > +bad_width: > + printk("vGICv3: vGICR: bad read width %d r%d offset %#08x\n", > + dabt.size, dabt.reg, gicr_reg); > + domain_crash_synchronous(); > + return 0; > + > +read_as_zero_64: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + *r = 0; > + return 1; > + > +read_as_zero: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = 0; > + return 1; > +} > + > +static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info, > + uint32_t gicr_reg) > +{ > + struct hsr_dabt dabt = info->dabt; > + struct cpu_user_regs *regs = guest_cpu_user_regs(); > + register_t *r = select_user_reg(regs, dabt.reg); > + > + switch ( gicr_reg ) > + { > + case GICR_CTLR: > + /* LPI's not implemented */ > + goto write_ignore; > + case GICR_IIDR: > + /* RO */ > + goto write_ignore; > + case GICR_TYPER: > + /* RO */ > + goto write_ignore_64; > + case GICR_STATUSR: > + /* Not implemented */ > + goto write_ignore; > + case GICR_WAKER: > + /* Power mgmt not implemented */ > + goto write_ignore; > + case GICR_SETLPIR: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_CLRLPIR: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_PROPBASER: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_PENDBASER: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_INVLPIR: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_INVALLR: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_SYNCR: > + /* RO */ > + goto write_ignore; > + case GICR_MOVLPIR: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_MOVALLR: > + /* LPI is not implemented */ > + goto write_ignore_64; > + case GICR_PIDR7... GICR_PIDR0: > + /* RO */ > + goto write_ignore; > + default: > + printk("vGICR: write r%d offset %#08x\n not found", dabt.reg, > gicr_reg); > + return 0; > + } > +bad_width: > + printk("vGICR: bad write width %d r%d=%"PRIregister" offset %#08x\n", > + dabt.size, dabt.reg, *r, gicr_reg); > + domain_crash_synchronous(); > + return 0; > + > +write_ignore_64: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + return 1; > + > +write_ignore: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 1; > +} > + > +static int __vgic_v3_distr_common_mmio_read(struct vcpu *v, mmio_info_t > *info, > + uint32_t reg) > +{ > + 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; > + unsigned long flags; > + > + switch ( reg ) > + { > + 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 != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ISENABLER, DABT_WORD); > + if ( rank == NULL ) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->ienable; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICENABLER ... GICD_ICENABLERN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ICENABLER, DABT_WORD); > + if ( rank == NULL) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->ienable; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ISPENDR ... GICD_ISPENDRN: > + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto > bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ISPENDR, DABT_WORD); > + if ( rank == NULL ) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = vgic_byte_read(rank->ipend, dabt.sign, reg); > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICPENDR ... GICD_ICPENDRN: > + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto > bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ICPENDR, DABT_WORD); > + if ( rank == NULL) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = vgic_byte_read(rank->ipend, dabt.sign, reg); > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ISACTIVER ... GICD_ISACTIVERN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ISACTIVER, DABT_WORD); > + if ( rank == NULL) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->iactive; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICACTIVER ... GICD_ICACTIVERN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ICACTIVER, DABT_WORD); > + if ( rank == NULL) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->iactive; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_IPRIORITYR ... GICD_IPRIORITYRN: > + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto > bad_width; > + rank = vgic_rank_offset(v, 8, reg - GICD_IPRIORITYR, DABT_WORD); > + if ( rank == NULL) goto read_as_zero; > + > + vgic_lock_rank(v, rank, flags); > + *r = rank->ipriority[REG_RANK_INDEX(8, reg - GICD_IPRIORITYR, > + DABT_WORD)]; > + if ( dabt.size == DABT_BYTE ) > + *r = vgic_byte_read(*r, dabt.sign, reg); > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICFGR ... GICD_ICFGRN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 2, reg - GICD_ICFGR, DABT_WORD); > + if ( rank == NULL) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->icfg[REG_RANK_INDEX(2, reg - GICD_ICFGR, DABT_WORD)]; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + default: > + printk("vGICv3: vGICD/vGICR: unhandled read r%d offset %#08x\n", > + dabt.reg, reg); > + return 0; > + } > + > +bad_width: > + dprintk(XENLOG_ERR, > + "vGICv3: vGICD/vGICR: bad read width %d r%d offset %#08x\n", > + dabt.size, dabt.reg, reg); > + domain_crash_synchronous(); > + return 0; > + > +read_as_zero: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = 0; > + return 1; > +} > + > +static int __vgic_v3_distr_common_mmio_write(struct vcpu *v, mmio_info_t > *info, > + uint32_t reg) > +{ > + 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; > + uint32_t tr; > + unsigned long flags; > + > + switch ( reg ) > + { > + 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 != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ISENABLER, DABT_WORD); > + if ( rank == NULL) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + tr = rank->ienable; > + rank->ienable |= *r; > + vgic_unlock_rank(v, rank, flags); > + /* The irq number is extracted from offset. so shift by register > size */ > + vgic_enable_irqs(v, (*r) & (~tr), (reg - GICD_ISENABLER) >> > DABT_WORD); > + return 1; > + case GICD_ICENABLER ... GICD_ICENABLERN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ICENABLER, DABT_WORD); > + if ( rank == NULL) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + tr = rank->ienable; > + rank->ienable &= ~*r; > + vgic_unlock_rank(v, rank, flags); > + /* The irq number is extracted from offset. so shift by register > size */ > + vgic_disable_irqs(v, (*r) & tr, (reg - GICD_ICENABLER) >> DABT_WORD); > + return 1; > + case GICD_ISPENDR ... GICD_ISPENDRN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ISPENDR, DABT_WORD); > + if ( rank == NULL ) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + rank->ipend = *r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICPENDR ... GICD_ICPENDRN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ICPENDR, DABT_WORD); > + if ( rank == NULL ) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + rank->ipend &= ~*r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ISACTIVER ... GICD_ISACTIVERN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ISACTIVER, DABT_WORD); > + if ( rank == NULL) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + rank->iactive &= ~*r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICACTIVER ... GICD_ICACTIVERN: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, reg - GICD_ICACTIVER, DABT_WORD); > + if ( rank == NULL) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + rank->iactive &= ~*r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_IPRIORITYR ... GICD_IPRIORITYRN: > + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto > bad_width; > + rank = vgic_rank_offset(v, 8, reg - GICD_IPRIORITYR, DABT_WORD); > + if ( rank == NULL) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + if ( dabt.size == DABT_WORD ) > + rank->ipriority[REG_RANK_INDEX(8, reg - GICD_IPRIORITYR, > + DABT_WORD)] = *r; > + else > + vgic_byte_write(&rank->ipriority[REG_RANK_INDEX(8, > + reg - GICD_IPRIORITYR, DABT_WORD)], *r, reg); > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_ICFGR: /* Restricted to configure SGIs */ > + goto write_ignore; > + case GICD_ICFGR + 4 ... GICD_ICFGRN: /* PPI + SPIs */ > + /* ICFGR1 for PPI's, which is implementation defined > + if ICFGR1 is programmable or not. We chose to program */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 2, reg - GICD_ICFGR, DABT_WORD); > + if ( rank == NULL) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + rank->icfg[REG_RANK_INDEX(2, reg - GICD_ICFGR, DABT_WORD)] = *r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + default: > + printk("vGICv3: vGICD/vGICR: unhandled write r%d " > + "=%"PRIregister" offset %#08x\n", dabt.reg, *r, reg); > + return 0; > + } > + > +bad_width: > + dprintk(XENLOG_ERR, > + "vGICv3: vGICD/vGICR: bad write width %d r%d=%"PRIregister" " > + "offset %#08x\n", dabt.size, dabt.reg, *r, reg); > + domain_crash_synchronous(); > + return 0; > + > +write_ignore: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 1; > +} > + > +static int vgic_v3_rdistr_sgi_mmio_read(struct vcpu *v, mmio_info_t *info, > + uint32_t gicr_reg) > +{ > + 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; > + unsigned long flags; > + > + switch ( gicr_reg ) > + { > + case GICR_IGRPMODR0: > + /* We do not implement security extensions for guests, read zero */ > + goto read_as_zero; > + case GICR_IGROUPR0: > + case GICR_ISENABLER0: > + case GICR_ICENABLER0: > + case GICR_ISACTIVER0: > + case GICR_ICACTIVER0: > + case GICR_IPRIORITYR0...GICR_IPRIORITYR7: > + case GICR_ICFGR0... GICR_ICFGR1: > + /* > + * Above registers offset are common with GICD. > + * So handle in common with GICD handling > + */ > + return __vgic_v3_distr_common_mmio_read(v, info, gicr_reg); > + case GICR_ISPENDR0: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, gicr_reg - GICR_ISPENDR0, DABT_WORD); > + if ( rank == NULL ) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->pendsgi; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICR_ICPENDR0: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, gicr_reg - GICR_ICPENDR0, DABT_WORD); > + if ( rank == NULL ) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + *r = rank->pendsgi; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICR_NSACR: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 1; > + default: > + printk("vGICv3: vGICR: read r%d offset %#08x\n not found", > + dabt.reg, gicr_reg); > + return 0; > + } > +bad_width: > + printk("vGICv3: vGICR: bad read width %d r%d offset %#08x\n", > + dabt.size, dabt.reg, gicr_reg); > + domain_crash_synchronous(); > + return 0; > + > +read_as_zero: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = 0; > + return 1; > +} > + > +static int vgic_v3_rdistr_sgi_mmio_write(struct vcpu *v, mmio_info_t *info, > + uint32_t gicr_reg) > +{ > + 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; > + unsigned long flags; > + > + switch ( gicr_reg ) > + { > + case GICR_IGRPMODR0: > + /* We do not implement security extensions for guests, write ignore > */ > + goto write_ignore; > + case GICR_IGROUPR0: > + case GICR_ISENABLER0: > + case GICR_ICENABLER0: > + case GICR_ISACTIVER0: > + case GICR_ICACTIVER0: > + case GICR_ICFGR1: > + case GICR_IPRIORITYR0...GICR_IPRIORITYR7: > + /* > + * Above registers offset are common with GICD. > + * So handle common with GICD handling > + */ > + return __vgic_v3_distr_common_mmio_write(v, info, gicr_reg); > + case GICR_ISPENDR0: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, gicr_reg - GICR_ISACTIVER0, DABT_WORD); > + if ( rank == NULL ) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + /* TODO: we just store the SGI pending status. Handle it properly */ > + rank->pendsgi |= *r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICR_ICPENDR0: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 1, gicr_reg - GICR_ISACTIVER0, DABT_WORD); > + if ( rank == NULL ) goto write_ignore; > + vgic_lock_rank(v, rank, flags); > + /* TODO: we just store the SGI pending status. Handle it properly */ > + rank->pendsgi &= ~*r; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICR_NSACR: > + /* We do not implement security extensions for guests, write ignore > */ > + goto write_ignore; > + default: > + printk("vGICv3: vGICR SGI: write r%d offset %#08x\n not found", > + dabt.reg, gicr_reg); > + return 0; > + } > + > +bad_width: > + printk("vGICR SGI: bad write width %d r%d=%"PRIregister" offset %#08x\n", > + dabt.size, dabt.reg, *r, gicr_reg); > + domain_crash_synchronous(); > + return 0; > + > +write_ignore: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 1; > +} > + > +static int vgic_v3_rdistr_mmio_read(struct vcpu *v, mmio_info_t *info) > +{ > + uint32_t offset; > + > + if ( v->domain->arch.vgic.rdist_stride != 0 ) > + offset = info->gpa & (v->domain->arch.vgic.rdist_stride - 1); > + else > + /* If stride is not set. Default 128K */ > + offset = info->gpa & (SZ_128K - 1); > + > + if ( offset < SZ_64K ) > + return __vgic_v3_rdistr_rd_mmio_read(v, info, offset); > + else if ( (offset >= SZ_64K) && (offset < 2 * SZ_64K) ) > + return vgic_v3_rdistr_sgi_mmio_read(v, info, (offset - SZ_64K)); > + else > + gdprintk(XENLOG_WARNING, > + "vGICv3: vGICR: unknown gpa read address %"PRIpaddr"\n", > + info->gpa); > + > + return 0; > +} > + > +static int vgic_v3_rdistr_mmio_write(struct vcpu *v, mmio_info_t *info) > +{ > + uint32_t offset; > + > + if ( v->domain->arch.vgic.rdist_stride != 0 ) > + offset = info->gpa & (v->domain->arch.vgic.rdist_stride - 1); > + else > + /* If stride is not set. Default 128K */ > + offset = info->gpa & (SZ_128K - 1); > + > + if ( offset < SZ_64K ) > + return __vgic_v3_rdistr_rd_mmio_write(v, info, offset); > + else if ( (offset >= SZ_64K) && (offset < 2 * SZ_64K) ) > + return vgic_v3_rdistr_sgi_mmio_write(v, info, (offset - SZ_64K)); > + else > + gdprintk(XENLOG_WARNING, > + "vGICV3: vGICR: unknown gpa write address %"PRIpaddr"\n", > + info->gpa); > + > + return 0; > +} > + > +static int vgic_v3_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; > + unsigned long flags; > + int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase); > + > + switch ( gicd_reg ) > + { > + case GICD_CTLR: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + vgic_lock(v); > + *r = v->domain->arch.vgic.ctlr; > + vgic_unlock(v); > + return 1; > + case GICD_TYPER: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + /* No secure world support for guests. */ > + *r = (((v->domain->max_vcpus << 5) & GICD_TYPE_CPUS ) | > + ((v->domain->arch.vgic.nr_lines / 32) & GICD_TYPE_LINES)); > + return 1; > + case GICD_STATUSR: > + /* > + * Optional, Not implemented for now. > + * Update to support guest debugging. > + */ > + goto read_as_zero; > + case GICD_IIDR: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICD_IIDR_VAL; > + return 1; > + case 0x020 ... 0x03c: > + case 0xc000 ... 0xffcc: > + /* Implementation defined -- read as zero */ > + goto read_as_zero; > + case GICD_IGROUPR ... GICD_IGROUPRN: > + case GICD_ISENABLER ... GICD_ISENABLERN: > + case GICD_ICENABLER ... GICD_ICENABLERN: > + case GICD_ISPENDR ... GICD_ISPENDRN: > + case GICD_ICPENDR ... GICD_ICPENDRN: > + case GICD_ISACTIVER ... GICD_ISACTIVERN: > + case GICD_IPRIORITYR ... GICD_IPRIORITYRN: > + case GICD_ICFGR ... GICD_ICFGRN: > + /* > + * Above all register are common with GICR and GICD > + * Manage in common > + */ > + return __vgic_v3_distr_common_mmio_read(v, info, gicd_reg); > + case GICD_IROUTER ... GICD_IROUTER31: > + /* SGI/PPI is RES0 */ > + goto read_as_zero_64; > + case GICD_IROUTER32 ... GICD_IROUTERN: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 64, gicd_reg - GICD_IROUTER, > DABT_DOUBLE_WORD); > + if ( rank == NULL) goto read_as_zero; > + vgic_lock_rank(v, rank, flags); > + /* IROUTER is 64 bit so, to make it byte size right shift by 3. > + Here once. macro REG_RANK_INDEX will do it twice */ This comment is not correct, is it? > + *r = rank->v3.irouter[REG_RANK_INDEX(64, > + (gicd_reg - GICD_IROUTER), DABT_DOUBLE_WORD)]; > + vgic_unlock_rank(v, rank, flags); > + return 1; > + case GICD_NSACR ... GICD_NSACRN: > + /* We do not implement security extensions for guests, read zero */ > + goto read_as_zero; > + case GICD_SGIR: > + /* Read as ICH_SGIR system register with SRE set. So ignore */ > + goto read_as_zero; > + case GICD_CPENDSGIR ... GICD_CPENDSGIRN: > + /* Replaced with GICR_ICPENDR0. So ignore write */ > + goto read_as_zero; > + case GICD_SPENDSGIR ... GICD_SPENDSGIRN: > + /* Replaced with GICR_ISPENDR0. So ignore write */ > + goto read_as_zero; > + case GICD_PIDR0: > + /* GICv3 identification value */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICD_PIDR0; > + return 1; > + case GICD_PIDR1: > + /* GICv3 identification value */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICD_PIDR1; > + return 1; > + case GICD_PIDR2: > + /* GICv3 identification value */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICD_PIDR2; > + return 1; > + case GICD_PIDR3: > + /* GICv3 identification value. Manufacturer/Customer defined */ > + goto read_as_zero; > + case GICD_PIDR4: > + /* GICv3 identification value */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = GICV3_GICD_PIDR4; > + return 1; > + case GICD_PIDR5 ... GICD_PIDR7: > + /* Reserved0 */ > + goto read_as_zero; > + case 0x00c: > + case 0x044: > + case 0x04c: > + case 0x05c ... 0x07c: > + case 0xf30 ... 0x5fcc: > + case 0x8000 ... 0xbfcc: > + /* These are reserved register addresses */ > + printk("vGICv3: vGICD: read unknown 0x00c .. 0xfcc r%d offset > %#08x\n", > + dabt.reg, gicd_reg); > + goto read_as_zero; > + default: > + printk("vGICv3: vGICD: unhandled read r%d offset %#08x\n", > + dabt.reg, gicd_reg); > + return 0; > + } > + > +bad_width: > + dprintk(XENLOG_ERR, "vGICv3: vGICD: bad read width %d r%d offset > %#08x\n", > + dabt.size, dabt.reg, gicd_reg); > + domain_crash_synchronous(); > + return 0; > + > +read_as_zero_64: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + *r = 0; > + return 1; > + > +read_as_zero: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + *r = 0; > + return 1; > +} > + > +static int vgic_v3_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; > + unsigned long flags; > + int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase); > + > + switch ( gicd_reg ) > + { > + case GICD_CTLR: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + /* Ignore all but the enable bit */ > + v->domain->arch.vgic.ctlr = (*r) & GICD_CTL_ENABLE; > + return 1; > + case GICD_TYPER: > + /* RO -- write ignored */ > + goto write_ignore; > + case GICD_IIDR: > + /* RO -- write ignored */ > + goto write_ignore; > + case GICD_STATUSR: > + /* RO -- write ignored */ > + goto write_ignore; > + case GICD_SETSPI_NSR: > + /* Message based SPI is not implemented */ > + goto write_ignore; > + case GICD_CLRSPI_NSR: > + /* Message based SPI is not implemented */ > + goto write_ignore; > + case GICD_SETSPI_SR: > + /* Message based SPI is not implemented */ > + goto write_ignore; > + case GICD_CLRSPI_SR: > + /* Message based SPI is not implemented */ > + goto write_ignore; > + case 0x020 ... 0x03c: > + case 0xc000 ... 0xffcc: > + /* Implementation defined -- write ignored */ > + printk("vGICv3: vGICD: write unknown 0x020 - 0x03c r%d offset > %#08x\n", > + dabt.reg, gicd_reg); > + goto write_ignore; > + case GICD_IGROUPR ... GICD_IGROUPRN: > + case GICD_ISENABLER ... GICD_ISENABLERN: > + case GICD_ICENABLER ... GICD_ICENABLERN: > + case GICD_ISPENDR ... GICD_ISPENDRN: > + case GICD_ICPENDR ... GICD_ICPENDRN: > + case GICD_ISACTIVER ... GICD_ISACTIVERN: > + case GICD_ICACTIVER ... GICD_ICACTIVERN: > + case GICD_IPRIORITYR ... GICD_IPRIORITYRN: > + case GICD_ICFGR ... GICD_ICFGRN: > + /* Above registers are common with GICR and GICD > + * Manage in common */ > + return __vgic_v3_distr_common_mmio_write(v, info, gicd_reg); > + case GICD_IROUTER ... GICD_IROUTER31: > + /* SGI/PPI is RES0 */ > + goto write_ignore_64; > + case GICD_IROUTER32 ... GICD_IROUTERN: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + rank = vgic_rank_offset(v, 64, gicd_reg - GICD_IROUTER, > DABT_DOUBLE_WORD); > + if ( rank == NULL) goto write_ignore_64; > + if ( *r ) > + { > + /* TODO: Ignored. We don't support irq delivery for vcpu != 0 */ > + gdprintk(XENLOG_DEBUG, > + "SPI delivery to secondary cpus not supported\n"); > + goto write_ignore_64; > + } > + vgic_lock_rank(v, rank, flags); > + rank->v3.irouter[REG_RANK_INDEX(64, > + (gicd_reg - GICD_IROUTER), DABT_DOUBLE_WORD)] = *r; > + vgic_unlock_rank(v, rank, flags); > + 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 in GICv3 */ > + goto write_ignore; > + case GICD_CPENDSGIR ... GICD_CPENDSGIRN: > + /* Replaced with GICR_ICPENDR0. So ignore write */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 0; > + case GICD_SPENDSGIR ... GICD_SPENDSGIRN: > + /* Replaced with GICR_ISPENDR0. So ignore write */ > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 0; > + case GICD_PIDR7... GICD_PIDR0: > + /* RO -- write ignore */ > + goto write_ignore; > + case 0x00c: > + case 0x044: > + case 0x04c: > + case 0x05c ... 0x07c: > + case 0xf30 ... 0x5fcc: > + case 0x8000 ... 0xbfcc: > + /* Reserved register addresses */ > + printk("vGICv3: vGICD: write unknown 0x00c 0xfcc r%d offset > %#08x\n", > + dabt.reg, gicd_reg); > + goto write_ignore; > + default: > + printk("vGICv3: vGICD: unhandled write r%d=%"PRIregister" " > + "offset %#08x\n", dabt.reg, *r, gicd_reg); > + return 0; > + } > + > +bad_width: > + dprintk(XENLOG_ERR, > + "VGICv3: vGICD: bad write width %d r%d=%"PRIregister" " > + "offset %#08x\n", dabt.size, dabt.reg, *r, gicd_reg); > + domain_crash_synchronous(); > + return 0; > + > +write_ignore: > + if ( dabt.size != DABT_WORD ) goto bad_width; > + return 1; > + > +write_ignore_64: > + if ( dabt.size != DABT_DOUBLE_WORD ) goto bad_width; > + return 1; > +} > + > +static const struct mmio_handler_ops vgic_rdistr_mmio_handler = { > + .read_handler = vgic_v3_rdistr_mmio_read, > + .write_handler = vgic_v3_rdistr_mmio_write, > +}; > + > +static const struct mmio_handler_ops vgic_distr_mmio_handler = { > + .read_handler = vgic_v3_distr_mmio_read, > + .write_handler = vgic_v3_distr_mmio_write, > +}; > + > +static int vgicv3_get_irq_priority(struct vcpu *v, unsigned int irq) > +{ > + int priority; > + struct vgic_irq_rank *rank; > + > + rank = vgic_rank_offset(v, 8, (irq / 4) * 4, DABT_WORD); Call vgic_rank_irq instead. You could also add an assert about the rank being locked like in the v2 case. > + priority = vgic_byte_read(rank->ipriority[REG_RANK_INDEX(8, > + irq, DABT_WORD)], 0, irq & > 0x3); > + > + return priority; > +} > + > +static int vgicv3_vcpu_init(struct vcpu *v) > +{ > + int i; > + uint64_t affinity; > + > + /* For SGI and PPI the target is always this CPU */ > + affinity = (MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 3) << 32 | > + MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 2) << 16 | > + MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 1) << 8 | > + MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 0)); > + > + for ( i = 0 ; i < 32 ; i++ ) > + v->arch.vgic.private_irqs->v3.irouter[i] = affinity; > + > + return 0; > +} > + > +static int vgicv3_domain_init(struct domain *d) > +{ > + int i; > + > + /* We rely on gicv init to get dbase and size */ > + register_mmio_handler(d, &vgic_distr_mmio_handler, d->arch.vgic.dbase, > + d->arch.vgic.dbase_size); > + > + /* > + * Register mmio handler per redistributor region but not for > + * every sgi rdist region which is per core. > + * The redistributor region encompasses per core sgi region. > + */ > + for ( i = 0; i < d->arch.vgic.rdist_count; i++ ) > + register_mmio_handler(d, &vgic_rdistr_mmio_handler, > + d->arch.vgic.rbase[i], d->arch.vgic.rbase_size[i]); > + > + return 0; > +} > + > +static const struct vgic_ops v3_ops = { > + .vcpu_init = vgicv3_vcpu_init, > + .domain_init = vgicv3_domain_init, > + .get_irq_priority = vgicv3_get_irq_priority, > + .get_target_vcpu = vgicv3_get_target_vcpu, > +}; > + > +int vgic_v3_init(struct domain *d) > +{ > + register_vgic_ops(d, &v3_ops); > + > + return 0; > +} > + > +/* > + * Local variables: > + * mode: C > + * c-file-style: "BSD" > + * c-basic-offset: 4 > + * indent-tabs-mode: nil > + * End: > + */ > diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c > index 30faefe..fc7c6a5 100644 > --- a/xen/arch/arm/vgic.c > +++ b/xen/arch/arm/vgic.c > @@ -76,6 +76,12 @@ int domain_vgic_init(struct domain *d) > > switch ( gic_hw_version() ) > { > +#ifdef CONFIG_ARM_64 > + case GIC_V3: > + if ( vgic_v3_init(d) ) > + return -ENODEV; > + break; > +#endif > case GIC_V2: > if ( vgic_v2_init(d) ) > return -ENODEV; > diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h > index a9f1943..60651cc 100644 > --- a/xen/include/asm-arm/vgic.h > +++ b/xen/include/asm-arm/vgic.h > @@ -88,7 +88,14 @@ struct vgic_irq_rank { > uint32_t ienable, iactive, ipend, pendsgi; > uint32_t icfg[2]; > uint32_t ipriority[8]; > - uint32_t itargets[8]; > + union { > + struct { > + uint32_t itargets[8]; > + }v2; > + struct { > + uint64_t irouter[32]; > + }v3; > + }; > }; > > struct vgic_ops { > @@ -120,6 +127,14 @@ static inline int REG_RANK_NR(int b, uint32_t n) > { > switch ( b ) > { > + /* > + * IRQ ranks are of size 32. So n cannot be shifter beyond 5 for 32 ^shifted > + * and above. For 64-bit n is already shifted DBAT_DOUBLE_WORD > + * by the caller > + */ > + case 64: > + 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; _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |