Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53692908 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

KVM: arm/arm64: vgic: Fix source vcpu issues for GICv2 SGI



Now that we make sure we don't inject multiple instances of the
same GICv2 SGI at the same time, we've made another bug more
obvious:

If we exit with an active SGI, we completely lose track of which
vcpu it came from. On the next entry, we restore it with 0 as a
source, and if that wasn't the right one, too bad. While this
doesn't seem to trouble GIC-400, the architectural model gets
offended and doesn't deactivate the interrupt on EOI.

Another connected issue is that we will happilly make pending
an interrupt from another vcpu, overriding the above zero with
something that is just as inconsistent. Don't do that.

The final issue is that we signal a maintenance interrupt when
no pending interrupts are present in the LR. Assuming we've fixed
the two issues above, we end-up in a situation where we keep
exiting as soon as we've reached the active state, and not be
able to inject the following pending.

The fix comes in 3 parts:
- GICv2 SGIs have their source vcpu saved if they are active on
  exit, and restored on entry
- Multi-SGIs cannot go via the Pending+Active state, as this would
  corrupt the source field
- Multi-SGIs are converted to using MI on EOI instead of NPIE

Fixes: 16ca6a60 ("KVM: arm/arm64: vgic: Don't populate multiple LRs with the same vintid")
Reported-by: default avatarMark Rutland <mark.rutland@arm.com>
Tested-by: default avatarMark Rutland <mark.rutland@arm.com>
Reviewed-by: default avatarChristoffer Dall <christoffer.dall@arm.com>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent 85bd0ba1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ struct vgic_irq {
		u32 mpidr;			/* GICv3 target VCPU */
	};
	u8 source;			/* GICv2 SGIs only */
	u8 active_source;		/* GICv2 SGIs only */
	u8 priority;
	enum vgic_irq_config config;	/* Level or edge */

+8 −2
Original line number Diff line number Diff line
@@ -289,10 +289,16 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
	       irq->vcpu->cpu != -1) /* VCPU thread is running */
		cond_resched_lock(&irq->irq_lock);

	if (irq->hw)
	if (irq->hw) {
		vgic_hw_irq_change_active(vcpu, irq, active, !requester_vcpu);
	else
	} else {
		u32 model = vcpu->kvm->arch.vgic.vgic_model;

		irq->active = active;
		if (model == KVM_DEV_TYPE_ARM_VGIC_V2 &&
		    active && vgic_irq_is_sgi(irq->intid))
			irq->active_source = requester_vcpu->vcpu_id;
	}

	if (irq->active)
		vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
+22 −16
Original line number Diff line number Diff line
@@ -37,13 +37,6 @@ void vgic_v2_init_lrs(void)
		vgic_v2_write_lr(i, 0);
}

void vgic_v2_set_npie(struct kvm_vcpu *vcpu)
{
	struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;

	cpuif->vgic_hcr |= GICH_HCR_NPIE;
}

void vgic_v2_set_underflow(struct kvm_vcpu *vcpu)
{
	struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
@@ -71,13 +64,18 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
	int lr;
	unsigned long flags;

	cpuif->vgic_hcr &= ~(GICH_HCR_UIE | GICH_HCR_NPIE);
	cpuif->vgic_hcr &= ~GICH_HCR_UIE;

	for (lr = 0; lr < vgic_cpu->used_lrs; lr++) {
		u32 val = cpuif->vgic_lr[lr];
		u32 intid = val & GICH_LR_VIRTUALID;
		u32 cpuid, intid = val & GICH_LR_VIRTUALID;
		struct vgic_irq *irq;

		/* Extract the source vCPU id from the LR */
		cpuid = val & GICH_LR_PHYSID_CPUID;
		cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
		cpuid &= 7;

		/* Notify fds when the guest EOI'ed a level-triggered SPI */
		if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
			kvm_notify_acked_irq(vcpu->kvm, 0,
@@ -90,18 +88,17 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
		/* Always preserve the active bit */
		irq->active = !!(val & GICH_LR_ACTIVE_BIT);

		if (irq->active && vgic_irq_is_sgi(intid))
			irq->active_source = cpuid;

		/* Edge is the only case where we preserve the pending bit */
		if (irq->config == VGIC_CONFIG_EDGE &&
		    (val & GICH_LR_PENDING_BIT)) {
			irq->pending_latch = true;

			if (vgic_irq_is_sgi(intid)) {
				u32 cpuid = val & GICH_LR_PHYSID_CPUID;

				cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
			if (vgic_irq_is_sgi(intid))
				irq->source |= (1 << cpuid);
		}
		}

		/*
		 * Clear soft pending state when level irqs have been acked.
@@ -152,8 +149,15 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
	u32 val = irq->intid;
	bool allow_pending = true;

	if (irq->active)
	if (irq->active) {
		val |= GICH_LR_ACTIVE_BIT;
		if (vgic_irq_is_sgi(irq->intid))
			val |= irq->active_source << GICH_LR_PHYSID_CPUID_SHIFT;
		if (vgic_irq_is_multi_sgi(irq)) {
			allow_pending = false;
			val |= GICH_LR_EOI;
		}
	}

	if (irq->hw) {
		val |= GICH_LR_HW;
@@ -190,8 +194,10 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
			BUG_ON(!src);
			val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
			irq->source &= ~(1 << (src - 1));
			if (irq->source)
			if (irq->source) {
				irq->pending_latch = true;
				val |= GICH_LR_EOI;
			}
		}
	}

+29 −20
Original line number Diff line number Diff line
@@ -27,13 +27,6 @@ static bool group1_trap;
static bool common_trap;
static bool gicv4_enable;

void vgic_v3_set_npie(struct kvm_vcpu *vcpu)
{
	struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;

	cpuif->vgic_hcr |= ICH_HCR_NPIE;
}

void vgic_v3_set_underflow(struct kvm_vcpu *vcpu)
{
	struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
@@ -55,17 +48,23 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
	int lr;
	unsigned long flags;

	cpuif->vgic_hcr &= ~(ICH_HCR_UIE | ICH_HCR_NPIE);
	cpuif->vgic_hcr &= ~ICH_HCR_UIE;

	for (lr = 0; lr < vgic_cpu->used_lrs; lr++) {
		u64 val = cpuif->vgic_lr[lr];
		u32 intid;
		u32 intid, cpuid;
		struct vgic_irq *irq;
		bool is_v2_sgi = false;

		if (model == KVM_DEV_TYPE_ARM_VGIC_V3)
		cpuid = val & GICH_LR_PHYSID_CPUID;
		cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;

		if (model == KVM_DEV_TYPE_ARM_VGIC_V3) {
			intid = val & ICH_LR_VIRTUAL_ID_MASK;
		else
		} else {
			intid = val & GICH_LR_VIRTUALID;
			is_v2_sgi = vgic_irq_is_sgi(intid);
		}

		/* Notify fds when the guest EOI'ed a level-triggered IRQ */
		if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
@@ -81,19 +80,17 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
		/* Always preserve the active bit */
		irq->active = !!(val & ICH_LR_ACTIVE_BIT);

		if (irq->active && is_v2_sgi)
			irq->active_source = cpuid;

		/* Edge is the only case where we preserve the pending bit */
		if (irq->config == VGIC_CONFIG_EDGE &&
		    (val & ICH_LR_PENDING_BIT)) {
			irq->pending_latch = true;

			if (vgic_irq_is_sgi(intid) &&
			    model == KVM_DEV_TYPE_ARM_VGIC_V2) {
				u32 cpuid = val & GICH_LR_PHYSID_CPUID;

				cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT;
			if (is_v2_sgi)
				irq->source |= (1 << cpuid);
		}
		}

		/*
		 * Clear soft pending state when level irqs have been acked.
@@ -133,10 +130,20 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
{
	u32 model = vcpu->kvm->arch.vgic.vgic_model;
	u64 val = irq->intid;
	bool allow_pending = true;
	bool allow_pending = true, is_v2_sgi;

	if (irq->active)
	is_v2_sgi = (vgic_irq_is_sgi(irq->intid) &&
		     model == KVM_DEV_TYPE_ARM_VGIC_V2);

	if (irq->active) {
		val |= ICH_LR_ACTIVE_BIT;
		if (is_v2_sgi)
			val |= irq->active_source << GICH_LR_PHYSID_CPUID_SHIFT;
		if (vgic_irq_is_multi_sgi(irq)) {
			allow_pending = false;
			val |= ICH_LR_EOI;
		}
	}

	if (irq->hw) {
		val |= ICH_LR_HW;
@@ -174,8 +181,10 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
			BUG_ON(!src);
			val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
			irq->source &= ~(1 << (src - 1));
			if (irq->source)
			if (irq->source) {
				irq->pending_latch = true;
				val |= ICH_LR_EOI;
			}
		}
	}

+7 −23
Original line number Diff line number Diff line
@@ -719,14 +719,6 @@ static inline void vgic_set_underflow(struct kvm_vcpu *vcpu)
		vgic_v3_set_underflow(vcpu);
}

static inline void vgic_set_npie(struct kvm_vcpu *vcpu)
{
	if (kvm_vgic_global_state.type == VGIC_V2)
		vgic_v2_set_npie(vcpu);
	else
		vgic_v3_set_npie(vcpu);
}

/* Requires the ap_list_lock to be held. */
static int compute_ap_list_depth(struct kvm_vcpu *vcpu,
				 bool *multi_sgi)
@@ -740,17 +732,15 @@ static int compute_ap_list_depth(struct kvm_vcpu *vcpu,
	DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&vgic_cpu->ap_list_lock));

	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
		int w;

		spin_lock(&irq->irq_lock);
		/* GICv2 SGIs can count for more than one... */
		if (vgic_irq_is_sgi(irq->intid) && irq->source) {
			int w = hweight8(irq->source);
		w = vgic_irq_get_lr_count(irq);
		spin_unlock(&irq->irq_lock);

		count += w;
		*multi_sgi |= (w > 1);
		} else {
			count++;
		}
		spin_unlock(&irq->irq_lock);
	}
	return count;
}
@@ -761,7 +751,6 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
	struct vgic_irq *irq;
	int count;
	bool npie = false;
	bool multi_sgi;
	u8 prio = 0xff;

@@ -791,11 +780,9 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
		if (likely(vgic_target_oracle(irq) == vcpu)) {
			vgic_populate_lr(vcpu, irq, count++);

			if (irq->source) {
				npie = true;
			if (irq->source)
				prio = irq->priority;
		}
		}

		spin_unlock(&irq->irq_lock);

@@ -807,9 +794,6 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
		}
	}

	if (npie)
		vgic_set_npie(vcpu);

	vcpu->arch.vgic_cpu.used_lrs = count;

	/* Nuke remaining LRs */
Loading