Host kernels that expose the KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 capability allow injection of interrupts along with vcpu ids larger than 255. Let's encode the vpcu id on 12 bits according to the upgraded KVM_IRQ_LINE ABI when needed.
Given that we have two callsites that need to assemble the value for kvm_set_irq(), a new helper routine, kvm_arm_set_irq is introduced. Without that patch qemu exits with "kvm_set_irq: Invalid argument" message. Signed-off-by: Eric Auger <eric.au...@redhat.com> Reported-by: Zenghui Yu <yuzeng...@huawei.com> --- v2 -> v3: - remove if (cpu !=0), drop mask, as per Drew's suggestions --- hw/intc/arm_gic_kvm.c | 7 ++----- target/arm/cpu.c | 10 ++++------ target/arm/kvm.c | 12 ++++++++++++ target/arm/kvm_arm.h | 1 + 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index b56fda144f..9deb15e7e6 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -55,7 +55,7 @@ void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) * has separate fields in the irq number for type, * CPU number and interrupt number. */ - int kvm_irq, irqtype, cpu; + int irqtype, cpu; if (irq < (num_irq - GIC_INTERNAL)) { /* External interrupt. The kernel numbers these like the GIC @@ -72,10 +72,7 @@ void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) cpu = irq / GIC_INTERNAL; irq %= GIC_INTERNAL; } - kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) - | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq; - - kvm_set_irq(kvm_state, kvm_irq, !!level); + kvm_arm_set_irq(cpu, irqtype, irq, !!level); } static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2399c14471..13813fb213 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -576,16 +576,16 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT; uint32_t linestate_bit; + int irq_id; switch (irq) { case ARM_CPU_IRQ: - kvm_irq |= KVM_ARM_IRQ_CPU_IRQ; + irq_id = KVM_ARM_IRQ_CPU_IRQ; linestate_bit = CPU_INTERRUPT_HARD; break; case ARM_CPU_FIQ: - kvm_irq |= KVM_ARM_IRQ_CPU_FIQ; + irq_id = KVM_ARM_IRQ_CPU_FIQ; linestate_bit = CPU_INTERRUPT_FIQ; break; default: @@ -597,9 +597,7 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) } else { env->irq_line_state &= ~linestate_bit; } - - kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT; - kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); + kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level); #endif } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index b2eaa50b8d..b10581fa06 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -744,6 +744,18 @@ int kvm_arm_vgic_probe(void) } } +int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level) +{ + int kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) | irq; + int cpu_idx1 = cpu % 256; + int cpu_idx2 = cpu / 256; + + kvm_irq |= (cpu_idx1 << KVM_ARM_IRQ_VCPU_SHIFT) | + (cpu_idx2 << KVM_ARM_IRQ_VCPU2_SHIFT); + + return kvm_set_irq(kvm_state, kvm_irq, !!level); +} + int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index b3106c8600..b4e19457a0 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -253,6 +253,7 @@ int kvm_arm_vgic_probe(void); void kvm_arm_pmu_set_irq(CPUState *cs, int irq); void kvm_arm_pmu_init(CPUState *cs); +int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); #else -- 2.20.1