diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 969ee523ab..20f59f41f1 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -26,7 +26,7 @@ obj-y += smpboot.o obj-y += smp.o obj-y += shutdown.o obj-y += traps.o -obj-y += vgic.o +obj-y += vgic.o vgic-v2.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 new file mode 100644 index 0000000000..2102e4305f --- /dev/null +++ b/xen/arch/arm/vgic-v2.c @@ -0,0 +1,506 @@ +/* + * xen/arch/arm/vgic-v2.c + * + * ARM Virtual Generic Interrupt Controller support v2 + * + * Ian Campbell + * Copyright (c) 2011 Citrix Systems. + * + * 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 +#include +#include + +static int vgic_v2_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 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. */ + 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_IIDR: + if ( dabt.size != DABT_WORD ) goto bad_width; + /* + * XXX Do we need a JEP106 manufacturer ID? + * Just use the physical h/w value for now + */ + *r = 0x0000043b; + return 1; + + /* Implementation defined -- read as zero */ + case 0x020 ... 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 != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISENABLER, DABT_WORD); + 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 != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICENABLER, DABT_WORD); + 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 != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISPENDR, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = vgic_byte_read(rank->ipend, dabt.sign, gicd_reg); + vgic_unlock_rank(v, rank); + 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, gicd_reg - GICD_ICPENDR, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = vgic_byte_read(rank->ipend, dabt.sign, gicd_reg); + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ISACTIVER ... GICD_ISACTIVERN: + if ( dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISACTIVER, DABT_WORD); + 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 != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->iactive; + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ITARGETSR ... GICD_ITARGETSRN: + 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); + *r = rank->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); + 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, gicd_reg - GICD_IPRIORITYR, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + + vgic_lock_rank(v, rank); + *r = rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, + DABT_WORD)]; + if ( dabt.size == DABT_BYTE ) + *r = vgic_byte_read(*r, dabt.sign, gicd_reg); + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ICFGR ... GICD_ICFGRN: + if ( dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 2, gicd_reg - GICD_ICFGR, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = rank->icfg[REG_RANK_INDEX(2, gicd_reg - GICD_ICFGR, DABT_WORD)]; + 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 != DABT_WORD ) goto bad_width; + /* Write only -- read unknown */ + *r = 0xdeadbeef; + return 1; + + case GICD_CPENDSGIR ... GICD_CPENDSGIRN: + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_CPENDSGIR, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = vgic_byte_read(rank->pendsgi, dabt.sign, gicd_reg); + vgic_unlock_rank(v, rank); + return 1; + + case GICD_SPENDSGIR ... GICD_SPENDSGIRN: + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_SPENDSGIR, DABT_WORD); + if ( rank == NULL) goto read_as_zero; + vgic_lock_rank(v, rank); + *r = vgic_byte_read(rank->pendsgi, dabt.sign, gicd_reg); + vgic_unlock_rank(v, rank); + return 1; + + /* Implementation defined -- read as zero */ + case 0xfd0 ... 0xfe4: + goto read_as_zero; + + case GICD_ICPIDR2: + if ( dabt.size != DABT_WORD ) goto bad_width; + printk("vGICD: unhandled read from ICPIDR2\n"); + return 0; + + /* Implementation defined -- read as zero */ + case 0xfec ... 0xffc: + goto read_as_zero; + + /* Reserved -- read as zero */ + case 0x00c ... 0x01c: + case 0x040 ... 0x07c: + case 0x7fc: + case 0xbfc: + case 0xf04 ... 0xf0c: + case 0xf30 ... 0xfcc: + goto read_as_zero; + + default: + printk("vGICD: unhandled read r%d offset %#08x\n", + dabt.reg, gicd_reg); + return 0; + } + +bad_width: + printk("vGICD: bad read width %d r%d offset %#08x\n", + dabt.size, dabt.reg, gicd_reg); + domain_crash_synchronous(); + return 0; + +read_as_zero: + if ( dabt.size != DABT_WORD ) goto bad_width; + *r = 0; + return 1; +} + +static int vgic_v2_to_sgi(struct vcpu *v, register_t sgir) +{ + + int virq; + int irqmode; + enum gic_sgi_mode sgi_mode; + unsigned long vcpu_mask = 0; + + irqmode = (sgir & GICD_SGI_TARGET_LIST_MASK) >> GICD_SGI_TARGET_LIST_SHIFT; + virq = (sgir & GICD_SGI_INTID_MASK); + vcpu_mask = (sgir & GICD_SGI_TARGET_MASK) >> GICD_SGI_TARGET_SHIFT; + + /* Map GIC sgi value to enum value */ + switch ( irqmode ) + { + case GICD_SGI_TARGET_LIST_VAL: + sgi_mode = SGI_TARGET_LIST; + break; + case GICD_SGI_TARGET_OTHERS_VAL: + sgi_mode = SGI_TARGET_OTHERS; + break; + case GICD_SGI_TARGET_SELF_VAL: + sgi_mode = SGI_TARGET_SELF; + break; + default: + BUG(); + } + + return vgic_to_sgi(v, sgir, sgi_mode, virq, vcpu_mask); +} + +static int vgic_v2_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 gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase); + uint32_t tr; + + 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; + + /* R/O -- write ignored */ + case GICD_TYPER: + case GICD_IIDR: + goto write_ignore; + + /* Implementation defined -- write ignored */ + case 0x020 ... 0x03c: + 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 != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISENABLER, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + tr = rank->ienable; + rank->ienable |= *r; + vgic_unlock_rank(v, rank); + /* The virtual irq is derived from register offset. + * The register difference is word difference. So divide by 2(DABT_WORD) + * to get Virtual irq number */ + vgic_enable_irqs(v, (*r) & (~tr), + (gicd_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, gicd_reg - GICD_ICENABLER, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + tr = rank->ienable; + rank->ienable &= ~*r; + vgic_unlock_rank(v, rank); + /* The virtual irq is derived from register offset. + * The register difference is word difference. So divide by 2(DABT_WORD) + * to get Virtual irq number */ + vgic_disable_irqs(v, (*r) & tr, + (gicd_reg - GICD_ICENABLER) >> DABT_WORD); + return 1; + + case GICD_ISPENDR ... GICD_ISPENDRN: + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + printk("vGICD: unhandled %s write %#"PRIregister" to ISPENDR%d\n", + dabt.size ? "word" : "byte", *r, gicd_reg - GICD_ISPENDR); + return 0; + + case GICD_ICPENDR ... GICD_ICPENDRN: + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + printk("vGICD: unhandled %s write %#"PRIregister" to ICPENDR%d\n", + dabt.size ? "word" : "byte", *r, gicd_reg - GICD_ICPENDR); + return 0; + + case GICD_ISACTIVER ... GICD_ISACTIVERN: + if ( dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISACTIVER, DABT_WORD); + 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 != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + rank->iactive &= ~*r; + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ITARGETSR ... GICD_ITARGETSR + 7: + /* SGI/PPI target is read only */ + goto write_ignore; + + case GICD_ITARGETSR + 8 ... GICD_ITARGETSRN: + 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 write_ignore; + vgic_lock_rank(v, rank); + if ( dabt.size == DABT_WORD ) + rank->itargets[REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, + DABT_WORD)] = *r; + else + vgic_byte_write(&rank->itargets[REG_RANK_INDEX(8, + gicd_reg - GICD_ITARGETSR, DABT_WORD)], *r, gicd_reg); + vgic_unlock_rank(v, rank); + 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, gicd_reg - GICD_IPRIORITYR, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + if ( dabt.size == DABT_WORD ) + rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, + DABT_WORD)] = *r; + else + vgic_byte_write(&rank->ipriority[REG_RANK_INDEX(8, + gicd_reg - GICD_IPRIORITYR, DABT_WORD)], *r, gicd_reg); + vgic_unlock_rank(v, rank); + return 1; + + case GICD_ICFGR: /* SGIs */ + goto write_ignore; + case GICD_ICFGR + 1: /* PPIs */ + /* It is implementation defined if these are writeable. We chose not */ + goto write_ignore; + case GICD_ICFGR + 2 ... GICD_ICFGRN: /* SPIs */ + if ( dabt.size != DABT_WORD ) goto bad_width; + rank = vgic_rank_offset(v, 2, gicd_reg - GICD_ICFGR, DABT_WORD); + if ( rank == NULL) goto write_ignore; + vgic_lock_rank(v, rank); + rank->icfg[REG_RANK_INDEX(2, gicd_reg - GICD_ICFGR, DABT_WORD)] = *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: + if ( dabt.size != DABT_WORD ) goto bad_width; + return vgic_v2_to_sgi(v, *r); + + case GICD_CPENDSGIR ... GICD_CPENDSGIRN: + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + printk("vGICD: unhandled %s write %#"PRIregister" to ICPENDSGIR%d\n", + dabt.size ? "word" : "byte", *r, gicd_reg - GICD_CPENDSGIR); + return 0; + + case GICD_SPENDSGIR ... GICD_SPENDSGIRN: + if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; + printk("vGICD: unhandled %s write %#"PRIregister" to ISPENDSGIR%d\n", + dabt.size ? "word" : "byte", *r, gicd_reg - GICD_SPENDSGIR); + return 0; + + /* Implementation defined -- write ignored */ + case 0xfd0 ... 0xfe4: + goto write_ignore; + + /* R/O -- write ignore */ + case GICD_ICPIDR2: + goto write_ignore; + + /* Implementation defined -- write ignored */ + case 0xfec ... 0xffc: + goto write_ignore; + + /* Reserved -- write ignored */ + case 0x00c ... 0x01c: + case 0x040 ... 0x07c: + case 0x7fc: + case 0xbfc: + case 0xf04 ... 0xf0c: + case 0xf30 ... 0xfcc: + goto write_ignore; + + default: + printk("vGICD: unhandled write r%d=%"PRIregister" offset %#08x\n", + dabt.reg, *r, gicd_reg); + return 0; + } + +bad_width: + printk("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; +} + +static const struct mmio_handler_ops vgic_v2_distr_mmio_handler = { + .read_handler = vgic_v2_distr_mmio_read, + .write_handler = vgic_v2_distr_mmio_write, +}; + +static int vgic_v2_vcpu_init(struct vcpu *v) +{ + int i; + + /* For SGI and PPI the target is always this CPU */ + for ( i = 0 ; i < 8 ; i++ ) + v->arch.vgic.private_irqs->itargets[i] = + (1<<(v->vcpu_id+0)) + | (1<<(v->vcpu_id+8)) + | (1<<(v->vcpu_id+16)) + | (1<<(v->vcpu_id+24)); + + return 0; +} + +static int vgic_v2_domain_init(struct domain *d) +{ + /* We rely on gicv_setup() to initialize dbase(vGIC distributor base) */ + register_mmio_handler(d, &vgic_v2_distr_mmio_handler, d->arch.vgic.dbase, + PAGE_SIZE); + + return 0; +} + +static const struct vgic_ops vgic_v2_ops = { + .vcpu_init = vgic_v2_vcpu_init, + .domain_init = vgic_v2_domain_init, +}; + +int vgic_v2_init(struct domain *d) +{ + register_vgic_ops(d, &vgic_v2_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 c8fbf9bf86..1948316619 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -31,14 +31,11 @@ #include #include -static int vgic_distr_mmio_read(struct vcpu *v, mmio_info_t *info); -static int vgic_distr_mmio_write(struct vcpu *v, mmio_info_t *info); - /* * Returns rank corresponding to a GICD_ register for * GICD_ with -bits-per-interrupt. */ -static struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, +struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s) { int rank = REG_RANK_NR(b, (n >> s)); @@ -56,11 +53,6 @@ static struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq) return vgic_rank_offset(v, 8, irq, DABT_WORD); } -static const struct mmio_handler_ops vgic_distr_mmio_handler = { - .read_handler = vgic_distr_mmio_read, - .write_handler = vgic_distr_mmio_write, -}; - int domain_vgic_init(struct domain *d) { int i; @@ -75,6 +67,16 @@ int domain_vgic_init(struct domain *d) else d->arch.vgic.nr_lines = 0; /* We don't need SPIs for the guest */ + switch ( gic_hw_version() ) + { + case GIC_V2: + if ( vgic_v2_init(d) ) + return -ENODEV; + break; + default: + return -ENODEV; + } + d->arch.vgic.shared_irqs = xzalloc_array(struct vgic_irq_rank, DOMAIN_NR_RANKS(d)); if ( d->arch.vgic.shared_irqs == NULL ) @@ -96,15 +98,16 @@ int domain_vgic_init(struct domain *d) for (i=0; iarch.vgic.shared_irqs[i].lock); - /* - * We rely on gicv_setup() to initialize dbase(vGIC distributor base) - */ - register_mmio_handler(d, &vgic_distr_mmio_handler, - d->arch.vgic.dbase, PAGE_SIZE); + d->arch.vgic.handler->domain_init(d); return 0; } +void register_vgic_ops(struct domain *d, const struct vgic_ops *ops) +{ + d->arch.vgic.handler = ops; +} + void domain_vgic_free(struct domain *d) { xfree(d->arch.vgic.shared_irqs); @@ -121,6 +124,8 @@ int vcpu_vgic_init(struct vcpu *v) spin_lock_init(&v->arch.vgic.private_irqs->lock); + v->domain->arch.vgic.handler->vcpu_init(v); + memset(&v->arch.vgic.pending_irqs, 0, sizeof(v->arch.vgic.pending_irqs)); for (i = 0; i < 32; i++) { @@ -128,13 +133,6 @@ int vcpu_vgic_init(struct vcpu *v) INIT_LIST_HEAD(&v->arch.vgic.pending_irqs[i].lr_queue); } - /* For SGI and PPI the target is always this CPU */ - for ( i = 0 ; i < 8 ; i++ ) - v->arch.vgic.private_irqs->itargets[i] = - (1<<(v->vcpu_id+0)) - | (1<<(v->vcpu_id+8)) - | (1<<(v->vcpu_id+16)) - | (1<<(v->vcpu_id+24)); INIT_LIST_HEAD(&v->arch.vgic.inflight_irqs); INIT_LIST_HEAD(&v->arch.vgic.lr_pending); spin_lock_init(&v->arch.vgic.lock); @@ -148,205 +146,7 @@ int vcpu_vgic_free(struct vcpu *v) 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 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. */ - 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_IIDR: - if ( dabt.size != DABT_WORD ) goto bad_width; - /* - * XXX Do we need a JEP106 manufacturer ID? - * Just use the physical h/w value for now - */ - *r = 0x0000043b; - return 1; - - /* Implementation defined -- read as zero */ - case 0x020 ... 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 != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISENABLER, DABT_WORD); - 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 != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICENABLER, DABT_WORD); - 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 != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISPENDR, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - vgic_lock_rank(v, rank); - *r = vgic_byte_read(rank->ipend, dabt.sign, gicd_reg); - vgic_unlock_rank(v, rank); - 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, gicd_reg - GICD_ICPENDR, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - vgic_lock_rank(v, rank); - *r = vgic_byte_read(rank->ipend, dabt.sign, gicd_reg); - vgic_unlock_rank(v, rank); - return 1; - - case GICD_ISACTIVER ... GICD_ISACTIVERN: - if ( dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISACTIVER, DABT_WORD); - 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 != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - vgic_lock_rank(v, rank); - *r = rank->iactive; - vgic_unlock_rank(v, rank); - return 1; - - case GICD_ITARGETSR ... GICD_ITARGETSRN: - 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); - *r = rank->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); - return 1; - - case GICD_IPRIORITYR ... GICD_IPRIORITYRN: - if ( dabt.size != 0 && dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 8, gicd_reg - GICD_IPRIORITYR, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - - vgic_lock_rank(v, rank); - *r = rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, - DABT_WORD)]; - if ( dabt.size == DABT_BYTE ) - *r = vgic_byte_read(*r, dabt.sign, gicd_reg); - vgic_unlock_rank(v, rank); - return 1; - - case GICD_ICFGR ... GICD_ICFGRN: - if ( dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 2, gicd_reg - GICD_ICFGR, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - vgic_lock_rank(v, rank); - *r = rank->icfg[REG_RANK_INDEX(2, gicd_reg - GICD_ICFGR, DABT_WORD)]; - 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 != DABT_WORD ) goto bad_width; - /* Write only -- read unknown */ - *r = 0xdeadbeef; - return 1; - - case GICD_CPENDSGIR ... GICD_CPENDSGIRN: - if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_CPENDSGIR, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - vgic_lock_rank(v, rank); - *r = vgic_byte_read(rank->pendsgi, dabt.sign, gicd_reg); - vgic_unlock_rank(v, rank); - return 1; - - case GICD_SPENDSGIR ... GICD_SPENDSGIRN: - if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_SPENDSGIR, DABT_WORD); - if ( rank == NULL) goto read_as_zero; - vgic_lock_rank(v, rank); - *r = vgic_byte_read(rank->pendsgi, dabt.sign, gicd_reg); - vgic_unlock_rank(v, rank); - return 1; - - /* Implementation defined -- read as zero */ - case 0xfd0 ... 0xfe4: - goto read_as_zero; - - case GICD_ICPIDR2: - if ( dabt.size != DABT_WORD ) goto bad_width; - printk("vGICD: unhandled read from ICPIDR2\n"); - return 0; - - /* Implementation defined -- read as zero */ - case 0xfec ... 0xffc: - goto read_as_zero; - - /* Reserved -- read as zero */ - case 0x00c ... 0x01c: - case 0x040 ... 0x07c: - case 0x7fc: - case 0xbfc: - case 0xf04 ... 0xf0c: - case 0xf30 ... 0xfcc: - goto read_as_zero; - - default: - printk("vGICD: unhandled read r%d offset %#08x\n", - dabt.reg, gicd_reg); - return 0; - } - -bad_width: - printk("vGICD: bad read width %d r%d offset %#08x\n", - dabt.size, dabt.reg, gicd_reg); - domain_crash_synchronous(); - return 0; - -read_as_zero: - if ( dabt.size != DABT_WORD ) goto bad_width; - *r = 0; - return 1; -} - -static void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n) +void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n) { const unsigned long mask = r; struct pending_irq *p; @@ -369,7 +169,7 @@ static void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n) } } -static void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n) +void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n) { const unsigned long mask = r; struct pending_irq *p; @@ -405,249 +205,63 @@ static void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n) } } -static int vgic_to_sgi(struct vcpu *v, register_t sgir) +/* TODO: unsigned long is used to fit vcpu_mask.*/ +int vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmode, int virq, + unsigned long vcpu_mask) { struct domain *d = v->domain; - int virtual_irq; - int filter; int vcpuid; int i; - unsigned long vcpu_mask = 0; ASSERT(d->max_vcpus < 8*sizeof(vcpu_mask)); - filter = (sgir & GICD_SGI_TARGET_LIST_MASK); - virtual_irq = (sgir & GICD_SGI_INTID_MASK); - ASSERT( virtual_irq < 16 ); + ASSERT( virq < 16 ); - switch ( filter ) + switch ( irqmode ) { - case GICD_SGI_TARGET_LIST: - vcpu_mask = (sgir & GICD_SGI_TARGET_MASK) >> GICD_SGI_TARGET_SHIFT; - break; - case GICD_SGI_TARGET_OTHERS: - for ( i = 0; i < d->max_vcpus; i++ ) - { - if ( i != current->vcpu_id && d->vcpu[i] != NULL && - is_vcpu_online(d->vcpu[i]) ) - set_bit(i, &vcpu_mask); - } - break; - case GICD_SGI_TARGET_SELF: - set_bit(current->vcpu_id, &vcpu_mask); - break; - default: - gdprintk(XENLOG_WARNING, "vGICD: unhandled GICD_SGIR write %"PRIregister" with wrong TargetListFilter field\n", - sgir); - return 0; + case SGI_TARGET_LIST: + break; + case SGI_TARGET_OTHERS: + /* + * We expect vcpu_mask to be 0 for SGI_TARGET_OTHERS and + * SGI_TARGET_SELF mode. So Force vcpu_mask to 0 + */ + vcpu_mask = 0; + for ( i = 0; i < d->max_vcpus; i++ ) + { + if ( i != current->vcpu_id && d->vcpu[i] != NULL && + is_vcpu_online(d->vcpu[i]) ) + set_bit(i, &vcpu_mask); + } + break; + case SGI_TARGET_SELF: + /* + * We expect vcpu_mask to be 0 for SGI_TARGET_OTHERS and + * SGI_TARGET_SELF mode. So Force vcpu_mask to 0 + */ + vcpu_mask = 0; + set_bit(current->vcpu_id, &vcpu_mask); + break; + default: + gdprintk(XENLOG_WARNING, + "vGICD:unhandled GICD_SGIR write %"PRIregister" \ + with wrong mode\n", sgir); + return 0; } for_each_set_bit( vcpuid, &vcpu_mask, d->max_vcpus ) { if ( d->vcpu[vcpuid] != NULL && !is_vcpu_online(d->vcpu[vcpuid]) ) { - gdprintk(XENLOG_WARNING, "vGICD: GICD_SGIR write r=%"PRIregister" vcpu_mask=%lx, wrong CPUTargetList\n", - sgir, vcpu_mask); + gdprintk(XENLOG_WARNING, "VGIC: write r=%"PRIregister" \ + vcpu_mask=%lx, wrong CPUTargetList\n", sgir, vcpu_mask); continue; } - vgic_vcpu_inject_irq(d->vcpu[vcpuid], virtual_irq); + vgic_vcpu_inject_irq(d->vcpu[vcpuid], virq); } 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 gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase); - uint32_t tr; - - 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; - - /* R/O -- write ignored */ - case GICD_TYPER: - case GICD_IIDR: - goto write_ignore; - - /* Implementation defined -- write ignored */ - case 0x020 ... 0x03c: - 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 != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISENABLER, DABT_WORD); - 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) >> DABT_WORD); - return 1; - - case GICD_ICENABLER ... GICD_ICENABLERN: - if ( dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICENABLER, DABT_WORD); - 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) >> DABT_WORD); - return 1; - - case GICD_ISPENDR ... GICD_ISPENDRN: - if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - printk("vGICD: unhandled %s write %#"PRIregister" to ISPENDR%d\n", - dabt.size ? "word" : "byte", *r, gicd_reg - GICD_ISPENDR); - return 0; - - case GICD_ICPENDR ... GICD_ICPENDRN: - if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - printk("vGICD: unhandled %s write %#"PRIregister" to ICPENDR%d\n", - dabt.size ? "word" : "byte", *r, gicd_reg - GICD_ICPENDR); - return 0; - - case GICD_ISACTIVER ... GICD_ISACTIVERN: - if ( dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ISACTIVER, DABT_WORD); - 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 != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICACTIVER, DABT_WORD); - if ( rank == NULL) goto write_ignore; - vgic_lock_rank(v, rank); - rank->iactive &= ~*r; - vgic_unlock_rank(v, rank); - return 1; - - case GICD_ITARGETSR ... GICD_ITARGETSR + 7: - /* SGI/PPI target is read only */ - goto write_ignore; - - case GICD_ITARGETSR + 8 ... GICD_ITARGETSRN: - 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 write_ignore; - vgic_lock_rank(v, rank); - if ( dabt.size == DABT_WORD ) - rank->itargets[REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, - DABT_WORD)] = *r; - else - { - tr = REG_RANK_INDEX(8, gicd_reg - GICD_ITARGETSR, DABT_WORD); - vgic_byte_write(&rank->itargets[tr], *r, gicd_reg); - } - vgic_unlock_rank(v, rank); - 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, gicd_reg - GICD_IPRIORITYR, DABT_WORD); - if ( rank == NULL) goto write_ignore; - vgic_lock_rank(v, rank); - if ( dabt.size == DABT_WORD ) - rank->ipriority[REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, - DABT_WORD)] = *r; - else - { - tr = REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, DABT_WORD); - vgic_byte_write(&rank->ipriority[tr], *r, gicd_reg); - } - vgic_unlock_rank(v, rank); - return 1; - - case GICD_ICFGR: /* SGIs */ - goto write_ignore; - case GICD_ICFGR + 1: /* PPIs */ - /* It is implementation defined if these are writeable. We chose not */ - goto write_ignore; - case GICD_ICFGR + 2 ... GICD_ICFGRN: /* SPIs */ - if ( dabt.size != DABT_WORD ) goto bad_width; - rank = vgic_rank_offset(v, 2, gicd_reg - GICD_ICFGR, DABT_WORD); - if ( rank == NULL) goto write_ignore; - vgic_lock_rank(v, rank); - rank->icfg[REG_RANK_INDEX(2, gicd_reg - GICD_ICFGR, DABT_WORD)] = *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: - if ( dabt.size != 2 ) - goto bad_width; - return vgic_to_sgi(v, *r); - - case GICD_CPENDSGIR ... GICD_CPENDSGIRN: - if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - printk("vGICD: unhandled %s write %#"PRIregister" to ICPENDSGIR%d\n", - dabt.size ? "word" : "byte", *r, gicd_reg - GICD_CPENDSGIR); - return 0; - - case GICD_SPENDSGIR ... GICD_SPENDSGIRN: - if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width; - printk("vGICD: unhandled %s write %#"PRIregister" to ISPENDSGIR%d\n", - dabt.size ? "word" : "byte", *r, gicd_reg - GICD_SPENDSGIR); - return 0; - - /* Implementation defined -- write ignored */ - case 0xfd0 ... 0xfe4: - goto write_ignore; - - /* R/O -- write ignore */ - case GICD_ICPIDR2: - goto write_ignore; - - /* Implementation defined -- write ignored */ - case 0xfec ... 0xffc: - goto write_ignore; - - /* Reserved -- write ignored */ - case 0x00c ... 0x01c: - case 0x040 ... 0x07c: - case 0x7fc: - case 0xbfc: - case 0xf04 ... 0xf0c: - case 0xf30 ... 0xfcc: - goto write_ignore; - - default: - printk("vGICD: unhandled write r%d=%"PRIregister" offset %#08x\n", - dabt.reg, *r, gicd_reg); - return 0; - } - -bad_width: - printk("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; -} - struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq) { struct pending_irq *n; diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h index 2a1e9763a2..32d0554ad2 100644 --- a/xen/include/asm-arm/domain.h +++ b/xen/include/asm-arm/domain.h @@ -76,6 +76,8 @@ struct arch_domain } virt_timer_base; struct { + /* GIC HW version specific vGIC driver handler */ + const struct vgic_ops *handler; /* * Covers access to other members of this struct _except_ for * shared_irqs where each member contains its own locking. diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 875729e538..a0c07bff4b 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -53,8 +53,11 @@ #define GICD_SGI_TARGET_LIST_SHIFT (24) #define GICD_SGI_TARGET_LIST_MASK (0x3UL << GICD_SGI_TARGET_LIST_SHIFT) #define GICD_SGI_TARGET_LIST (0UL<arch.vgic.nr_lines+31)/32) @@ -133,6 +140,8 @@ static inline void vgic_byte_write(uint32_t *reg, uint32_t var, int offset) *reg |= var; } +enum gic_sgi_mode; + /* * Offset of GICD_ with its rank, for GICD_ size with * -bits-per-interrupt. @@ -145,8 +154,16 @@ extern int vcpu_vgic_init(struct vcpu *v); extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int irq); extern void vgic_clear_pending_irqs(struct vcpu *v); extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq); +extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s); +extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n); +extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n); +extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops); +int vgic_v2_init(struct domain *d); extern int vcpu_vgic_free(struct vcpu *v); +extern int vgic_to_sgi(struct vcpu *v, register_t sgir, + enum gic_sgi_mode irqmode, int virq, + unsigned long vcpu_mask); #endif /* __ASM_ARM_VGIC_H__ */ /*