[Android-virt] [PATCH 13/15] ARM: KVM: Make VGIC distributor MMIO table relative

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Change the vgic_ranges table to be relative to a per VM
vgic_dist_base. It makes it faster to bail out if the
access is out of range, and makes it possible to have multiple
VMs using different GIC base addresses.

Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
---
 arch/arm/include/asm/kvm_vgic.h |    2 +
 arch/arm/kvm/vgic.c             |   49 +++++++++++++++++++++++---------------
 2 files changed, 32 insertions(+), 19 deletions(-)

diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index 354e528..4e7f6c3 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -85,6 +85,8 @@ struct vgic_dist {
 	spinlock_t		lock;
 
 	void __iomem		*vctrl_base;
+	unsigned long		vgic_dist_base;
+	unsigned long		vgic_dist_size;
 
 	u32			enabled;
 
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index f26b091..1b4f6b7 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -290,62 +290,62 @@ struct mmio_range {
 
 static const struct mmio_range vgic_ranges[] = {
 	{			/* CTRL, TYPER, IIDR */
-		.base		= VGIC_DIST_BASE,
+		.base		= 0,
 		.len		= 12,
 		.handle_mmio	= handle_mmio_misc,
 	},
 	{			/* IGROUPRn */
-		.base		= VGIC_DIST_BASE + 0x80,
+		.base		= 0x80,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_group_reg,
 	},
 	{			/* ISENABLERn */
-		.base		= VGIC_DIST_BASE + 0x100,
+		.base		= 0x100,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_set_enable_reg,
 	},
 	{			/* ICENABLERn */
-		.base		= VGIC_DIST_BASE + 0x180,
+		.base		= 0x180,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_clear_enable_reg,
 	},
 	{			/* ISPENDRn */
-		.base		= VGIC_DIST_BASE + 0x200,
+		.base		= 0x200,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_set_pending_reg,
 	},
 	{			/* ICPENDRn */
-		.base		= VGIC_DIST_BASE + 0x280,
+		.base		= 0x280,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_clear_pending_reg,
 	},
 	{			/* ISACTIVERn */
-		.base		= VGIC_DIST_BASE + 0x300,
+		.base		= 0x300,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_set_active_reg,
 	},
 	{			/* ICACTIVERn */
-		.base		= VGIC_DIST_BASE + 0x380,
+		.base		= 0x380,
 		.len		= VGIC_NR_IRQS / 8,
 		.handle_mmio	= handle_mmio_clear_active_reg,
 	},
 	{			/* IPRIORITYRn */
-		.base		= VGIC_DIST_BASE + 0x400,
+		.base		= 0x400,
 		.len		= VGIC_NR_IRQS,
 		.handle_mmio	= handle_mmio_priority_reg,
 	},
 	{			/* ITARGETSRn */
-		.base		= VGIC_DIST_BASE + 0x800,
+		.base		= 0x800,
 		.len		= VGIC_NR_IRQS,
 		.handle_mmio	= handle_mmio_target_reg,
 	},
 	{			/* ICFGRn */
-		.base		= VGIC_DIST_BASE + 0xC00,
+		.base		= 0xC00,
 		.len		= VGIC_NR_IRQS / 4,
 		.handle_mmio	= handle_mmio_cfg_reg,
 	},
 	{			/* SGIRn */
-		.base		= VGIC_DIST_BASE + 0xF00,
+		.base		= 0xF00,
 		.len		= 4,
 		.handle_mmio	= handle_mmio_sgi_reg,
 	},
@@ -354,12 +354,14 @@ static const struct mmio_range vgic_ranges[] = {
 
 static const
 struct mmio_range *find_matching_range(const struct mmio_range *ranges,
-				       struct kvm_run *run)
+				       struct kvm_run *run, unsigned long base)
 {
 	const struct mmio_range *r = ranges;
+	unsigned long addr = run->mmio.phys_addr - base;
+
 	while (r->len) {
-		if (run->mmio.phys_addr >= r->base &&
-		    (run->mmio.phys_addr + run->mmio.len) <= (r->base + r->len))
+		if (addr >= r->base &&
+		    (addr + run->mmio.len) <= (r->base + r->len))
 			return r;
 		r++;
 	}
@@ -370,18 +372,25 @@ struct mmio_range *find_matching_range(const struct mmio_range *ranges,
 int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
 	const struct mmio_range *range;
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	unsigned long base = dist->vgic_dist_base;
 
-	if (!irqchip_in_kernel(vcpu->kvm))
+	if (!irqchip_in_kernel(vcpu->kvm) ||
+	    run->mmio.phys_addr < base ||
+	    (run->mmio.phys_addr + run->mmio.len) > (base + dist->vgic_dist_size))
 		return KVM_EXIT_MMIO;
 
-	range = find_matching_range(vgic_ranges, run);
-	if (!range || !range->handle_mmio)
+	range = find_matching_range(vgic_ranges, run, base);
+	if (unlikely(!range || !range->handle_mmio)) {
+		pr_warn("Unhandled access %d %08llx %d\n",
+			run->mmio.is_write, run->mmio.phys_addr, run->mmio.len);
 		return KVM_EXIT_MMIO;
+	}
 
 	spin_lock(&vcpu->kvm->arch.vgic.lock);
 	pr_debug("emulating %d %08llx %d\n", run->mmio.is_write,
 		 run->mmio.phys_addr, run->mmio.len);
-	range->handle_mmio(vcpu, run, run->mmio.phys_addr - range->base);
+	range->handle_mmio(vcpu, run, run->mmio.phys_addr - range->base - base);
 	kvm_handle_mmio_return(vcpu, run);
 	spin_unlock(&vcpu->kvm->arch.vgic.lock);
 
@@ -857,6 +866,8 @@ int kvm_vgic_init(struct kvm *kvm)
 
 	spin_lock_init(&kvm->arch.vgic.lock);
 	kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
+	kvm->arch.vgic.vgic_dist_base = VGIC_DIST_BASE;
+	kvm->arch.vgic.vgic_dist_size = VGIC_DIST_SIZE;
 
 	ret = kvm_phys_addr_ioremap(kvm, VGIC_CPU_BASE,
 				    vcpu_res.start, VGIC_CPU_SIZE);
-- 
1.7.7.1





[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux