Subject: Re: [PATCH v4 35/49] KVM: arm64: GICv3: nv: Plug L1 LR sync into deactivation primitive

From: Marc Zyngier <[email-masked]>
Date: Sun, 26 Apr 2026 10:14:11 +0100

On Wed, 22 Apr 2026 15:57:44 +0100,
Vishnu Pajjuri <[email-masked]> wrote:
> 
> Hi Marc,
> 
> On 22-04-2026 12:25, Marc Zyngier wrote:
> >
> > Have you made progress on this? I can't reproduce it at all despite my
> > best effort. I'm perfectly happy to help, but you need to give me
> > *something* to go on.
> 
> 
> Thanks for your support!!
> 
> The issue is triggered as soon as the timer interrupt (IRQ 27) is
> deactivated. Preventing the deactivation of IRQ 27 during nested VGIC
> state transitions prevents the failure from reproducing.

Which level of deactivation? From L2 to L1? Or L1 to L0? The former
should just be a an update to the irq structure, while the latter is
effectively a write to ICC_DIR_EL1, and *that* is a new behaviour
introduced by this patch.

I wonder if your implementation is such that ICC_DIR_EL1 is trapped by
ICH_HCR_EL2.TDIR, which is allowed by the architecture, but that none
of the two implementations I have actually enforce (the trap only
applies to ICV_DIR_EL1). Can you try the hack below which disables the
traps much earlier, and let me know if that helps?

Even if that's the case, this should result in an EL2->EL2 exception,
and that should be caught as an unhandled exception in entry-common.c,
so something else is afoot.

Thanks,

	M.

diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c
index 5c69fa615823c..ee415c1038b4e 100644
--- a/arch/arm64/kvm/vgic/vgic-v3-nested.c
+++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c
@@ -280,6 +280,14 @@ void vgic_v3_sync_nested(struct kvm_vcpu *vcpu)
 	struct shadow_if *shadow_if = get_shadow_if();
 	int i;
 
+	/* We need these to be synchronised to generate the MI */
+	__vcpu_assign_sys_reg(vcpu, ICH_VMCR_EL2, read_sysreg_s(SYS_ICH_VMCR_EL2));
+	__vcpu_rmw_sys_reg(vcpu, ICH_HCR_EL2, &=, ~ICH_HCR_EL2_EOIcount);
+	__vcpu_rmw_sys_reg(vcpu, ICH_HCR_EL2, |=, read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EL2_EOIcount);
+
+	write_sysreg_s(0, SYS_ICH_HCR_EL2);
+	isb();
+
 	for_each_set_bit(i, &shadow_if->lr_map, kvm_vgic_global_state.nr_lr) {
 		u64 val, host_lr, lr;
 
@@ -309,14 +317,6 @@ void vgic_v3_sync_nested(struct kvm_vcpu *vcpu)
 		vgic_v3_deactivate(vcpu, FIELD_GET(ICH_LR_PHYS_ID_MASK, lr));
 	}
 
-	/* We need these to be synchronised to generate the MI */
-	__vcpu_assign_sys_reg(vcpu, ICH_VMCR_EL2, read_sysreg_s(SYS_ICH_VMCR_EL2));
-	__vcpu_rmw_sys_reg(vcpu, ICH_HCR_EL2, &=, ~ICH_HCR_EL2_EOIcount);
-	__vcpu_rmw_sys_reg(vcpu, ICH_HCR_EL2, |=, read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EL2_EOIcount);
-
-	write_sysreg_s(0, SYS_ICH_HCR_EL2);
-	isb();
-
 	vgic_v3_nested_update_mi(vcpu);
 }

-- 
Without deviation from the norm, progress is not possible.