[Android-virt] [PATCH 06/15] ARM: KVM: VGIC interrupt injection

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

 



Plug the interrupt injection code. Interrupts can now be generated
from user space.

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

diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index 939f3f8..e293f60 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -140,6 +140,7 @@ struct kvm_run;
 #ifdef CONFIG_KVM_ARM_VGIC
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq);
 int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
 int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run);
 
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index a573f16..c03e450 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -582,7 +582,28 @@ static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
-	return -EINVAL;
+	struct kvm_vcpu *vcpu = filp->private_data;
+	void __user *argp = (void __user *)arg;
+
+	switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_IRQ_LINE: {
+		struct kvm_irq_level irq_event;
+
+		if (copy_from_user(&irq_event, argp, sizeof irq_event))
+			return -EFAULT;
+
+		if (!irqchip_in_kernel(vcpu->kvm))
+			return -EINVAL;
+
+		if (irq_event.irq < 16 || irq_event.irq >= 32)
+			return -EINVAL;
+		return kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, &irq_event);
+	}
+#endif
+	default:
+		return -EINVAL;
+	}
 }
 
 int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
@@ -602,6 +623,15 @@ long kvm_arch_vm_ioctl(struct file *filp,
 
 		if (copy_from_user(&irq_event, argp, sizeof irq_event))
 			return -EFAULT;
+
+#ifdef CONFIG_KVM_ARM_VGIC
+		if (irqchip_in_kernel(kvm)) {
+			if (irq_event.irq < 32)
+				return -EINVAL;
+			return kvm_vgic_inject_irq(kvm, 0, &irq_event);
+		}
+#endif
+
 		return kvm_arch_vm_ioctl_irq_line(kvm, &irq_event);
 	}
 	default:
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index ccd8b69..a48b436 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -35,6 +35,7 @@
 #define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
 
 static void vgic_update_state(struct kvm *kvm);
+static void kvm_vgic_kick_vcpus(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 
 static void mmio_do_copy(struct kvm_run *run, u32 *reg, u32 offset, int mode)
@@ -371,6 +372,8 @@ int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	kvm_handle_mmio_return(vcpu, run);
 	spin_unlock(&vcpu->kvm->arch.vgic.lock);
 
+	kvm_vgic_kick_vcpus(vcpu->kvm);
+
 	return KVM_EXIT_UNKNOWN;
 }
 
@@ -671,3 +674,47 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 
 	return !!(atomic_read(&dist->irq_pending_on_cpu) & (1 << vcpu->vcpu_id));
 }
+
+static void kvm_vgic_kick_vcpus(struct kvm *kvm)
+{
+	int nrcpus = atomic_read(&kvm->online_vcpus);
+	int c;
+
+	/*
+	 * We've injected an interrupt, time to find out who deserves
+	 * a good kick...
+	 */
+	for (c = 0; c < nrcpus; c++) {
+		struct kvm_vcpu *vcpu = kvm_get_vcpu(kvm, c);
+
+		if (kvm_vgic_vcpu_pending_irq(vcpu)) {
+			vcpu->arch.wait_for_interrupts = 0;
+			kvm_vcpu_kick(vcpu);
+		}
+	}
+}
+
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq)
+{
+	int nrcpus = atomic_read(&kvm->online_vcpus);
+
+	if (WARN_ON(cpuid >= nrcpus))
+		return -EINVAL;
+
+	/* Only PPIs or SPIs */
+	if (WARN_ON(irq->irq >= VGIC_NR_IRQS || irq->irq < 16))
+		return -EINVAL;
+
+	if (!irq->level)
+		return 0;
+
+	pr_debug("Inject IRQ%d\n", irq->irq);
+	spin_lock(&kvm->arch.vgic.lock);
+	vgic_bitmap_set_irq_val(&kvm->arch.vgic.irq_pending, cpuid, irq->irq, 1);
+	vgic_update_state(kvm);
+	spin_unlock(&kvm->arch.vgic.lock);
+
+	kvm_vgic_kick_vcpus(kvm);
+
+	return 0;
+}
-- 
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