From ecc7f02499544ae879716be837af78260a6a10f7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:35:56 +0100 Subject: KVM: arm64: vgic: Don't reset cpuif/redist addresses at finalize time Although we are OK with rewriting idregs at finalize time, resetting the guest's cpuif (GICv3) or redistributor (GICv3) addresses once we start running the guest is a pretty bad idea. Move back this initialisation to vgic creation time. Reviewed-by: Sascha Bischoff Fixes: a258a383b9177 ("KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE") Link: https://patch.msgid.link/20260323174713.3183111-1-maz@kernel.org Link: https://patch.msgid.link/20260401103611.357092-2-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-init.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 47169604100f..34460179fb8a 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -147,6 +147,15 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: + INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions); + break; + } + /* * We've now created the GIC. Update the system register state * to accurately reflect what we've created. @@ -684,10 +693,8 @@ void kvm_vgic_finalize_idregs(struct kvm *kvm) switch (type) { case KVM_DEV_TYPE_ARM_VGIC_V2: - kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; break; case KVM_DEV_TYPE_ARM_VGIC_V3: - INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions); aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3); break; -- cgit v1.2.3 From d82d09d5ba4be0b5eb053b2ba2bc0e82c49cf2c8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:35:57 +0100 Subject: KVM: arm64: Don't skip per-vcpu NV initialisation Some GICv5-related rework have resulted in the NV sanitisation of registers being skipped for secondary vcpus, which is a pretty bad idea. Hoist the NV init early so that it is always executed. Reviewed-by: Sascha Bischoff Fixes: cbd8c958be54a ("KVM: arm64: Return early from kvm_finalize_sys_regs() if guest has run") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-3-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index e1001544d4f4..18e2d2fccedb 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -5772,6 +5772,12 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu) guard(mutex)(&kvm->arch.config_lock); + if (vcpu_has_nv(vcpu)) { + int ret = kvm_init_nv_sysregs(vcpu); + if (ret) + return ret; + } + if (kvm_vm_has_ran_once(kvm)) return 0; @@ -5820,12 +5826,6 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu) kvm_vgic_finalize_idregs(kvm); } - if (vcpu_has_nv(vcpu)) { - int ret = kvm_init_nv_sysregs(vcpu); - if (ret) - return ret; - } - return 0; } -- cgit v1.2.3 From 77acae60be60adddf33e4c7e9cf73291f64fb9e8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:35:58 +0100 Subject: arm64: Fix field references for ICH_PPI_DVIR[01]_EL2 The ICH_PPI_DVIR[01]_EL2 registers should refer to the ICH_PPI_DVIRx_EL2 fields, instead of ICH_PPI_DVIx_EL2. Reviewed-by: Sascha Bischoff Fixes: 2808a8337078f ("arm64/sysreg: Add remaining GICv5 ICC_ & ICH_ sysregs for KVM support") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-4-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/tools/sysreg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 51dcca5b2fa6..3b57cb692c5b 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -4888,11 +4888,11 @@ Field 0 DVI0 EndSysregFields Sysreg ICH_PPI_DVIR0_EL2 3 4 12 10 0 -Fields ICH_PPI_DVIx_EL2 +Fields ICH_PPI_DVIRx_EL2 EndSysreg Sysreg ICH_PPI_DVIR1_EL2 3 4 12 10 1 -Fields ICH_PPI_DVIx_EL2 +Fields ICH_PPI_DVIRx_EL2 EndSysreg SysregFields ICH_PPI_ENABLERx_EL2 -- cgit v1.2.3 From 76efe94b1c5cc9b5fac7c5c1096d03f1596c7267 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:35:59 +0100 Subject: KVM: arm64: Fix writeable mask for ID_AA64PFR2_EL1 The writeable mask for fields in ID_AA64PFR2_EL1 has been accidentally inverted, which isn't a very good idea. Restore the expected polarity. Reviewed-by: Sascha Bischoff Fixes: a258a383b9177 ("KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-5-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 18e2d2fccedb..6a96cb7ba9a3 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -3304,10 +3304,10 @@ static const struct sys_reg_desc sys_reg_descs[] = { ID_AA64PFR1_EL1_MPAM_frac | ID_AA64PFR1_EL1_MTE)), ID_FILTERED(ID_AA64PFR2_EL1, id_aa64pfr2_el1, - ~(ID_AA64PFR2_EL1_FPMR | - ID_AA64PFR2_EL1_MTEFAR | - ID_AA64PFR2_EL1_MTESTOREONLY | - ID_AA64PFR2_EL1_GCIE)), + (ID_AA64PFR2_EL1_FPMR | + ID_AA64PFR2_EL1_MTEFAR | + ID_AA64PFR2_EL1_MTESTOREONLY | + ID_AA64PFR2_EL1_GCIE)), ID_UNALLOCATED(4,3), ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0), ID_HIDDEN(ID_AA64SMFR0_EL1), -- cgit v1.2.3 From d70d4323dd9636e35696639f6b4c2b2735291516 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:00 +0100 Subject: KVM: arm64: Account for RESx bits in __compute_fgt() When computing Fine Grained Traps, it is preferable to account for the reserved bits. The HW will most probably ignore them, unless the bits have been repurposed to do something else. Use caution, and fold our view of the reserved bits in, Reviewed-by: Sascha Bischoff Fixes: c259d763e6b09 ("KVM: arm64: Account for RES1 bits in DECLARE_FEAT_MAP() and co") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260401103611.357092-6-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c index e14685343191..f35b8dddd7c1 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c @@ -1663,8 +1663,8 @@ static __always_inline void __compute_fgt(struct kvm_vcpu *vcpu, enum vcpu_sysre clear |= ~nested & m->nmask; } - val |= set; - val &= ~clear; + val |= set | m->res1; + val &= ~(clear | m->res0); *vcpu_fgt(vcpu, reg) = val; } -- cgit v1.2.3 From e63d0a32e7368f3eb935755db87add1bf000ea90 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:01 +0100 Subject: KVM: arm64: vgic-v5: Hold config_lock while finalizing GICv5 PPIs Finalizing the PPI state is done without holding any lock, which means that two vcpus can race against each other and have one zeroing the state while another one is setting it, or even maybe using it. Fixing this is done by: - holding the config lock while performing the initialisation - checking if SW_PPI has already been advertised, meaning that we have already completed the initialisation once Reviewed-by: Sascha Bischoff Fixes: 8f1fbe2fd2792 ("KVM: arm64: gic-v5: Finalize GICv5 PPIs and generate mask") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-7-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 2b6cd5c3f9c2..119d7d01d0e7 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -172,6 +172,16 @@ int vgic_v5_finalize_ppi_state(struct kvm *kvm) if (!vgic_is_v5(kvm)) return 0; + guard(mutex)(&kvm->arch.config_lock); + + /* + * If SW_PPI has been advertised, then we know we already + * initialised the whole thing, and we can return early. Yes, + * this is pretty hackish as far as state tracking goes... + */ + if (test_bit(GICV5_ARCH_PPI_SW_PPI, kvm->arch.vgic.gicv5_vm.vgic_ppi_mask)) + return 0; + /* The PPI state for all VCPUs should be the same. Pick the first. */ vcpu0 = kvm_get_vcpu(kvm, 0); -- cgit v1.2.3 From 170a77b4185a87cc7e02e404d22b9bf3f9923884 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:02 +0100 Subject: KVM: arm64: vgic-v5: Transfer edge pending state to ICH_PPI_PENDRx_EL2 While it is perfectly correct to leave the pending state of a level interrupt as is when queuing it (it is, after all, only driven by the line), edge pending state must be transfered, as nothing will lower it. Reviewed-by: Sascha Bischoff Fixes: 4d591252bacb2 ("KVM: arm64: gic-v5: Implement PPI interrupt injection") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-8-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 119d7d01d0e7..422741c86c6a 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -445,8 +445,11 @@ void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu) irq = vgic_get_vcpu_irq(vcpu, intid); - scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { __assign_bit(i, pendr, irq_is_pending(irq)); + if (irq->config == VGIC_CONFIG_EDGE) + irq->pending_latch = false; + } vgic_put_irq(vcpu->kvm, irq); } -- cgit v1.2.3 From 42d7eac8291d2724b3897141ab2f226c69b7923e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:03 +0100 Subject: KVM: arm64: vgic-v5: Cast vgic_apr to u32 to avoid undefined behaviours Passing a u64 to __builtin_ctz() is odd, and requires some digging to figure out why this construct is indeed safe as long as the HW is correct. But it is much easier to make it clear to the compiler by casting the u64 into an intermediate u32, and be done with the UD. Reviewed-by: Sascha Bischoff Fixes: 933e5288fa971 ("KVM: arm64: gic-v5: Check for pending PPIs") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-9-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 422741c86c6a..0f269321ece4 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -212,7 +212,7 @@ int vgic_v5_finalize_ppi_state(struct kvm *kvm) static u32 vgic_v5_get_effective_priority_mask(struct kvm_vcpu *vcpu) { struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; - u32 highest_ap, priority_mask; + u32 highest_ap, priority_mask, apr; /* * If the guest's CPU has not opted to receive interrupts, then the @@ -227,7 +227,8 @@ static u32 vgic_v5_get_effective_priority_mask(struct kvm_vcpu *vcpu) * priority. Explicitly use the 32-bit version here as we have 32 * priorities. 32 then means that there are no active priorities. */ - highest_ap = cpu_if->vgic_apr ? __builtin_ctz(cpu_if->vgic_apr) : 32; + apr = cpu_if->vgic_apr; + highest_ap = apr ? __builtin_ctz(apr) : 32; /* * An interrupt is of sufficient priority if it is equal to or -- cgit v1.2.3 From a4a645584793dbbb4e5a1a876800654a8883326e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:04 +0100 Subject: KVM: arm64: vgic-v5: Make the effective priority mask a strict limit The way the effective priority mask is compared to the priority of an interrupt to decide whether to wake-up or not, is slightly odd, and breaks at the limits. This could result in spurious wake-ups that are undesirable. Make the computed priority mask comparison a strict inequality, so that interrupts that have the same priority as the mask are not signalled. Fixes: 933e5288fa971 ("KVM: arm64: gic-v5: Check for pending PPIs") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-10-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-v5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 0f269321ece4..31040cfb61fc 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -367,7 +367,7 @@ bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu) scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) has_pending = (irq->enabled && irq_is_pending(irq) && - irq->priority <= priority_mask); + irq->priority < priority_mask); vgic_put_irq(vcpu->kvm, irq); -- cgit v1.2.3 From 848fa8373a53b0e5d871560743e13278da56fabc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:05 +0100 Subject: KVM: arm64: vgic-v5: Correctly set dist->ready once initialised kvm_vgic_map_resources() targetting a v5 model results in vgic->dist_ready never being set. This doesn't result in anything really bad, only some more heavy locking as we go and re-init something for no good reason. Rejig the code to correctly set the ready flag in all non-failing cases. Reviewed-by: Sascha Bischoff Fixes: f4d37c7c35769 ("KVM: arm64: gic-v5: Create and initialise vgic_v5") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-11-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-init.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 34460179fb8a..2859dad46a93 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -657,16 +657,20 @@ int kvm_vgic_map_resources(struct kvm *kvm) needs_dist = false; } - if (ret || !needs_dist) + if (ret) goto out; - dist_base = dist->vgic_dist_base; - mutex_unlock(&kvm->arch.config_lock); + if (needs_dist) { + dist_base = dist->vgic_dist_base; + mutex_unlock(&kvm->arch.config_lock); - ret = vgic_register_dist_iodev(kvm, dist_base, type); - if (ret) { - kvm_err("Unable to register VGIC dist MMIO regions\n"); - goto out_slots; + ret = vgic_register_dist_iodev(kvm, dist_base, type); + if (ret) { + kvm_err("Unable to register VGIC dist MMIO regions\n"); + goto out_slots; + } + } else { + mutex_unlock(&kvm->arch.config_lock); } smp_store_release(&dist->ready, true); -- cgit v1.2.3 From 8fe30434a81d36715ab83fdb4a5e6c967d2e3ecf Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:06 +0100 Subject: KVM: arm64: Kill arch_timer_context::direct field The newly introduced arch_timer_context::direct field is a bit pointless, as it is always set on timers that are... err... direct, while we already have a way to get to that by doing a get_map() operation. Additionally, this field is: - only set when get_map() is called - never cleared and the single point where it is actually checked doesn't call get_map() at all. At this stage, it is probably better to just kill it, and rely on get_map() to give us the correct information. Reviewed-by: Sascha Bischoff Fixes: 9491c63b6cd7b ("KVM: arm64: gic-v5: Enlighten arch timer for GICv5") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-12-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 67b989671b41..37279f874869 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -183,10 +183,6 @@ void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map) map->emul_ptimer = vcpu_ptimer(vcpu); } - map->direct_vtimer->direct = true; - if (map->direct_ptimer) - map->direct_ptimer->direct = true; - trace_kvm_get_timer_map(vcpu->vcpu_id, map); } @@ -462,8 +458,15 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, return; /* Skip injecting on GICv5 for directly injected (DVI'd) timers */ - if (vgic_is_v5(vcpu->kvm) && timer_ctx->direct) - return; + if (vgic_is_v5(vcpu->kvm)) { + struct timer_map map; + + get_timer_map(vcpu, &map); + + if (map.direct_ptimer == timer_ctx || + map.direct_vtimer == timer_ctx) + return; + } kvm_vgic_inject_irq(vcpu->kvm, vcpu, timer_irq(timer_ctx), -- cgit v1.2.3 From fbcbf259d97d340376a176de20bdc04687356949 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:07 +0100 Subject: KVM: arm64: Remove evaluation of timer state in kvm_cpu_has_pending_timer() The vgic-v5 code added some evaluations of the timers in a helper funtion (kvm_cpu_has_pending_timer()) that is called to determine whether the vcpu can wake-up. But looking at the timer there is wrong: - we want to see timers that are signalling an interrupt to the vcpu, and not just that have a pending interrupt - we already have kvm_arch_vcpu_runnable() that evaluates the state of interrupts - kvm_cpu_has_pending_timer() really is about WFIT, as the timeout does not generate an interrupt, and is therefore distinct from the point above As a consequence, revert these changes and teach vgic_v5_has_pending_ppi() about checking for pending HW interrupts instead. Fixes: 9491c63b6cd7b ("KVM: arm64: gic-v5: Enlighten arch timer for GICv5") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-13-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 6 +----- arch/arm64/kvm/vgic/vgic-v5.c | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 37279f874869..6608c47d1f62 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -402,11 +402,7 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) { - struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); - - return kvm_timer_should_fire(vtimer) || kvm_timer_should_fire(ptimer) || - (vcpu_has_wfit_active(vcpu) && wfit_delay_ns(vcpu) == 0); + return vcpu_has_wfit_active(vcpu) && wfit_delay_ns(vcpu) == 0; } /* diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 31040cfb61fc..8680a8354db9 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -366,8 +366,8 @@ bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu) irq = vgic_get_vcpu_irq(vcpu, intid); scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) - has_pending = (irq->enabled && irq_is_pending(irq) && - irq->priority < priority_mask); + if (irq->enabled && irq->priority < priority_mask) + has_pending = irq->hw ? vgic_get_phys_line_level(irq) : irq_is_pending(irq); vgic_put_irq(vcpu->kvm, irq); -- cgit v1.2.3 From 06c85b58e0b13e67f4e56cbba346201bfe95ad00 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:08 +0100 Subject: KVM: arm64: Move GICv5 timer PPI validation into timer_irqs_are_valid() Userspace can set the timer PPI numbers way before a GIC has been created, leading to odd behaviours on GICv5 as we'd accept non architectural PPI numbers. Move the v5 check into timer_irqs_are_valid(), which aligns the behaviour with the pre-v5 GICs, and is also guaranteed to run only once a GIC has been configured. Reviewed-by: Sascha Bischoff Fixes: 9491c63b6cd7b ("KVM: arm64: gic-v5: Enlighten arch timer for GICv5") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-14-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/arch_timer.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 6608c47d1f62..cbea4d9ee955 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -1543,6 +1543,10 @@ static bool timer_irqs_are_valid(struct kvm_vcpu *vcpu) if (kvm_vgic_set_owner(vcpu, irq, ctx)) break; + /* With GICv5, the default PPI is what you get -- nothing else */ + if (vgic_is_v5(vcpu->kvm) && irq != get_vgic_ppi(vcpu->kvm, default_ppi[i])) + break; + /* * We know by construction that we only have PPIs, so all values * are less than 32 for non-GICv5 VGICs. On GICv5, they are @@ -1678,13 +1682,6 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) return -ENXIO; } - /* - * The PPIs for the Arch Timers are architecturally defined for - * GICv5. Reject anything that changes them from the specified value. - */ - if (vgic_is_v5(vcpu->kvm) && vcpu->kvm->arch.timer_data.ppi[idx] != irq) - return -EINVAL; - /* * We cannot validate the IRQ unicity before we run, so take it at * face value. The verdict will be given on first vcpu run, for each -- cgit v1.2.3 From be46a408f376df31762e8a9914dc6d082755e686 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:09 +0100 Subject: KVM: arm64: Correctly plumb ID_AA64PFR2_EL1 into pkvm idreg handling While we now compute ID_AA64PFR2_EL1 to a glorious 0, we never use that data and instead return the 0 that corresponds to an allocated idreg. Not a big deal, but we might as well be consistent. Reviewed-by: Sascha Bischoff Fixes: 5aefaf11f9af5 ("KVM: arm64: gic: Hide GICv5 for protected guests") Link: https://sashiko.dev/#/patchset/20260319154937.3619520-1-sascha.bischoff%40arm.com Link: https://patch.msgid.link/20260401103611.357092-15-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp/nvhe/sys_regs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c index b40fd01ebf32..be6f420388a1 100644 --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c @@ -439,7 +439,7 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = { /* CRm=4 */ AARCH64(SYS_ID_AA64PFR0_EL1), AARCH64(SYS_ID_AA64PFR1_EL1), - ID_UNALLOCATED(4,2), + AARCH64(SYS_ID_AA64PFR2_EL1), ID_UNALLOCATED(4,3), AARCH64(SYS_ID_AA64ZFR0_EL1), ID_UNALLOCATED(4,5), -- cgit v1.2.3 From f4626281c6bb563ef5ad9d3a59a1449b45a3dc30 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 11:36:10 +0100 Subject: KVM: arm64: Don't advertises GICv3 in ID_PFR1_EL1 if AArch32 isn't supported Although the AArch32 ID regs are architecturally UNKNOWN when AArch32 isn't supported at any EL, KVM makes a point in making them RAZ. Therefore, advertising GICv3 in ID_PFR1_EL1 must be gated on AArch32 being supported at least at EL0. Reviewed-by: Sascha Bischoff Fixes: a258a383b9177 ("KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE") Reported-by: Mark Brown Tested-by: Mark Brown Link: https://patch.msgid.link/20260401103611.357092-16-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 2859dad46a93..933983bb2005 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -700,7 +700,8 @@ void kvm_vgic_finalize_idregs(struct kvm *kvm) break; case KVM_DEV_TYPE_ARM_VGIC_V3: aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); - pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3); + if (kvm_supports_32bit_el0()) + pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3); break; case KVM_DEV_TYPE_ARM_VGIC_V5: aa64pfr2 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR2_EL1, GCIE, IMP); -- cgit v1.2.3 From 9c1ac77ddfc90b6292ef63a4fa5ab6f9e4b29981 Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Wed, 1 Apr 2026 16:21:57 +0000 Subject: KVM: arm64: vgic-v5: Fold PPI state for all exposed PPIs GICv5 supports up to 128 PPIs, which would introduce a large amount of overhead if all of them were actively tracked. Rather than keeping track of all 128 potential PPIs, we instead only consider the set of architected PPIs (the first 64). Moreover, we further reduce that set by only exposing a subset of the PPIs to a guest. In practice, this means that only 4 PPIs are typically exposed to a guest - the SW_PPI, PMUIRQ, and the timers. When folding the PPI state, changed bits in the active or pending were used to choose which state to sync back. However, this breaks badly for Edge interrupts when exiting the guest before it has consumed the edge. There is no change in pending state detected, and the edge is lost forever. Given the reduced set of PPIs exposed to the guest, and the issues around tracking the edges, drop the tracking of changed state, and instead iterate over the limited subset of PPIs exposed to the guest directly. This change drops the second copy of the PPI pending state used for detecting edges in the pending state, and reworks vgic_v5_fold_ppi_state() to iterate over the VM's PPI mask instead. Signed-off-by: Sascha Bischoff Link: https://patch.msgid.link/20260401162152.932243-1-sascha.bischoff@arm.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 9 +-------- arch/arm64/kvm/hyp/vgic-v5-sr.c | 6 +++--- arch/arm64/kvm/vgic/vgic-v5.c | 28 +++++----------------------- 3 files changed, 9 insertions(+), 34 deletions(-) (limited to 'arch') diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index a7dc0aac3b93..729bd32207fa 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -803,14 +803,7 @@ struct kvm_host_data { /* PPI state tracking for GICv5-based guests */ struct { - /* - * For tracking the PPI pending state, we need both the entry - * state and exit state to correctly detect edges as it is - * possible that an interrupt has been injected in software in - * the interim. - */ - DECLARE_BITMAP(pendr_entry, VGIC_V5_NR_PRIVATE_IRQS); - DECLARE_BITMAP(pendr_exit, VGIC_V5_NR_PRIVATE_IRQS); + DECLARE_BITMAP(pendr, VGIC_V5_NR_PRIVATE_IRQS); /* The saved state of the regs when leaving the guest */ DECLARE_BITMAP(activer_exit, VGIC_V5_NR_PRIVATE_IRQS); diff --git a/arch/arm64/kvm/hyp/vgic-v5-sr.c b/arch/arm64/kvm/hyp/vgic-v5-sr.c index 2c4304ffa9f3..47e6bcd43702 100644 --- a/arch/arm64/kvm/hyp/vgic-v5-sr.c +++ b/arch/arm64/kvm/hyp/vgic-v5-sr.c @@ -37,7 +37,7 @@ void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if) bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, read_sysreg_s(SYS_ICH_PPI_ACTIVER0_EL2), 0, 64); - bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr_exit, + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr, read_sysreg_s(SYS_ICH_PPI_PENDR0_EL2), 0, 64); cpu_if->vgic_ppi_priorityr[0] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR0_EL2); @@ -52,7 +52,7 @@ void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if) if (VGIC_V5_NR_PRIVATE_IRQS == 128) { bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, read_sysreg_s(SYS_ICH_PPI_ACTIVER1_EL2), 64, 64); - bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr_exit, + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr, read_sysreg_s(SYS_ICH_PPI_PENDR1_EL2), 64, 64); cpu_if->vgic_ppi_priorityr[8] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR8_EL2); @@ -87,7 +87,7 @@ void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if) SYS_ICH_PPI_ENABLER0_EL2); /* Update the pending state of the NON-DVI'd PPIs, only */ - bitmap_andnot(pendr, host_data_ptr(vgic_v5_ppi_state)->pendr_entry, + bitmap_andnot(pendr, host_data_ptr(vgic_v5_ppi_state)->pendr, cpu_if->vgic_ppi_dvir, VGIC_V5_NR_PRIVATE_IRQS); write_sysreg_s(bitmap_read(pendr, 0, 64), SYS_ICH_PPI_PENDR0_EL2); diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 8680a8354db9..fdd39ea7f83e 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -385,24 +385,14 @@ bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu) void vgic_v5_fold_ppi_state(struct kvm_vcpu *vcpu) { struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; - DECLARE_BITMAP(changed_active, VGIC_V5_NR_PRIVATE_IRQS); - DECLARE_BITMAP(changed_pending, VGIC_V5_NR_PRIVATE_IRQS); - DECLARE_BITMAP(changed_bits, VGIC_V5_NR_PRIVATE_IRQS); - unsigned long *activer, *pendr_entry, *pendr; + unsigned long *activer, *pendr; int i; activer = host_data_ptr(vgic_v5_ppi_state)->activer_exit; - pendr_entry = host_data_ptr(vgic_v5_ppi_state)->pendr_entry; - pendr = host_data_ptr(vgic_v5_ppi_state)->pendr_exit; + pendr = host_data_ptr(vgic_v5_ppi_state)->pendr; - bitmap_xor(changed_active, cpu_if->vgic_ppi_activer, activer, - VGIC_V5_NR_PRIVATE_IRQS); - bitmap_xor(changed_pending, pendr_entry, pendr, - VGIC_V5_NR_PRIVATE_IRQS); - bitmap_or(changed_bits, changed_active, changed_pending, - VGIC_V5_NR_PRIVATE_IRQS); - - for_each_set_bit(i, changed_bits, VGIC_V5_NR_PRIVATE_IRQS) { + for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, + VGIC_V5_NR_PRIVATE_IRQS) { u32 intid = vgic_v5_make_ppi(i); struct vgic_irq *irq; @@ -462,15 +452,7 @@ void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu) * incoming changes are merged with the outgoing changes on the return * path. */ - bitmap_copy(host_data_ptr(vgic_v5_ppi_state)->pendr_entry, pendr, - VGIC_V5_NR_PRIVATE_IRQS); - - /* - * Make sure that we can correctly detect "edges" in the PPI - * state. There's a path where we never actually enter the guest, and - * failure to do this risks losing pending state - */ - bitmap_copy(host_data_ptr(vgic_v5_ppi_state)->pendr_exit, pendr, + bitmap_copy(host_data_ptr(vgic_v5_ppi_state)->pendr, pendr, VGIC_V5_NR_PRIVATE_IRQS); } -- cgit v1.2.3 From 7e629348df81b339dbc233313f0f36ff5a25fc3d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Apr 2026 18:00:17 +0100 Subject: KVM: arm64: Advertise ID_AA64PFR2_EL1.GCIE As we are missing ID_AA64PFR2_EL1.GCIE from the kernel feature set, userspace cannot write ID_AA64PFR2_EL1 with GCIE set, even if we are on a GICv5 host. Add the required field description. Acked-by: Catalin Marinas Link: https://patch.msgid.link/20260401170017.369529-1-maz@kernel.org Signed-off-by: Marc Zyngier --- arch/arm64/kernel/cpufeature.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 32c2dbcc0c64..1bfaa96881da 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -325,6 +325,7 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = { static const struct arm64_ftr_bits ftr_id_aa64pfr2[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_FPMR_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_GCIE_SHIFT, 4, ID_AA64PFR2_EL1_GCIE_NI), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_MTEFAR_SHIFT, 4, ID_AA64PFR2_EL1_MTEFAR_NI), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_MTESTOREONLY_SHIFT, 4, ID_AA64PFR2_EL1_MTESTOREONLY_NI), ARM64_FTR_END, -- cgit v1.2.3