[Android-virt] [PATCH v2 06/11] 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              |   43 ++++++++++++++++++++++++++++--
 arch/arm/kvm/vgic.c             |   56 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index 481f4a9..48c27da 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -220,6 +220,7 @@ struct kvm_exit_mmio;
 #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,
 		     struct kvm_exit_mmio *mmio);
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index bdf0f86..7b27a4e 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -654,6 +654,12 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 	bool set;
 	int bit_nr;
 
+	if (irqchip_in_kernel(kvm)) {
+		if (irq_level->irq < 32)
+			return -EINVAL;
+		return kvm_vgic_inject_irq(kvm, 0, irq_level);
+	}
+
 	vcpu_idx = irq_level->irq >> 1;
 	if (vcpu_idx >= KVM_MAX_VCPUS)
 		return -EINVAL;
@@ -696,7 +702,28 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 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)
@@ -707,7 +734,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg)
 {
-	return -EINVAL;
+	struct kvm *kvm = filp->private_data;
+
+	switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CREATE_IRQCHIP:
+		if (vgic_present)
+			return kvm_vgic_init(kvm);
+		else
+			return -EINVAL;
+#endif
+	default:
+		return -EINVAL;
+	}
 }
 
 static void cpu_set_vector(void *vector)
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 8722efc..4d5d23a 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -69,6 +69,7 @@
 #define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
 
 static void vgic_update_state(struct kvm *kvm);
+static void vgic_kick_vcpus(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 
 static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq)
@@ -500,6 +501,8 @@ int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit
 	kvm_handle_mmio_return(vcpu, run);
 	spin_unlock(&vcpu->kvm->arch.vgic.lock);
 
+	vgic_kick_vcpus(vcpu->kvm);
+
 	return KVM_EXIT_UNKNOWN;
 }
 
@@ -795,3 +798,56 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 
 	return test_bit(1 << vcpu->vcpu_id, &dist->irq_pending_on_cpu);
 }
+
+static void vgic_kick_vcpus(struct kvm *kvm)
+{
+	struct kvm_vcpu *vcpu;
+	int c;
+
+	/*
+	 * We've injected an interrupt, time to find out who deserves
+	 * a good kick...
+	 */
+	kvm_for_each_vcpu(c, vcpu, kvm) {
+		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)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	int nrcpus = atomic_read(&kvm->online_vcpus);
+	int is_edge, state;
+
+	if (cpuid >= nrcpus)
+		return -EINVAL;
+
+	/* Only PPIs or SPIs */
+	if (irq->irq >= VGIC_NR_IRQS || irq->irq < 16)
+		return -EINVAL;
+
+	kvm_debug("Inject IRQ%d\n", irq->irq);
+	spin_lock(&dist->lock);
+	is_edge = vgic_irq_is_edge(dist, irq->irq);
+	state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq->irq);
+
+	/*
+	 * Inject an interrupt if:
+	 * - level triggered and we change level
+	 * - edge triggered and we have a rising edge
+	 */
+	if ((!is_edge && (state ^ !!irq->level)) ||
+	    (is_edge && !state && irq->level)) {
+		vgic_bitmap_set_irq_val(&dist->irq_state, cpuid,
+					irq->irq, !!irq->level);
+		vgic_update_state(kvm);
+	}
+	spin_unlock(&dist->lock);
+
+	vgic_kick_vcpus(kvm);
+
+	return 0;
+}
-- 
1.7.10.3





[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