mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
xen/arm: Add virtual GICv3 support
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@caviumnetworks.com> Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
This commit is contained in:
1 parent
bc183a0235
commit
73b9bdfd2a
5 files changed
+988
-13
No files matched your search
@@ -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
|
||||
|
||||
+11
-12
@@ -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. */
|
||||
@@ -538,7 +537,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))
|
||||
@@ -553,8 +552,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,
|
||||
|
||||
@@ -0,0 +1,953 @@
|
||||
/*
|
||||
* 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@caviumnetworks.com>
|
||||
* 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 *vgic_v3_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);
|
||||
*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 vgic_v3_get_irq_priority(struct vcpu *v, unsigned int irq)
|
||||
{
|
||||
int priority;
|
||||
struct vgic_irq_rank *rank = vgic_rank_irq(v, irq);
|
||||
|
||||
ASSERT(spin_is_locked(&rank->lock));
|
||||
priority = vgic_byte_read(rank->ipriority[(irq%32)/4], 0, irq % 4);
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
static int vgic_v3_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 vgic_v3_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 = vgic_v3_vcpu_init,
|
||||
.domain_init = vgic_v3_domain_init,
|
||||
.get_irq_priority = vgic_v3_get_irq_priority,
|
||||
.get_target_vcpu = vgic_v3_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:
|
||||
*/
|
||||
@@ -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;
|
||||
|
||||
@@ -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 shifted beyond 5 for 32
|
||||
* 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;
|
||||
@@ -172,6 +187,7 @@ 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);
|
||||
int vgic_v3_init(struct domain *d);
|
||||
|
||||
extern int vcpu_vgic_free(struct vcpu *v);
|
||||
extern int vgic_to_sgi(struct vcpu *v, register_t sgir,
|
||||
|
||||