[PATCH 1/2] KVM: ARM: Sanitize interrupt injection API.

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

 



All interrupt injection is now based on the VM ioctl KVM_IRQ_LINE.  This
works semantically well for the GIC as we in fact raise/lower a line on
a machine component (the gic).  The IOCTL uses the follwing struct.

struct kvm_irq_level {
	union {
		__u32 irq;     /* GSI */
		__s32 status;  /* not used for KVM_IRQ_LEVEL */
	};
	__u32 level;           /* 0 or 1 */
};

ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip
(GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for
specific cpus.  The irq field is interpreted like this:

  bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
  field: | irq_type  | vcpu_index |   irq_number   |

The irq_type field has the following values:
- irq_type[0]: out-of-kernel GIC: irq_number 0 is IRQ, irq_number 1 is FIQ
- irq_type[1]: in-kernel GIC: SPI, irq_number between 32 and 1019 (incl.)
               (the vcpu_index field is ignored)
- irq_type[2]: in-kernel GIC: PPI, irq_number between 16 and 31 (incl.)

Signed-off-by: Christoffer Dall <c.dall@xxxxxxxxxxxxxxxxxxxxxx>
---
 Documentation/virtual/kvm/api.txt |   23 ++++----
 arch/arm/include/asm/kvm.h        |   28 +++++++---
 arch/arm/include/asm/kvm_vgic.h   |    3 +-
 arch/arm/kvm/arm.c                |  106 +++++++++++++++++++++++--------------
 arch/arm/kvm/vgic.c               |   23 +++-----
 5 files changed, 106 insertions(+), 77 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index a816bf4..d42f067 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -616,7 +616,7 @@ created.
 
 Capability: KVM_CAP_IRQCHIP
 Architectures: x86, ia64, arm
-Type: vm ioctl (and vcpu_ioctl on ARM)
+Type: vm ioctl
 Parameters: struct kvm_irq_level
 Returns: 0 on success, -1 on error
 
@@ -625,19 +625,18 @@ On some architectures it is required that an interrupt controller model has
 been previously created with KVM_CREATE_IRQCHIP.  Note that edge-triggered
 interrupts require the level to be set to 1 and then back to 0.
 
-ARM can signal an interrupt either at the CPU level, or at the
-in-kernel irqchip (GIC), with the following convention:
+ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip
+(GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for
+specific cpus.  The irq field is interpreted like this:
 
-- CPU signaling: ARM uses two types of interrupt lines per CPU: IRQ
-  and FIQ.  The value of the irq field should be (vcpu_index << 1) for
-  IRQs and ((vcpu_index << 1) | 1) for FIQs. As there's a limit of 8
-  CPUs, irq is between 0 and 15. This is a vm_ioctl.
+  bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
+  field: | irq_type  | vcpu_index |   irq_number   |
 
-- GIC signaling: If signaling a PPI, this is a vcpu_ioctl, and irq is
-  between 16 and 31. If signaling a SPI, this is a vm_ioctl, and irq
-  is above 31. Values between 0 and 15 would designate a SGI, which
-  should never be injected from user-space, thus avoiding any conflict
-  with the CPU signaling method.
+The irq_type field has the following values:
+- irq_type[0]: out-of-kernel GIC: irq_number 0 is IRQ, irq_number 1 is FIQ
+- irq_type[1]: in-kernel GIC: SPI, irq_number between 32 and 1019 (incl.)
+               (the vcpu_index field is ignored)
+- irq_type[2]: in-kernel GIC: PPI, irq_number between 16 and 31 (incl.)
 
 In both cases, level is used to raise/lower the line.
 
diff --git a/arch/arm/include/asm/kvm.h b/arch/arm/include/asm/kvm.h
index d040a2a..a41cc4b 100644
--- a/arch/arm/include/asm/kvm.h
+++ b/arch/arm/include/asm/kvm.h
@@ -25,14 +25,6 @@
 #define __KVM_HAVE_IRQ_LINE
 
 /*
- * KVM_IRQ_LINE macros to set/read IRQ/FIQ for specific VCPU index.
- */
-enum KVM_ARM_IRQ_LINE_TYPE {
-	KVM_ARM_IRQ_LINE = 0,
-	KVM_ARM_FIQ_LINE = 1,
-};
-
-/*
  * Modes used for short-hand mode determinition in the world-switch code and
  * in emulation code.
  *
@@ -116,4 +108,24 @@ struct kvm_msr_list {
 #define KVM_ARM_MSR_32_CRN_MASK		0x00000780
 #define KVM_ARM_MSR_32_OPC1_MASK	0x00003800
 
+/* KVM_IRQ_LINE irq field index values */
+#define KVM_ARM_IRQ_TYPE_SHIFT		24
+#define KVM_ARM_IRQ_TYPE_MASK		0xff
+#define KVM_ARM_IRQ_VCPU_SHIFT		16
+#define KVM_ARM_IRQ_VCPU_MASK		0xff
+#define KVM_ARM_IRQ_NUM_SHIFT		0
+#define KVM_ARM_IRQ_NUM_MASK		0xffff
+
+/* irq_type field */
+#define KVM_ARM_IRQ_TYPE_CPU		0
+#define KVM_ARM_IRQ_TYPE_SPI		1
+#define KVM_ARM_IRQ_TYPE_PPI		2
+
+/* out-of-kernel GIC cpu interrupt injection irq_number field */
+#define KVM_ARM_IRQ_CPU_IRQ		0
+#define KVM_ARM_IRQ_CPU_FIQ		1
+
+/* Highest supported SPI, from VGIC_NR_IRQS */
+#define KVM_ARM_IRQ_GIC_MAX		127
+
 #endif /* __ARM_KVM_H__ */
diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index 633e697..5efc94d 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -244,7 +244,8 @@ int kvm_vgic_init(struct kvm *kvm);
 void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
 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_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
+			bool level);
 int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
 bool 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 4a01b42..b296834 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -701,37 +701,21 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	return ret;
 }
 
-int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level)
 {
-	unsigned int vcpu_idx;
-	struct kvm_vcpu *vcpu;
-	unsigned long *ptr;
-	bool set;
 	int bit_index;
+	bool set;
+	unsigned long *ptr;
 
-	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;
-
-	vcpu = kvm_get_vcpu(kvm, vcpu_idx);
-	if (!vcpu)
-		return -EINVAL;
-
-	trace_kvm_set_irq(irq_level->irq, irq_level->level, 0);
+	trace_kvm_set_irq(number, level, 0);
 
-	if ((irq_level->irq & 1) == KVM_ARM_IRQ_LINE)
+	if (number == KVM_ARM_IRQ_CPU_IRQ)
 		bit_index = ffs(HCR_VI) - 1;
-	else /* KVM_ARM_FIQ_LINE */
+	else /* KVM_ARM_IRQ_CPU_FIQ */
 		bit_index = ffs(HCR_VF) - 1;
 
 	ptr = (unsigned long *)&vcpu->arch.irq_lines;
-	if (irq_level->level)
+	if (level)
 		set = test_and_set_bit(bit_index, ptr);
 	else
 		set = test_and_clear_bit(bit_index, ptr);
@@ -739,7 +723,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 	/*
 	 * If we didn't change anything, no need to wake up or kick other CPUs
 	 */
-	if (!!set == !!irq_level->level)
+	if (set == level)
 		return 0;
 
 	/*
@@ -752,6 +736,60 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 	return 0;
 }
 
+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
+{
+	u32 irq = irq_level->irq;
+	unsigned int irq_type, vcpu_idx, irq_num;
+	int nrcpus = atomic_read(&kvm->online_vcpus);
+	struct kvm_vcpu *vcpu = NULL;
+	bool level = irq_level->level;
+
+	irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK;
+	vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK;
+	irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK;
+
+	if (irq_type == KVM_ARM_IRQ_TYPE_CPU ||
+	    irq_type == KVM_ARM_IRQ_TYPE_PPI) {
+		if (vcpu_idx >= nrcpus)
+			return -EINVAL;
+
+		vcpu = kvm_get_vcpu(kvm, vcpu_idx);
+		if (!vcpu)
+			return -EINVAL;
+	}
+
+	switch (irq_type) {
+	case KVM_ARM_IRQ_TYPE_CPU:
+		if (irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
+			return -EINVAL;
+
+		return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_ARM_IRQ_TYPE_PPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 16 || irq_num > 31)
+			return -EINVAL;
+
+		return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+	case KVM_ARM_IRQ_TYPE_SPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+			return -EINVAL;
+
+		return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
+	}
+
+	return -EINVAL;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -797,21 +835,6 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 			return -EFAULT;
 		return kvm_arm_set_msrs(vcpu, umsrs->entries, msrs.nmsrs);
 	}
-#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;
 	}
@@ -825,15 +848,16 @@ 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)
 {
-	struct kvm *kvm = filp->private_data;
 
 	switch (ioctl) {
 #ifdef CONFIG_KVM_ARM_VGIC
-	case KVM_CREATE_IRQCHIP:
+	case KVM_CREATE_IRQCHIP: {
+		struct kvm *kvm = filp->private_data;
 		if (vgic_present)
 			return kvm_vgic_init(kvm);
 		else
 			return -EINVAL;
+	}
 #endif
 	default:
 		return -EINVAL;
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 3c90712..3da7a44 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -866,35 +866,28 @@ static void vgic_kick_vcpus(struct kvm *kvm)
 	}
 }
 
-int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq)
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
+			bool level)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
-	int nrcpus = atomic_read(&kvm->online_vcpus);
 	int is_edge, state;
 	unsigned long flags;
 	bool updated_state = false;
 
-	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);
+	kvm_debug("Inject IRQ%d\n", irq_num);
 	spin_lock_irqsave(&dist->lock, flags);
-	is_edge = vgic_irq_is_edge(dist, irq->irq);
-	state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq->irq);
+	is_edge = vgic_irq_is_edge(dist, irq_num);
+	state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq_num);
 
 	/*
 	 * 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)) {
+	if ((!is_edge && (state ^ level)) ||
+	    (is_edge && !state && level)) {
 		vgic_bitmap_set_irq_val(&dist->irq_state, cpuid,
-					irq->irq, !!irq->level);
+					irq_num, level);
 		vgic_update_state(kvm);
 		updated_state = true;
 	}
-- 
1.7.9.5

_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm



[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