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