linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure
@ 2025-11-09 17:15 Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 01/45] irqchip/gic: Add missing GICH_HCR control bits Marc Zyngier
                   ` (45 more replies)
  0 siblings, 46 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

This is the 2nd version of the series originally posted at [1]. The
series has significantly evolved with a bunch of bug fixes, some
additional optimisations, and a number of test cases.

This has now been extensively tested on much of what I have access to,
specially on some of the most broken stuff (Apple, Qualcomm, Cavium,
ARMv8.0 CPUs without TDIR), but also on some less shitty systems
(which are the minority, unsurprisingly).

Given that this is fixing some really ugly vgic bugs, I'm aiming this
at 6.19, though these bugs being 10 year old, any form of urgency is
very questionable.

Patches still against -rc4.

* From v1 [1]:

  - Fixed the ICH_HCR_EL2.TDIR detection code to include the Apple
    stuff, and to deal with GICv5's legacy mode

  - Fixed compilation issue for old toolchains that don't understand
    the GICv3 sysreg names

  - Allow GICv3 in-LR deactivation even when DIR trapping is enabled

  - Dropped the split overflow list, once I convinced myself it wasn't
    bringing much to the table

  - Turned kvm_vgic_vcpu_enable() into a vgic reset helper

  - Remove IPI-ing on GICv3 systems without TDIR

  - Fixed the out-of-LR deactivation when dealing with asymmetric SPI
    deactivation

  - Fixed broken MMIO offset computation

  - Added group enable to the GIC selftest library

  - Added fixes and improvements to the vgic_irq selftest:

    - Fixed definition of spurious interrupt

    - Fixed config/enable ordering

    - Prevent timer interrupts from being injected from userspace

    - Removed limit of 4 interrupts being injected at any given time

    - Added an asymmetric SPI deactivation test case

    - Added a Group-0 enable test case

    - Added a timer interrupt + SPI interrupt test case

  - Fixed a couple of spelling mistakes (and added many more, I'm sure)

  - Reordered the series slightly

[1] https://lore.kernel.org/r/20251103165517.2960148-1-maz@kernel.org

Marc Zyngier (45):
  irqchip/gic: Add missing GICH_HCR control bits
  irqchip/gic: Expose CPU interface VA to KVM
  irqchip/apple-aic: Spit out ICH_MISR_EL2 value on spurious vGIC MI
  KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1
    trapping
  KVM: arm64: Repack struct vgic_irq fields
  KVM: arm64: Add tracking of vgic_irq being present in a LR
  KVM: arm64: Add LR overflow handling documentation
  KVM: arm64: GICv3: Drop LPI active state when folding LRs
  KVM: arm64: GICv3: Preserve EOIcount on exit
  KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs
  KVM: arm64: GICv3: Extract LR folding primitive
  KVM: arm64: GICv3: Extract LR computing primitive
  KVM: arm64: GICv2: Preserve EOIcount on exit
  KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded
  KVM: arm64: GICv2: Extract LR folding primitive
  KVM: arm64: GICv2: Extract LR computing primitive
  KVM: arm64: Compute vgic state irrespective of the number of
    interrupts
  KVM: arm64: Eagerly save VMCR on exit
  KVM: arm64: Revamp vgic maintenance interrupt configuration
  KVM: arm64: Turn kvm_vgic_vcpu_enable() into kvm_vgic_vcpu_reset()
  KVM: arm64: Make vgic_target_oracle() globally available
  KVM: arm64: Invert ap_list sorting to push active interrupts out
  KVM: arm64: Move undeliverable interrupts to the end of ap_list
  KVM: arm64: Use MI to detect groups being enabled/disabled
  KVM: arm64: GICv3: Handle LR overflow when EOImode==0
  KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps
  KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive
  KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR
    capacity
  KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation
  KVM: arm64: GICv3: Handle in-LR deactivation when possible
  KVM: arm64: GICv3: Avoid broadcast kick on CPUs lacking TDIR
  KVM: arm64: GICv2: Handle LR overflow when EOImode==0
  KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps
  KVM: arm64: GICv2: Always trap GICV_DIR register
  KVM: arm64: selftests: gic_v3: Add irq group setting helper
  KVM: arm64: selftests: gic_v3: Disable Group-0 interrupts by default
  KVM: arm64: selftests: vgic_irq: Fix GUEST_ASSERT_IAR_EMPTY() helper
  KVM: arm64: selftests: vgic_irq: Change configuration before enabling
    interrupt
  KVM: arm64: selftests: vgic_irq: Exclude timer-controlled interrupts
  KVM: arm64: selftests: vgic_irq: Remove LR-bound limitation
  KVM: arm64: selftests: vgic_irq: Perform EOImode==1 deactivation in
    ack order
  KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test
  KVM: arm64: selftests: vgic_irq: Add Group-0 enable test
  KVM: arm64: selftests: vgic_irq: Add timer deactivation test

 arch/arm64/include/asm/kvm_asm.h              |   2 +-
 arch/arm64/include/asm/kvm_host.h             |   1 +
 arch/arm64/include/asm/kvm_hyp.h              |   2 +-
 arch/arm64/include/asm/virt.h                 |   7 +-
 arch/arm64/kernel/cpufeature.c                |  52 +++
 arch/arm64/kernel/hyp-stub.S                  |   5 +
 arch/arm64/kernel/image-vars.h                |   1 +
 arch/arm64/kvm/arm.c                          |   7 +-
 arch/arm64/kvm/hyp/nvhe/hyp-main.c            |   7 +-
 arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c      |   4 +
 arch/arm64/kvm/hyp/vgic-v3-sr.c               |  87 ++--
 arch/arm64/kvm/sys_regs.c                     |  19 +-
 arch/arm64/kvm/vgic/vgic-init.c               |   9 +-
 arch/arm64/kvm/vgic/vgic-mmio-v2.c            |  24 +
 arch/arm64/kvm/vgic/vgic-mmio.h               |   1 +
 arch/arm64/kvm/vgic/vgic-v2.c                 | 291 +++++++++---
 arch/arm64/kvm/vgic/vgic-v3-nested.c          |  11 +-
 arch/arm64/kvm/vgic/vgic-v3.c                 | 421 ++++++++++++++----
 arch/arm64/kvm/vgic/vgic-v4.c                 |   5 +-
 arch/arm64/kvm/vgic/vgic.c                    | 294 +++++++-----
 arch/arm64/kvm/vgic/vgic.h                    |  42 +-
 arch/arm64/tools/cpucaps                      |   1 +
 drivers/irqchip/irq-apple-aic.c               |   7 +-
 drivers/irqchip/irq-gic.c                     |   3 +
 include/kvm/arm_vgic.h                        |  29 +-
 include/linux/irqchip/arm-gic.h               |   6 +
 include/linux/irqchip/arm-vgic-info.h         |   2 +
 tools/testing/selftests/kvm/arm64/vgic_irq.c  | 285 +++++++++++-
 .../testing/selftests/kvm/include/arm64/gic.h |   1 +
 tools/testing/selftests/kvm/lib/arm64/gic.c   |   6 +
 .../selftests/kvm/lib/arm64/gic_private.h     |   1 +
 .../testing/selftests/kvm/lib/arm64/gic_v3.c  |  17 +
 32 files changed, 1276 insertions(+), 374 deletions(-)

-- 
2.47.3



^ permalink raw reply	[flat|nested] 83+ messages in thread

* [PATCH v2 01/45] irqchip/gic: Add missing GICH_HCR control bits
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 02/45] irqchip/gic: Expose CPU interface VA to KVM Marc Zyngier
                   ` (44 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

The GICH_HCR description is missing a bunch of additional control
bits for the maintenance interrupt. Add them.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 include/linux/irqchip/arm-gic.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 2223f95079ce8..d45fa19f9e470 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -86,7 +86,13 @@
 
 #define GICH_HCR_EN			(1 << 0)
 #define GICH_HCR_UIE			(1 << 1)
+#define GICH_HCR_LRENPIE		(1 << 2)
 #define GICH_HCR_NPIE			(1 << 3)
+#define GICH_HCR_VGrp0EIE		(1 << 4)
+#define GICH_HCR_VGrp0DIE		(1 << 5)
+#define GICH_HCR_VGrp1EIE		(1 << 6)
+#define GICH_HCR_VGrp1DIE		(1 << 7)
+#define GICH_HCR_EOICOUNT		GENMASK(31, 27)
 
 #define GICH_LR_VIRTUALID		(0x3ff << 0)
 #define GICH_LR_PHYSID_CPUID_SHIFT	(10)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 02/45] irqchip/gic: Expose CPU interface VA to KVM
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 01/45] irqchip/gic: Add missing GICH_HCR control bits Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 03/45] irqchip/apple-aic: Spit out ICH_MISR_EL2 value on spurious vGIC MI Marc Zyngier
                   ` (43 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Future changes will require KVM to be able to perform deactivations
by writing to the physical CPU interface. Add the corresponding
VA to the kvm_info structure, and let KVM stash it.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c         | 1 +
 drivers/irqchip/irq-gic.c             | 3 +++
 include/kvm/arm_vgic.h                | 3 +++
 include/linux/irqchip/arm-vgic-info.h | 2 ++
 4 files changed, 9 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 381673f03c395..441efef80d609 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -385,6 +385,7 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
 
 	kvm_vgic_global_state.can_emulate_gicv2 = true;
 	kvm_vgic_global_state.vcpu_base = info->vcpu.start;
+	kvm_vgic_global_state.gicc_base = info->gicc_base;
 	kvm_vgic_global_state.type = VGIC_V2;
 	kvm_vgic_global_state.max_gic_vcpus = VGIC_V2_MAX_CPUS;
 
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 1269ab8eb726a..ec70c84e9f91d 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -1459,6 +1459,8 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
 	if (ret)
 		return;
 
+	gic_v2_kvm_info.gicc_base = gic_data[0].cpu_base.common_base;
+
 	if (static_branch_likely(&supports_deactivate_key))
 		vgic_set_kvm_info(&gic_v2_kvm_info);
 }
@@ -1620,6 +1622,7 @@ static void __init gic_acpi_setup_kvm_info(void)
 		return;
 
 	gic_v2_kvm_info.maint_irq = irq;
+	gic_v2_kvm_info.gicc_base = gic_data[0].cpu_base.common_base;
 
 	vgic_set_kvm_info(&gic_v2_kvm_info);
 }
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 7a0b972eb1b12..577723f5599bd 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -59,6 +59,9 @@ struct vgic_global {
 	/* virtual control interface mapping, HYP VA */
 	void __iomem		*vctrl_hyp;
 
+	/* Physical CPU interface, kernel VA */
+	void __iomem		*gicc_base;
+
 	/* Number of implemented list registers */
 	int			nr_lr;
 
diff --git a/include/linux/irqchip/arm-vgic-info.h b/include/linux/irqchip/arm-vgic-info.h
index a470a73a805aa..67d9d960273b9 100644
--- a/include/linux/irqchip/arm-vgic-info.h
+++ b/include/linux/irqchip/arm-vgic-info.h
@@ -24,6 +24,8 @@ struct gic_kvm_info {
 	enum gic_type	type;
 	/* Virtual CPU interface */
 	struct resource vcpu;
+	/* GICv2 GICC VA */
+	void __iomem	*gicc_base;
 	/* Interrupt number */
 	unsigned int	maint_irq;
 	/* No interrupt mask, no need to use the above field */
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 03/45] irqchip/apple-aic: Spit out ICH_MISR_EL2 value on spurious vGIC MI
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 01/45] irqchip/gic: Add missing GICH_HCR control bits Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 02/45] irqchip/gic: Expose CPU interface VA to KVM Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
                   ` (42 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

It is all good and well to scream about spurious vGIC maintenance
interrupts. It would be even better to output the reason why, which
is already checked, but not printed out.

The unsuspecting kernel tinkerer thanks you.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 drivers/irqchip/irq-apple-aic.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c
index 032d66dceb8ec..4607f4943b19a 100644
--- a/drivers/irqchip/irq-apple-aic.c
+++ b/drivers/irqchip/irq-apple-aic.c
@@ -411,12 +411,15 @@ static void __exception_irq_entry aic_handle_irq(struct pt_regs *regs)
 	if (is_kernel_in_hyp_mode() &&
 	    (read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EL2_En) &&
 	    read_sysreg_s(SYS_ICH_MISR_EL2) != 0) {
+		u64 val;
+
 		generic_handle_domain_irq(aic_irqc->hw_domain,
 					  AIC_FIQ_HWIRQ(AIC_VGIC_MI));
 
 		if (unlikely((read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EL2_En) &&
-			     read_sysreg_s(SYS_ICH_MISR_EL2))) {
-			pr_err_ratelimited("vGIC IRQ fired and not handled by KVM, disabling.\n");
+			     (val = read_sysreg_s(SYS_ICH_MISR_EL2)))) {
+			pr_err_ratelimited("vGIC IRQ fired and not handled by KVM (MISR=%llx), disabling.\n",
+					   val);
 			sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EL2_En, 0);
 		}
 	}
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (2 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 03/45] irqchip/apple-aic: Spit out ICH_MISR_EL2 value on spurious vGIC MI Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-10 10:40   ` Suzuki K Poulose
                     ` (3 more replies)
  2025-11-09 17:15 ` [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping Marc Zyngier
                   ` (41 subsequent siblings)
  45 siblings, 4 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

The trap bits are currently only set to manage CPU errata. However,
we are about to make use of them for purposes beyond beating broken
CPUs into submission.

For this purpose, turn these errata-driven bits into a patched-in
constant that is merged with the KVM-driven value at the point of
programming the ICH_HCR_EL2 register, rather than being directly
stored with with the shadow value..

This allows the KVM code to distinguish between a trap being handled
for the purpose of an erratum workaround, or for KVM's own need.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kernel/image-vars.h       |  1 +
 arch/arm64/kvm/hyp/vgic-v3-sr.c      | 21 +++++---
 arch/arm64/kvm/vgic/vgic-v3-nested.c |  9 ----
 arch/arm64/kvm/vgic/vgic-v3.c        | 81 +++++++++++++++++-----------
 arch/arm64/kvm/vgic/vgic.h           | 16 ++++++
 5 files changed, 82 insertions(+), 46 deletions(-)

diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index 5369763606e71..85bc629270bd9 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -91,6 +91,7 @@ KVM_NVHE_ALIAS(spectre_bhb_patch_loop_mitigation_enable);
 KVM_NVHE_ALIAS(spectre_bhb_patch_wa3);
 KVM_NVHE_ALIAS(spectre_bhb_patch_clearbhb);
 KVM_NVHE_ALIAS(alt_cb_patch_nops);
+KVM_NVHE_ALIAS(kvm_compute_ich_hcr_trap_bits);
 
 /* Global kernel state accessed by nVHE hyp code. */
 KVM_NVHE_ALIAS(kvm_vgic_global_state);
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index acd909b7f2257..00ad89d71bb3f 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -14,6 +14,8 @@
 #include <asm/kvm_hyp.h>
 #include <asm/kvm_mmu.h>
 
+#include "../../vgic/vgic.h"
+
 #define vtr_to_max_lr_idx(v)		((v) & 0xf)
 #define vtr_to_nr_pre_bits(v)		((((u32)(v) >> 26) & 7) + 1)
 #define vtr_to_nr_apr_regs(v)		(1 << (vtr_to_nr_pre_bits(v) - 5))
@@ -196,6 +198,11 @@ static u32 __vgic_v3_read_ap1rn(int n)
 	return val;
 }
 
+static u64 compute_ich_hcr(struct vgic_v3_cpu_if *cpu_if)
+{
+	return cpu_if->vgic_hcr | vgic_ich_hcr_trap_bits();
+}
+
 void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
 {
 	u64 used_lrs = cpu_if->used_lrs;
@@ -218,7 +225,7 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
 
 		elrsr = read_gicreg(ICH_ELRSR_EL2);
 
-		write_gicreg(cpu_if->vgic_hcr & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
+		write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
 
 		for (i = 0; i < used_lrs; i++) {
 			if (elrsr & (1 << i))
@@ -237,7 +244,7 @@ void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
 	int i;
 
 	if (used_lrs || cpu_if->its_vpe.its_vm) {
-		write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
+		write_gicreg(compute_ich_hcr(cpu_if), ICH_HCR_EL2);
 
 		for (i = 0; i < used_lrs; i++)
 			__gic_v3_set_lr(cpu_if->vgic_lr[i], i);
@@ -307,14 +314,14 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
 	}
 
 	/*
-	 * If we need to trap system registers, we must write
-	 * ICH_HCR_EL2 anyway, even if no interrupts are being
-	 * injected. Note that this also applies if we don't expect
-	 * any system register access (no vgic at all).
+	 * If we need to trap system registers, we must write ICH_HCR_EL2
+	 * anyway, even if no interrupts are being injected. Note that this
+	 * also applies if we don't expect any system register access (no
+	 * vgic at all). In any case, no need to provide MI configuration.
 	 */
 	if (static_branch_unlikely(&vgic_v3_cpuif_trap) ||
 	    cpu_if->its_vpe.its_vm || !cpu_if->vgic_sre)
-		write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
+		write_gicreg(vgic_ich_hcr_trap_bits() | ICH_HCR_EL2_En, ICH_HCR_EL2);
 }
 
 void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c
index 7f1259b49c505..387557e20a272 100644
--- a/arch/arm64/kvm/vgic/vgic-v3-nested.c
+++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c
@@ -301,15 +301,6 @@ static void vgic_v3_create_shadow_state(struct kvm_vcpu *vcpu,
 	u64 val = 0;
 	int i;
 
-	/*
-	 * If we're on a system with a broken vgic that requires
-	 * trapping, propagate the trapping requirements.
-	 *
-	 * Ah, the smell of rotten fruits...
-	 */
-	if (static_branch_unlikely(&vgic_v3_cpuif_trap))
-		val = host_if->vgic_hcr & (ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 |
-					   ICH_HCR_EL2_TC | ICH_HCR_EL2_TDIR);
 	s_cpu_if->vgic_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2) | val;
 	s_cpu_if->vgic_vmcr = __vcpu_sys_reg(vcpu, ICH_VMCR_EL2);
 	s_cpu_if->vgic_sre = host_if->vgic_sre;
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 6fbb4b0998552..236d0beef561d 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -301,20 +301,9 @@ void vcpu_set_ich_hcr(struct kvm_vcpu *vcpu)
 		return;
 
 	/* Hide GICv3 sysreg if necessary */
-	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) {
+	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2)
 		vgic_v3->vgic_hcr |= (ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 |
 				      ICH_HCR_EL2_TC);
-		return;
-	}
-
-	if (group0_trap)
-		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TALL0;
-	if (group1_trap)
-		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TALL1;
-	if (common_trap)
-		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TC;
-	if (dir_trap)
-		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TDIR;
 }
 
 int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq)
@@ -635,10 +624,52 @@ static const struct midr_range broken_seis[] = {
 
 static bool vgic_v3_broken_seis(void)
 {
-	return ((kvm_vgic_global_state.ich_vtr_el2 & ICH_VTR_EL2_SEIS) &&
+	return (is_kernel_in_hyp_mode() &&
+		(read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS) &&
 		is_midr_in_range_list(broken_seis));
 }
 
+void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
+					   __le32 *origptr, __le32 *updptr,
+					   int nr_inst)
+{
+	u32 insn, oinsn, rd;
+	u64 hcr = 0;
+
+	if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
+		group0_trap = true;
+		group1_trap = true;
+	}
+
+	if (vgic_v3_broken_seis()) {
+		/* We know that these machines have ICH_HCR_EL2.TDIR */
+		group0_trap = true;
+		group1_trap = true;
+		dir_trap = true;
+	}
+
+	if (group0_trap)
+		hcr |= ICH_HCR_EL2_TALL0;
+	if (group1_trap)
+		hcr |= ICH_HCR_EL2_TALL1;
+	if (common_trap)
+		hcr |= ICH_HCR_EL2_TC;
+	if (dir_trap)
+		hcr |= ICH_HCR_EL2_TDIR;
+
+	/* Compute target register */
+	oinsn = le32_to_cpu(*origptr);
+	rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn);
+
+	/* movz rd, #(val & 0xffff) */
+	insn = aarch64_insn_gen_movewide(rd,
+					 (u16)hcr,
+					 0,
+					 AARCH64_INSN_VARIANT_64BIT,
+					 AARCH64_INSN_MOVEWIDE_ZERO);
+	*updptr = cpu_to_le32(insn);
+}
+
 /**
  * vgic_v3_probe - probe for a VGICv3 compatible interrupt controller
  * @info:	pointer to the GIC description
@@ -650,6 +681,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
 {
 	u64 ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
 	bool has_v2;
+	u64 traps;
 	int ret;
 
 	has_v2 = ich_vtr_el2 >> 63;
@@ -708,29 +740,18 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
 	if (has_v2)
 		static_branch_enable(&vgic_v3_has_v2_compat);
 
-	if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
-		group0_trap = true;
-		group1_trap = true;
-	}
-
 	if (vgic_v3_broken_seis()) {
 		kvm_info("GICv3 with broken locally generated SEI\n");
-
 		kvm_vgic_global_state.ich_vtr_el2 &= ~ICH_VTR_EL2_SEIS;
-		group0_trap = true;
-		group1_trap = true;
-		if (ich_vtr_el2 & ICH_VTR_EL2_TDS)
-			dir_trap = true;
-		else
-			common_trap = true;
 	}
 
-	if (group0_trap || group1_trap || common_trap | dir_trap) {
+	traps = vgic_ich_hcr_trap_bits();
+	if (traps) {
 		kvm_info("GICv3 sysreg trapping enabled ([%s%s%s%s], reduced performance)\n",
-			 group0_trap ? "G0" : "",
-			 group1_trap ? "G1" : "",
-			 common_trap ? "C"  : "",
-			 dir_trap    ? "D"  : "");
+			 (traps & ICH_HCR_EL2_TALL0) ? "G0" : "",
+			 (traps & ICH_HCR_EL2_TALL1) ? "G1" : "",
+			 (traps & ICH_HCR_EL2_TC)    ? "C"  : "",
+			 (traps & ICH_HCR_EL2_TDIR)  ? "D"  : "");
 		static_branch_enable(&vgic_v3_cpuif_trap);
 	}
 
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index ac5f9c5d2b980..0ecadfa00397d 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -164,6 +164,22 @@ static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
 	return ret;
 }
 
+void kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
+				   __le32 *origptr, __le32 *updptr, int nr_inst);
+
+static inline u64 vgic_ich_hcr_trap_bits(void)
+{
+	u64 hcr;
+
+	/* All the traps are in the bottom 16bits */
+	asm volatile(ALTERNATIVE_CB("movz %0, #0\n",
+				    ARM64_ALWAYS_SYSTEM,
+				    kvm_compute_ich_hcr_trap_bits)
+		     : "=r" (hcr));
+
+	return hcr;
+}
+
 /*
  * This struct provides an intermediate representation of the fields contained
  * in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (3 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-13 14:33   ` Mark Brown
  2025-11-09 17:15 ` [PATCH v2 06/45] KVM: arm64: Repack struct vgic_irq fields Marc Zyngier
                   ` (40 subsequent siblings)
  45 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

A long time ago, an unsuspecting architect forgot to add a trap
bit for ICV_DIR_EL1 in ICH_HCR_EL2. Which was unfortunate, but
what's a bit of spec between friends? Thankfully, this was fixed
in a later revision, and ARM "deprecates" the lack of trapping
ability.

Unfortuantely, a few (billion) CPUs went out with that defect,
anything ARMv8.0 from ARM, give or take. And on these CPUs,
you can't trap DIR on its own, full stop.

As the next best thing, we can trap everything in the common group,
which is a tad expensive, but hey ho, that's what you get. You can
otherwise recycle the HW in the neaby bin.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/include/asm/virt.h  |  7 ++++-
 arch/arm64/kernel/cpufeature.c | 52 ++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/hyp-stub.S   |  5 ++++
 arch/arm64/kvm/vgic/vgic-v3.c  |  3 ++
 arch/arm64/tools/cpucaps       |  1 +
 5 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index aa280f356b96a..8eb63d3294974 100644
--- a/arch/arm64/include/asm/virt.h
+++ b/arch/arm64/include/asm/virt.h
@@ -40,8 +40,13 @@
  */
 #define HVC_FINALISE_EL2	3
 
+/*
+ * HVC_GET_ICH_VTR_EL2 - Retrieve the ICH_VTR_EL2 value
+ */
+#define HVC_GET_ICH_VTR_EL2	4
+
 /* Max number of HYP stub hypercalls */
-#define HVC_STUB_HCALL_NR 4
+#define HVC_STUB_HCALL_NR 5
 
 /* Error returned when an invalid stub number is passed into x0 */
 #define HVC_STUB_ERR	0xbadca11
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 5ed401ff79e3e..5de51cb1b8fe2 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2303,6 +2303,49 @@ static bool has_gic_prio_relaxed_sync(const struct arm64_cpu_capabilities *entry
 }
 #endif
 
+static bool can_trap_icv_dir_el1(const struct arm64_cpu_capabilities *entry,
+				 int scope)
+{
+	static const struct midr_range has_vgic_v3[] = {
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_PRO),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_PRO),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_MAX),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_MAX),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_PRO),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_PRO),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_MAX),
+		MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_MAX),
+		{},
+	};
+	struct arm_smccc_res res = {};
+
+	BUILD_BUG_ON(ARM64_HAS_ICH_HCR_EL2_TDIR <= ARM64_HAS_GICV3_CPUIF);
+	BUILD_BUG_ON(ARM64_HAS_ICH_HCR_EL2_TDIR <= ARM64_HAS_GICV5_LEGACY);
+	if (!cpus_have_cap(ARM64_HAS_GICV3_CPUIF) &&
+	    !is_midr_in_range_list(has_vgic_v3))
+		return false;
+
+	if (!is_hyp_mode_available())
+		return false;
+
+	if (cpus_have_cap(ARM64_HAS_GICV5_LEGACY))
+		return true;
+
+	if (is_kernel_in_hyp_mode())
+		res.a1 = read_sysreg_s(SYS_ICH_VTR_EL2);
+	else
+		arm_smccc_1_1_hvc(HVC_GET_ICH_VTR_EL2, &res);
+
+	if (res.a0 == HVC_STUB_ERR)
+		return false;
+
+	return res.a1 & ICH_VTR_EL2_TDS;
+}
+
 #ifdef CONFIG_ARM64_BTI
 static void bti_enable(const struct arm64_cpu_capabilities *__unused)
 {
@@ -2814,6 +2857,15 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.matches = has_gic_prio_relaxed_sync,
 	},
 #endif
+	{
+		/*
+		 * Depends on having GICv3
+		 */
+		.desc = "ICV_DIR_EL1 trapping",
+		.capability = ARM64_HAS_ICH_HCR_EL2_TDIR,
+		.type = ARM64_CPUCAP_SYSTEM_FEATURE,
+		.matches = can_trap_icv_dir_el1,
+	},
 #ifdef CONFIG_ARM64_E0PD
 	{
 		.desc = "E0PD",
diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S
index 36e2d26b54f5c..085bc9972f6bb 100644
--- a/arch/arm64/kernel/hyp-stub.S
+++ b/arch/arm64/kernel/hyp-stub.S
@@ -54,6 +54,11 @@ SYM_CODE_START_LOCAL(elx_sync)
 1:	cmp	x0, #HVC_FINALISE_EL2
 	b.eq	__finalise_el2
 
+	cmp	x0, #HVC_GET_ICH_VTR_EL2
+	b.ne	2f
+	mrs_s	x1, SYS_ICH_VTR_EL2
+	b	9f
+
 2:	cmp	x0, #HVC_SOFT_RESTART
 	b.ne	3f
 	mov	x0, x2
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 236d0beef561d..473252c98367b 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -648,6 +648,9 @@ void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
 		dir_trap = true;
 	}
 
+	if (!cpus_have_cap(ARM64_HAS_ICH_HCR_EL2_TDIR))
+		common_trap = true;
+
 	if (group0_trap)
 		hcr |= ICH_HCR_EL2_TALL0;
 	if (group1_trap)
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 1b32c1232d28d..116d1a7b688cb 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -40,6 +40,7 @@ HAS_GICV5_CPUIF
 HAS_GICV5_LEGACY
 HAS_GIC_PRIO_MASKING
 HAS_GIC_PRIO_RELAXED_SYNC
+HAS_ICH_HCR_EL2_TDIR
 HAS_HCR_NV1
 HAS_HCX
 HAS_LDAPR
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 06/45] KVM: arm64: Repack struct vgic_irq fields
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (4 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 07/45] KVM: arm64: Add tracking of vgic_irq being present in a LR Marc Zyngier
                   ` (39 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

struct vgic_irq has grown over the years, in a rather bad way.
Repack it using bitfields so that the individual flags, and move
things around a bit so that it a bit smaller.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v4.c |  5 ++++-
 include/kvm/arm_vgic.h        | 20 ++++++++++----------
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c
index 548aec9d5a728..09c3e9eb23f89 100644
--- a/arch/arm64/kvm/vgic/vgic-v4.c
+++ b/arch/arm64/kvm/vgic/vgic-v4.c
@@ -163,6 +163,7 @@ static void vgic_v4_disable_vsgis(struct kvm_vcpu *vcpu)
 		struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, i);
 		struct irq_desc *desc;
 		unsigned long flags;
+		bool pending;
 		int ret;
 
 		raw_spin_lock_irqsave(&irq->irq_lock, flags);
@@ -173,9 +174,11 @@ static void vgic_v4_disable_vsgis(struct kvm_vcpu *vcpu)
 		irq->hw = false;
 		ret = irq_get_irqchip_state(irq->host_irq,
 					    IRQCHIP_STATE_PENDING,
-					    &irq->pending_latch);
+					    &pending);
 		WARN_ON(ret);
 
+		irq->pending_latch = pending;
+
 		desc = irq_to_desc(irq->host_irq);
 		irq_domain_deactivate_irq(irq_desc_get_irq_data(desc));
 	unlock:
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 577723f5599bd..e84a1bc5cf172 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -123,6 +123,7 @@ struct irq_ops {
 
 struct vgic_irq {
 	raw_spinlock_t irq_lock;	/* Protects the content of the struct */
+	u32 intid;			/* Guest visible INTID */
 	struct rcu_head rcu;
 	struct list_head ap_list;
 
@@ -137,17 +138,17 @@ struct vgic_irq {
 					 * affinity reg (v3).
 					 */
 
-	u32 intid;			/* Guest visible INTID */
-	bool line_level;		/* Level only */
-	bool pending_latch;		/* The pending latch state used to calculate
-					 * the pending state for both level
-					 * and edge triggered IRQs. */
-	bool active;
-	bool pending_release;		/* Used for LPIs only, unreferenced IRQ
+	bool pending_release:1;		/* Used for LPIs only, unreferenced IRQ
 					 * pending a release */
 
-	bool enabled;
-	bool hw;			/* Tied to HW IRQ */
+	bool pending_latch:1;		/* The pending latch state used to calculate
+					 * the pending state for both level
+					 * and edge triggered IRQs. */
+	enum vgic_irq_config config:1;	/* Level or edge */
+	bool line_level:1;		/* Level only */
+	bool enabled:1;
+	bool active:1;
+	bool hw:1;			/* Tied to HW IRQ */
 	refcount_t refcount;		/* Used for LPIs */
 	u32 hwintid;			/* HW INTID number */
 	unsigned int host_irq;		/* linux irq corresponding to hwintid */
@@ -159,7 +160,6 @@ struct vgic_irq {
 	u8 active_source;		/* GICv2 SGIs only */
 	u8 priority;
 	u8 group;			/* 0 == group 0, 1 == group 1 */
-	enum vgic_irq_config config;	/* Level or edge */
 
 	struct irq_ops *ops;
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 07/45] KVM: arm64: Add tracking of vgic_irq being present in a LR
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (5 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 06/45] KVM: arm64: Repack struct vgic_irq fields Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 08/45] KVM: arm64: Add LR overflow handling documentation Marc Zyngier
                   ` (38 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

We currently cannot identify whether an interrupt is queued into
a LR. It wasn't needed until now, but that's about to change.

Add yet another flag to track that state.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 6 ++++++
 arch/arm64/kvm/vgic/vgic-v3.c | 6 ++++++
 include/kvm/arm_vgic.h        | 1 +
 3 files changed, 13 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 441efef80d609..74efacba38d42 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -101,6 +101,8 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 		/* Handle resampling for mapped interrupts if required */
 		vgic_irq_handle_resampling(irq, deactivated, val & GICH_LR_PENDING_BIT);
 
+		irq->on_lr = false;
+
 		raw_spin_unlock(&irq->irq_lock);
 		vgic_put_irq(vcpu->kvm, irq);
 	}
@@ -124,6 +126,8 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	u32 val = irq->intid;
 	bool allow_pending = true;
 
+	WARN_ON(irq->on_lr);
+
 	if (irq->active) {
 		val |= GICH_LR_ACTIVE_BIT;
 		if (vgic_irq_is_sgi(irq->intid))
@@ -194,6 +198,8 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	/* The GICv2 LR only holds five bits of priority. */
 	val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
 
+	irq->on_lr = true;
+
 	vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = val;
 }
 
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 473252c98367b..58942abd2f4e7 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -97,6 +97,8 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 		/* Handle resampling for mapped interrupts if required */
 		vgic_irq_handle_resampling(irq, deactivated, val & ICH_LR_PENDING_BIT);
 
+		irq->on_lr = false;
+
 		raw_spin_unlock(&irq->irq_lock);
 		vgic_put_irq(vcpu->kvm, irq);
 	}
@@ -111,6 +113,8 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	u64 val = irq->intid;
 	bool allow_pending = true, is_v2_sgi;
 
+	WARN_ON(irq->on_lr);
+
 	is_v2_sgi = (vgic_irq_is_sgi(irq->intid) &&
 		     model == KVM_DEV_TYPE_ARM_VGIC_V2);
 
@@ -185,6 +189,8 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT;
 
 	vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = val;
+
+	irq->on_lr = true;
 }
 
 void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr)
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index e84a1bc5cf172..ec349c5a4a8b6 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -149,6 +149,7 @@ struct vgic_irq {
 	bool enabled:1;
 	bool active:1;
 	bool hw:1;			/* Tied to HW IRQ */
+	bool on_lr:1;			/* Present in a CPU LR */
 	refcount_t refcount;		/* Used for LPIs */
 	u32 hwintid;			/* HW INTID number */
 	unsigned int host_irq;		/* linux irq corresponding to hwintid */
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 08/45] KVM: arm64: Add LR overflow handling documentation
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (6 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 07/45] KVM: arm64: Add tracking of vgic_irq being present in a LR Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 09/45] KVM: arm64: GICv3: Drop LPI active state when folding LRs Marc Zyngier
                   ` (37 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Add a bit of documentation describing how we are dealing with LR
overflow. This is mostly a braindump of how things are expected
to work. For now anyway.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic.c | 81 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 80 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 6dd5a10081e27..8d7f6803e601b 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -825,7 +825,86 @@ static int compute_ap_list_depth(struct kvm_vcpu *vcpu,
 	return count;
 }
 
-/* Requires the VCPU's ap_list_lock to be held. */
+/*
+ * Dealing with LR overflow is close to black magic -- dress accordingly.
+ *
+ * We have to present an almost infinite number of interrupts through a very
+ * limited number of registers. Therefore crucial decisions must be made to
+ * ensure we feed the most relevant interrupts into the LRs, and yet have
+ * some facilities to let the guest interact with those that are not there.
+ *
+ * All considerations below are in the context of interrupts targeting a
+ * single vcpu with non-idle state (either pending, active, or both),
+ * colloquially called the ap_list:
+ *
+ * - Pending interrupts must have priority over active interrupts. This also
+ *   excludes pending+active interrupts. This ensures that a guest can
+ *   perform priority drops on any number of interrupts, and yet be
+ *   presented the next pending one.
+ *
+ * - Deactivation of interrupts outside of the LRs must be tracked by using
+ *   either the EOIcount-driven maintenance interrupt, and sometimes by
+ *   trapping the DIR register.
+ *
+ * - For EOImode=0, a non-zero EOIcount means walking the ap_list past the
+ *   point that made it into the LRs, and deactivate interrupts that would
+ *   have made it onto the LRs if we had the space.
+ *
+ * - The MI-generation bits must be used to try and force an exit when the
+ *   guest has done enough changes to the LRs that we want to reevaluate the
+ *   situation:
+ *
+ *	- if the total number of pending interrupts exceeds the number of
+ *	  LR, NPIE must be set in order to exit once no pending interrupts
+ *	  are present in the LRs, allowing us to populate the next batch.
+ *
+ *	- if there are active interrupts outside of the LRs, then LRENPIE
+ *	  must be set so that we exit on deactivation of one of these, and
+ *	  work out which one is to be deactivated.  Note that this is not
+ *	  enough to deal with EOImode=1, see below.
+ *
+ *	- if the overall number of interrupts exceeds the number of LRs,
+ *	  then UIE must be set to allow refilling of the LRs once the
+ *	  majority of them has been processed.
+ *
+ *	- as usual, MI triggers are only an optimisation, since we cannot
+ *        rely on the MI being delivered in timely manner...
+ *
+ * - EOImode=1 creates some additional problems:
+ *
+ *      - deactivation can happen in any order, and we cannot rely on
+ *	  EOImode=0's coupling of priority-drop and deactivation which
+ *	  imposes strict reverse Ack order. This means that DIR must be set
+ *	  if we have active interrupts outside of the LRs.
+ *
+ *      - deactivation of SPIs can occur on any CPU, while the SPI is only
+ *	  present in the ap_list of the CPU that actually ack-ed it. In that
+ *	  case, EOIcount doesn't provide enough information, and we must
+ *	  resort to trapping DIR even if we don't overflow the LRs. Bonus
+ *	  point for not trapping DIR when no SPIs are pending or active in
+ *	  the whole VM.
+ *
+ *	- LPIs do not suffer the same problem as SPIs on deactivation, as we
+ *	  have to essentially discard the active state, see below.
+ *
+ * - Virtual LPIs have an active state (surprise!), which gets removed on
+ *   priority drop (EOI). However, EOIcount doesn't get bumped when the LPI
+ *   is not present in the LR (surprise again!). Special care must therefore
+ *   be taken to remove the active state from any activated LPI when exiting
+ *   from the guest. This is in a way no different from what happens on the
+ *   physical side. We still rely on the running priority to have been
+ *   removed from the APRs, irrespective of the LPI being present in the LRs
+ *   or not.
+ *
+ * - Virtual SGIs directly injected via GICv4.1 must not affect EOIcount, as
+ *   they are not managed in SW and don't have a true active state. So only
+ *   set vSGIEOICount when no SGIs are in the ap_list.
+ *
+ * - GICv2 SGIs with multiple sources are injected one source at a time, as
+ *   if they were made pending sequentially. This may mean that we don't
+ *   always present the HPPI if other interrupts with lower priority are
+ *   pending in the LRs. Big deal.
+ */
 static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 09/45] KVM: arm64: GICv3: Drop LPI active state when folding LRs
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (7 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 08/45] KVM: arm64: Add LR overflow handling documentation Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 10/45] KVM: arm64: GICv3: Preserve EOIcount on exit Marc Zyngier
                   ` (36 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Despite LPIs not having an active state, *virtual* LPIs do have
one, which gets cleared on EOI. So far, so good.

However, this leads to a small problem: when an active LPI is not
in the LRs, that EOImode==0 and that the guest EOIs it, EOIcount
doesn't get bumped up. Which means that in these condition, the
LPI would stay active forever.

Clearly, we can't have that. So if we spot an active LPI, we drop
that state. It's pretty pointless anyway, and only serves as a way
to trip SW over.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 58942abd2f4e7..3ede79e381513 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -72,7 +72,9 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 
 		raw_spin_lock(&irq->irq_lock);
 
-		/* Always preserve the active bit, note deactivation */
+		/* Always preserve the active bit for !LPIs, note deactivation */
+		if (irq->intid >= VGIC_MIN_LPI)
+			val &= ~ICH_LR_ACTIVE_BIT;
 		deactivated = irq->active && !(val & ICH_LR_ACTIVE_BIT);
 		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 10/45] KVM: arm64: GICv3: Preserve EOIcount on exit
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (8 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 09/45] KVM: arm64: GICv3: Drop LPI active state when folding LRs Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 11/45] KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs Marc Zyngier
                   ` (35 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

EOIcount is how the virtual CPU interface signals that the guest
is deactivating interrupts outside of the LRs when EOImode==0.

We therefore need to preserve that information so that we can find
out what actually needs deactivating.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/hyp/vgic-v3-sr.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 00ad89d71bb3f..aa04cc9cdc1ab 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -225,6 +225,12 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
 
 		elrsr = read_gicreg(ICH_ELRSR_EL2);
 
+		if (cpu_if->vgic_hcr & ICH_HCR_EL2_LRENPIE) {
+			u64 val = read_gicreg(ICH_HCR_EL2);
+			cpu_if->vgic_hcr &= ~ICH_HCR_EL2_EOIcount;
+			cpu_if->vgic_hcr |= val & ICH_HCR_EL2_EOIcount;
+		}
+
 		write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
 
 		for (i = 0; i < used_lrs; i++) {
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 11/45] KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (9 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 10/45] KVM: arm64: GICv3: Preserve EOIcount on exit Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive Marc Zyngier
                   ` (34 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Not programming ICH_HCR_EL2 while no LRs are populated is a bit
of an issue, as we otherwise don't see any maintenance interrupt
when the guest interacts with the LRs.

Decouple the two and always program the control register, even when
we don't have to touch the LRs.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/hyp/vgic-v3-sr.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index aa04cc9cdc1ab..d001b26a21f16 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -219,20 +219,12 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
 		}
 	}
 
-	if (used_lrs || cpu_if->its_vpe.its_vm) {
+	if (used_lrs) {
 		int i;
 		u32 elrsr;
 
 		elrsr = read_gicreg(ICH_ELRSR_EL2);
 
-		if (cpu_if->vgic_hcr & ICH_HCR_EL2_LRENPIE) {
-			u64 val = read_gicreg(ICH_HCR_EL2);
-			cpu_if->vgic_hcr &= ~ICH_HCR_EL2_EOIcount;
-			cpu_if->vgic_hcr |= val & ICH_HCR_EL2_EOIcount;
-		}
-
-		write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
-
 		for (i = 0; i < used_lrs; i++) {
 			if (elrsr & (1 << i))
 				cpu_if->vgic_lr[i] &= ~ICH_LR_STATE;
@@ -242,6 +234,14 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
 			__gic_v3_set_lr(0, i);
 		}
 	}
+
+	if (cpu_if->vgic_hcr & ICH_HCR_EL2_LRENPIE) {
+		u64 val = read_gicreg(ICH_HCR_EL2);
+		cpu_if->vgic_hcr &= ~ICH_HCR_EL2_EOIcount;
+		cpu_if->vgic_hcr |= val & ICH_HCR_EL2_EOIcount;
+	}
+
+	write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
 }
 
 void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
@@ -249,12 +249,10 @@ void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
 	u64 used_lrs = cpu_if->used_lrs;
 	int i;
 
-	if (used_lrs || cpu_if->its_vpe.its_vm) {
-		write_gicreg(compute_ich_hcr(cpu_if), ICH_HCR_EL2);
+	write_gicreg(compute_ich_hcr(cpu_if), ICH_HCR_EL2);
 
-		for (i = 0; i < used_lrs; i++)
-			__gic_v3_set_lr(cpu_if->vgic_lr[i], i);
-	}
+	for (i = 0; i < used_lrs; i++)
+		__gic_v3_set_lr(cpu_if->vgic_lr[i], i);
 
 	/*
 	 * Ensure that writes to the LRs, and on non-VHE systems ensure that
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (10 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 11/45] KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-10  9:01   ` Yao Yuan
  2025-11-09 17:15 ` [PATCH v2 13/45] KVM: arm64: GICv3: Extract LR computing primitive Marc Zyngier
                   ` (33 subsequent siblings)
  45 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

As we are going to need to handle deactivation for interrupts that
are not in the LRs, split vgic_v3_fold_lr_state() into a helper
that deals with a single interrupt, and the function that loops
over the used LRs.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c | 88 +++++++++++++++++------------------
 1 file changed, 43 insertions(+), 45 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 3ede79e381513..0fccfe9e3e8dd 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -33,78 +33,76 @@ static bool lr_signals_eoi_mi(u64 lr_val)
 	       !(lr_val & ICH_LR_HW);
 }
 
-void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
+static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
 {
-	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
-	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
-	u32 model = vcpu->kvm->arch.vgic.vgic_model;
-	int lr;
-
-	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
-
-	cpuif->vgic_hcr &= ~ICH_HCR_EL2_UIE;
-
-	for (lr = 0; lr < cpuif->used_lrs; lr++) {
-		u64 val = cpuif->vgic_lr[lr];
-		u32 intid, cpuid;
-		struct vgic_irq *irq;
-		bool is_v2_sgi = false;
-		bool deactivated;
-
-		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 {
-			intid = val & GICH_LR_VIRTUALID;
-			is_v2_sgi = vgic_irq_is_sgi(intid);
-		}
+	struct vgic_irq *irq;
+	bool is_v2_sgi = false;
+	bool deactivated;
+	u32 intid;
 
-		/* Notify fds when the guest EOI'ed a level-triggered IRQ */
-		if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
-			kvm_notify_acked_irq(vcpu->kvm, 0,
-					     intid - VGIC_NR_PRIVATE_IRQS);
+	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
+		intid = val & ICH_LR_VIRTUAL_ID_MASK;
+	} else {
+		intid = val & GICH_LR_VIRTUALID;
+		is_v2_sgi = vgic_irq_is_sgi(intid);
+	}
 
-		irq = vgic_get_vcpu_irq(vcpu, intid);
-		if (!irq)	/* An LPI could have been unmapped. */
-			continue;
+	irq = vgic_get_vcpu_irq(vcpu, intid);
+	if (!irq)	/* An LPI could have been unmapped. */
+		return;
 
-		raw_spin_lock(&irq->irq_lock);
+	/* Notify fds when the guest EOI'ed a level-triggered IRQ */
+	if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
+		kvm_notify_acked_irq(vcpu->kvm, 0,
+				     intid - VGIC_NR_PRIVATE_IRQS);
 
+	scoped_guard(raw_spinlock, &irq->irq_lock) {
 		/* Always preserve the active bit for !LPIs, note deactivation */
 		if (irq->intid >= VGIC_MIN_LPI)
 			val &= ~ICH_LR_ACTIVE_BIT;
 		deactivated = irq->active && !(val & ICH_LR_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)) {
+		    (val & ICH_LR_PENDING_BIT))
 			irq->pending_latch = true;
 
-			if (is_v2_sgi)
-				irq->source |= (1 << cpuid);
-		}
-
 		/*
 		 * Clear soft pending state when level irqs have been acked.
 		 */
 		if (irq->config == VGIC_CONFIG_LEVEL && !(val & ICH_LR_STATE))
 			irq->pending_latch = false;
 
+		if (is_v2_sgi) {
+			u8 cpuid = FIELD_GET(GICH_LR_PHYSID_CPUID, val);
+
+			if (irq->active)
+				irq->active_source = cpuid;
+
+			if (val & ICH_LR_PENDING_BIT)
+				irq->source |= BIT(cpuid);
+		}
+
 		/* Handle resampling for mapped interrupts if required */
 		vgic_irq_handle_resampling(irq, deactivated, val & ICH_LR_PENDING_BIT);
 
 		irq->on_lr = false;
-
-		raw_spin_unlock(&irq->irq_lock);
-		vgic_put_irq(vcpu->kvm, irq);
 	}
 
+	vgic_put_irq(vcpu->kvm, irq);
+}
+
+void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
+
+	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
+
+	for (int lr = 0; lr < cpuif->used_lrs; lr++)
+		vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]);
+
 	cpuif->used_lrs = 0;
 }
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 13/45] KVM: arm64: GICv3: Extract LR computing primitive
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (11 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 14/45] KVM: arm64: GICv2: Preserve EOIcount on exit Marc Zyngier
                   ` (32 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Split vgic_v3_populate_lr() into two, so that we have another
primitive that computes the LR from a vgic_irq, but doesn't
update anything in the shadow structure.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c | 49 +++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 17 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 0fccfe9e3e8dd..4981065ca1b7e 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -107,7 +107,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 }
 
 /* Requires the irq to be locked already */
-void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
+static u64 vgic_v3_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
 {
 	u32 model = vcpu->kvm->arch.vgic.vgic_model;
 	u64 val = irq->intid;
@@ -154,6 +154,35 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	if (allow_pending && irq_is_pending(irq)) {
 		val |= ICH_LR_PENDING_BIT;
 
+		if (is_v2_sgi) {
+			u32 src = ffs(irq->source);
+
+			if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
+					   irq->intid))
+				return 0;
+
+			val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
+			if (irq->source & ~BIT(src - 1))
+				val |= ICH_LR_EOI;
+		}
+	}
+
+	if (irq->group)
+		val |= ICH_LR_GROUP;
+
+	val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT;
+
+	return val;
+}
+
+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 = vgic_v3_compute_lr(vcpu, irq);
+
+	vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = val;
+
+	if (val & ICH_LR_PENDING_BIT) {
 		if (irq->config == VGIC_CONFIG_EDGE)
 			irq->pending_latch = false;
 
@@ -161,16 +190,9 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 		    model == KVM_DEV_TYPE_ARM_VGIC_V2) {
 			u32 src = ffs(irq->source);
 
-			if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
-					   irq->intid))
-				return;
-
-			val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
-			irq->source &= ~(1 << (src - 1));
-			if (irq->source) {
+			irq->source &= ~BIT(src - 1);
+			if (irq->source)
 				irq->pending_latch = true;
-				val |= ICH_LR_EOI;
-			}
 		}
 	}
 
@@ -183,13 +205,6 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	if (vgic_irq_is_mapped_level(irq) && (val & ICH_LR_PENDING_BIT))
 		irq->line_level = false;
 
-	if (irq->group)
-		val |= ICH_LR_GROUP;
-
-	val |= (u64)irq->priority << ICH_LR_PRIORITY_SHIFT;
-
-	vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[lr] = val;
-
 	irq->on_lr = true;
 }
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 14/45] KVM: arm64: GICv2: Preserve EOIcount on exit
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (12 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 13/45] KVM: arm64: GICv3: Extract LR computing primitive Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 15/45] KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded Marc Zyngier
                   ` (31 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

EOIcount is how the virtual CPU interface signals that the guest
is deactivating interrupts outside of the LRs when EOImode==0.

We therefore need to preserve that information so that we can find
out what actually needs deactivating, just like we already do on
GICv3.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 74efacba38d42..5cfbe58983428 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -437,6 +437,12 @@ void vgic_v2_save_state(struct kvm_vcpu *vcpu)
 		return;
 
 	if (used_lrs) {
+		if (vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr & GICH_HCR_LRENPIE) {
+			u32 val = readl_relaxed(base + GICH_HCR);
+
+			vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_EOICOUNT;
+			vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= val & GICH_HCR_EOICOUNT;
+		}
 		save_lrs(vcpu, base);
 		writel_relaxed(0, base + GICH_HCR);
 	}
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 15/45] KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (13 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 14/45] KVM: arm64: GICv2: Preserve EOIcount on exit Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 16/45] KVM: arm64: GICv2: Extract LR folding primitive Marc Zyngier
                   ` (30 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Not programming GICH_HCR while no LRs are populated is a bit
of an issue, as we otherwise don't see any maintenance interrupt
when the guest interacts with the LRs.

Decouple the two and always program the control register, even when
we don't have to touch the LRs.

This is very similar to what we are already doing for GICv3.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 5cfbe58983428..a0d803c5b08ae 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -430,22 +430,25 @@ static void save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
 
 void vgic_v2_save_state(struct kvm_vcpu *vcpu)
 {
+	struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
 	void __iomem *base = kvm_vgic_global_state.vctrl_base;
 	u64 used_lrs = vcpu->arch.vgic_cpu.vgic_v2.used_lrs;
 
 	if (!base)
 		return;
 
-	if (used_lrs) {
-		if (vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr & GICH_HCR_LRENPIE) {
-			u32 val = readl_relaxed(base + GICH_HCR);
 
-			vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_EOICOUNT;
-			vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= val & GICH_HCR_EOICOUNT;
-		}
+	if (used_lrs)
 		save_lrs(vcpu, base);
-		writel_relaxed(0, base + GICH_HCR);
+
+	if (cpu_if->vgic_hcr & GICH_HCR_LRENPIE) {
+		u32 val = readl_relaxed(base + GICH_HCR);
+
+		cpu_if->vgic_hcr &= ~GICH_HCR_EOICOUNT;
+		cpu_if->vgic_hcr |= val & GICH_HCR_EOICOUNT;
 	}
+
+	writel_relaxed(0, base + GICH_HCR);
 }
 
 void vgic_v2_restore_state(struct kvm_vcpu *vcpu)
@@ -458,13 +461,10 @@ void vgic_v2_restore_state(struct kvm_vcpu *vcpu)
 	if (!base)
 		return;
 
-	if (used_lrs) {
-		writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
-		for (i = 0; i < used_lrs; i++) {
-			writel_relaxed(cpu_if->vgic_lr[i],
-				       base + GICH_LR0 + (i * 4));
-		}
-	}
+	writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
+
+	for (i = 0; i < used_lrs; i++)
+		writel_relaxed(cpu_if->vgic_lr[i], base + GICH_LR0 + (i * 4));
 }
 
 void vgic_v2_load(struct kvm_vcpu *vcpu)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 16/45] KVM: arm64: GICv2: Extract LR folding primitive
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (14 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 15/45] KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 17/45] KVM: arm64: GICv2: Extract LR computing primitive Marc Zyngier
                   ` (29 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

As we are going to need to handle deactivation for interrupts that
are not in the LRs, split vgic_v2_fold_lr_state() into a helper
that deals with a single interrupt, and the function that loops
over the used LRs.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 67 +++++++++++++++++------------------
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index a0d803c5b08ae..fb8efdd4196b1 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -39,43 +39,23 @@ static bool lr_signals_eoi_mi(u32 lr_val)
 	       !(lr_val & GICH_LR_HW);
 }
 
-/*
- * transfer the content of the LRs back into the corresponding ap_list:
- * - active bit is transferred as is
- * - pending bit is
- *   - transferred as is in case of edge sensitive IRQs
- *   - set to the line-level (resample time) for level sensitive IRQs
- */
-void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
+static void vgic_v2_fold_lr(struct kvm_vcpu *vcpu, u32 val)
 {
-	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
-	struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
-	int lr;
-
-	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
-
-	cpuif->vgic_hcr &= ~GICH_HCR_UIE;
+	u32 cpuid, intid = val & GICH_LR_VIRTUALID;
+	struct vgic_irq *irq;
+	bool deactivated;
 
-	for (lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++) {
-		u32 val = cpuif->vgic_lr[lr];
-		u32 cpuid, intid = val & GICH_LR_VIRTUALID;
-		struct vgic_irq *irq;
-		bool deactivated;
+	/* Extract the source vCPU id from the LR */
+	cpuid = FIELD_GET(GICH_LR_PHYSID_CPUID, val) & 7;
 
-		/* 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,
+				     intid - VGIC_NR_PRIVATE_IRQS);
 
-		/* 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,
-					     intid - VGIC_NR_PRIVATE_IRQS);
-
-		irq = vgic_get_vcpu_irq(vcpu, intid);
-
-		raw_spin_lock(&irq->irq_lock);
+	irq = vgic_get_vcpu_irq(vcpu, intid);
 
+	scoped_guard(raw_spinlock, &irq->irq_lock) {
 		/* Always preserve the active bit, note deactivation */
 		deactivated = irq->active && !(val & GICH_LR_ACTIVE_BIT);
 		irq->active = !!(val & GICH_LR_ACTIVE_BIT);
@@ -102,11 +82,28 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 		vgic_irq_handle_resampling(irq, deactivated, val & GICH_LR_PENDING_BIT);
 
 		irq->on_lr = false;
-
-		raw_spin_unlock(&irq->irq_lock);
-		vgic_put_irq(vcpu->kvm, irq);
 	}
 
+	vgic_put_irq(vcpu->kvm, irq);
+}
+
+/*
+ * transfer the content of the LRs back into the corresponding ap_list:
+ * - active bit is transferred as is
+ * - pending bit is
+ *   - transferred as is in case of edge sensitive IRQs
+ *   - set to the line-level (resample time) for level sensitive IRQs
+ */
+void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
+
+	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
+
+	for (int lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++)
+		vgic_v2_fold_lr(vcpu, cpuif->vgic_lr[lr]);
+
 	cpuif->used_lrs = 0;
 }
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 17/45] KVM: arm64: GICv2: Extract LR computing primitive
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (15 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 16/45] KVM: arm64: GICv2: Extract LR folding primitive Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 18/45] KVM: arm64: Compute vgic state irrespective of the number of interrupts Marc Zyngier
                   ` (28 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Split vgic_v2_populate_lr() into two helpers, so that we have another
primitive that computes the LR from a vgic_irq, but doesn't update
anything in the shadow structure.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 61 ++++++++++++++++++++++-------------
 1 file changed, 39 insertions(+), 22 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index fb8efdd4196b1..5a2165a8d22c0 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -107,18 +107,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 	cpuif->used_lrs = 0;
 }
 
-/*
- * Populates the particular LR with the state of a given IRQ:
- * - for an edge sensitive IRQ the pending state is cleared in struct vgic_irq
- * - for a level sensitive IRQ the pending state value is unchanged;
- *   it is dictated directly by the input level
- *
- * If @irq describes an SGI with multiple sources, we choose the
- * lowest-numbered source VCPU and clear that bit in the source bitmap.
- *
- * The irq_lock must be held by the caller.
- */
-void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
+static u32 vgic_v2_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
 {
 	u32 val = irq->intid;
 	bool allow_pending = true;
@@ -164,22 +153,52 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	if (allow_pending && irq_is_pending(irq)) {
 		val |= GICH_LR_PENDING_BIT;
 
-		if (irq->config == VGIC_CONFIG_EDGE)
-			irq->pending_latch = false;
-
 		if (vgic_irq_is_sgi(irq->intid)) {
 			u32 src = ffs(irq->source);
 
 			if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
 					   irq->intid))
-				return;
+				return 0;
 
 			val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
-			irq->source &= ~(1 << (src - 1));
-			if (irq->source) {
-				irq->pending_latch = true;
+			if (irq->source & ~BIT(src - 1))
 				val |= GICH_LR_EOI;
-			}
+		}
+	}
+
+	/* The GICv2 LR only holds five bits of priority. */
+	val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
+
+	return val;
+}
+
+/*
+ * Populates the particular LR with the state of a given IRQ:
+ * - for an edge sensitive IRQ the pending state is cleared in struct vgic_irq
+ * - for a level sensitive IRQ the pending state value is unchanged;
+ *   it is dictated directly by the input level
+ *
+ * If @irq describes an SGI with multiple sources, we choose the
+ * lowest-numbered source VCPU and clear that bit in the source bitmap.
+ *
+ * The irq_lock must be held by the caller.
+ */
+void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
+{
+	u32 val = vgic_v2_compute_lr(vcpu, irq);
+
+	vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = val;
+
+	if (val & GICH_LR_PENDING_BIT) {
+		if (irq->config == VGIC_CONFIG_EDGE)
+			irq->pending_latch = false;
+
+		if (vgic_irq_is_sgi(irq->intid)) {
+			u32 src = ffs(irq->source);
+
+			irq->source &= ~BIT(src - 1);
+			if (irq->source)
+				irq->pending_latch = true;
 		}
 	}
 
@@ -196,8 +215,6 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
 	val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
 
 	irq->on_lr = true;
-
-	vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = val;
 }
 
 void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 18/45] KVM: arm64: Compute vgic state irrespective of the number of interrupts
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (16 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 17/45] KVM: arm64: GICv2: Extract LR computing primitive Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 19/45] KVM: arm64: Eagerly save VMCR on exit Marc Zyngier
                   ` (27 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

As we are going to rely on the [G]ICH_HCR{,_EL2} register to be
programmed with MI information at all times, slightly de-optimise
the flush/sync code to always be called. This is rather lightweight
when no interrupts are in flight.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic.c | 35 ++---------------------------------
 1 file changed, 2 insertions(+), 33 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 8d7f6803e601b..46b31cd365466 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -985,8 +985,6 @@ static inline void vgic_save_state(struct kvm_vcpu *vcpu)
 /* Sync back the hardware VGIC state into our emulation after a guest's run. */
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 {
-	int used_lrs;
-
 	/* If nesting, emulate the HW effect from L0 to L1 */
 	if (vgic_state_is_nested(vcpu)) {
 		vgic_v3_sync_nested(vcpu);
@@ -996,20 +994,10 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 	if (vcpu_has_nv(vcpu))
 		vgic_v3_nested_update_mi(vcpu);
 
-	/* An empty ap_list_head implies used_lrs == 0 */
-	if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head))
-		return;
-
 	if (can_access_vgic_from_kernel())
 		vgic_save_state(vcpu);
 
-	if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
-		used_lrs = vcpu->arch.vgic_cpu.vgic_v2.used_lrs;
-	else
-		used_lrs = vcpu->arch.vgic_cpu.vgic_v3.used_lrs;
-
-	if (used_lrs)
-		vgic_fold_lr_state(vcpu);
+	vgic_fold_lr_state(vcpu);
 	vgic_prune_ap_list(vcpu);
 }
 
@@ -1053,29 +1041,10 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
 	if (vcpu_has_nv(vcpu))
 		vgic_v3_nested_update_mi(vcpu);
 
-	/*
-	 * If there are no virtual interrupts active or pending for this
-	 * VCPU, then there is no work to do and we can bail out without
-	 * taking any lock.  There is a potential race with someone injecting
-	 * interrupts to the VCPU, but it is a benign race as the VCPU will
-	 * either observe the new interrupt before or after doing this check,
-	 * and introducing additional synchronization mechanism doesn't change
-	 * this.
-	 *
-	 * Note that we still need to go through the whole thing if anything
-	 * can be directly injected (GICv4).
-	 */
-	if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head) &&
-	    !vgic_supports_direct_irqs(vcpu->kvm))
-		return;
-
 	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
 
-	if (!list_empty(&vcpu->arch.vgic_cpu.ap_list_head)) {
-		raw_spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
+	scoped_guard(raw_spinlock, &vcpu->arch.vgic_cpu.ap_list_lock)
 		vgic_flush_lr_state(vcpu);
-		raw_spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
-	}
 
 	if (can_access_vgic_from_kernel())
 		vgic_restore_state(vcpu);
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 19/45] KVM: arm64: Eagerly save VMCR on exit
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (17 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 18/45] KVM: arm64: Compute vgic state irrespective of the number of interrupts Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration Marc Zyngier
                   ` (26 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

We currently save/restore the VMCR register in a pretty lazy way
(on load/put, consistently with what we do with the APRs).

However, we are going to need the group-enable bits that are backed
by VMCR on each entry (so that we can avoid injecting interrupts for
disabled groups).

Move the synchronisation from put to sync, which results in some minor
churn in the nVHE hypercalls to simplify things.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/include/asm/kvm_asm.h     |  2 +-
 arch/arm64/include/asm/kvm_hyp.h     |  2 +-
 arch/arm64/kvm/arm.c                 |  3 +--
 arch/arm64/kvm/hyp/nvhe/hyp-main.c   |  7 ++++---
 arch/arm64/kvm/hyp/vgic-v3-sr.c      | 15 +++------------
 arch/arm64/kvm/vgic/vgic-v2.c        |  2 +-
 arch/arm64/kvm/vgic/vgic-v3-nested.c |  2 +-
 arch/arm64/kvm/vgic/vgic-v3.c        |  2 +-
 8 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 9da54d4ee49ee..f8adbd535b4ae 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -79,7 +79,7 @@ enum __kvm_host_smccc_func {
 	__KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range,
 	__KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context,
 	__KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff,
-	__KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs,
+	__KVM_HOST_SMCCC_FUNC___vgic_v3_save_aprs,
 	__KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs,
 	__KVM_HOST_SMCCC_FUNC___pkvm_reserve_vm,
 	__KVM_HOST_SMCCC_FUNC___pkvm_unreserve_vm,
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index e6be1f5d0967f..dbf16a9f67728 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -82,7 +82,7 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if);
 void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if);
 void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if);
 void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if);
-void __vgic_v3_save_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if);
+void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if);
 void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if);
 int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu);
 
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 870953b4a8a74..733195ef183e1 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -659,8 +659,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
 	if (is_protected_kvm_enabled()) {
-		kvm_call_hyp(__vgic_v3_save_vmcr_aprs,
-			     &vcpu->arch.vgic_cpu.vgic_v3);
+		kvm_call_hyp(__vgic_v3_save_aprs, &vcpu->arch.vgic_cpu.vgic_v3);
 		kvm_call_hyp_nvhe(__pkvm_vcpu_put);
 	}
 
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 29430c031095a..a7c689152f686 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -157,6 +157,7 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
 	host_vcpu->arch.iflags		= hyp_vcpu->vcpu.arch.iflags;
 
 	host_cpu_if->vgic_hcr		= hyp_cpu_if->vgic_hcr;
+	host_cpu_if->vgic_vmcr		= hyp_cpu_if->vgic_vmcr;
 	for (i = 0; i < hyp_cpu_if->used_lrs; ++i)
 		host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
 }
@@ -464,11 +465,11 @@ static void handle___vgic_v3_init_lrs(struct kvm_cpu_context *host_ctxt)
 	__vgic_v3_init_lrs();
 }
 
-static void handle___vgic_v3_save_vmcr_aprs(struct kvm_cpu_context *host_ctxt)
+static void handle___vgic_v3_save_aprs(struct kvm_cpu_context *host_ctxt)
 {
 	DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1);
 
-	__vgic_v3_save_vmcr_aprs(kern_hyp_va(cpu_if));
+	__vgic_v3_save_aprs(kern_hyp_va(cpu_if));
 }
 
 static void handle___vgic_v3_restore_vmcr_aprs(struct kvm_cpu_context *host_ctxt)
@@ -616,7 +617,7 @@ static const hcall_t host_hcall[] = {
 	HANDLE_FUNC(__kvm_tlb_flush_vmid_range),
 	HANDLE_FUNC(__kvm_flush_cpu_context),
 	HANDLE_FUNC(__kvm_timer_set_cntvoff),
-	HANDLE_FUNC(__vgic_v3_save_vmcr_aprs),
+	HANDLE_FUNC(__vgic_v3_save_aprs),
 	HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs),
 	HANDLE_FUNC(__pkvm_reserve_vm),
 	HANDLE_FUNC(__pkvm_unreserve_vm),
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index d001b26a21f16..1e5c1cf4b9144 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -235,6 +235,8 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
 		}
 	}
 
+	cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
+
 	if (cpu_if->vgic_hcr & ICH_HCR_EL2_LRENPIE) {
 		u64 val = read_gicreg(ICH_HCR_EL2);
 		cpu_if->vgic_hcr &= ~ICH_HCR_EL2_EOIcount;
@@ -332,10 +334,6 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
 {
 	u64 val;
 
-	if (!cpu_if->vgic_sre) {
-		cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
-	}
-
 	/* Only restore SRE if the host implements the GICv2 interface */
 	if (static_branch_unlikely(&vgic_v3_has_v2_compat)) {
 		val = read_gicreg(ICC_SRE_EL2);
@@ -357,7 +355,7 @@ void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
 		write_gicreg(0, ICH_HCR_EL2);
 }
 
-static void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if)
+void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if)
 {
 	u64 val;
 	u32 nr_pre_bits;
@@ -518,13 +516,6 @@ static void __vgic_v3_write_vmcr(u32 vmcr)
 	write_gicreg(vmcr, ICH_VMCR_EL2);
 }
 
-void __vgic_v3_save_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if)
-{
-	__vgic_v3_save_aprs(cpu_if);
-	if (cpu_if->vgic_sre)
-		cpu_if->vgic_vmcr = __vgic_v3_read_vmcr();
-}
-
 void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if)
 {
 	__vgic_v3_compat_mode_enable();
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 5a2165a8d22c0..07e93acafd04d 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -451,6 +451,7 @@ void vgic_v2_save_state(struct kvm_vcpu *vcpu)
 	if (!base)
 		return;
 
+	cpu_if->vgic_vmcr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VMCR);
 
 	if (used_lrs)
 		save_lrs(vcpu, base);
@@ -495,6 +496,5 @@ void vgic_v2_put(struct kvm_vcpu *vcpu)
 {
 	struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
 
-	cpu_if->vgic_vmcr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VMCR);
 	cpu_if->vgic_apr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_APR);
 }
diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c
index 387557e20a272..17bceef83269e 100644
--- a/arch/arm64/kvm/vgic/vgic-v3-nested.c
+++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c
@@ -341,7 +341,7 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu)
 	u64 val;
 	int i;
 
-	__vgic_v3_save_vmcr_aprs(s_cpu_if);
+	__vgic_v3_save_aprs(s_cpu_if);
 	__vgic_v3_deactivate_traps(s_cpu_if);
 	__vgic_v3_save_state(s_cpu_if);
 
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 4981065ca1b7e..2619e120484c5 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -815,7 +815,7 @@ void vgic_v3_put(struct kvm_vcpu *vcpu)
 	}
 
 	if (likely(!is_protected_kvm_enabled()))
-		kvm_call_hyp(__vgic_v3_save_vmcr_aprs, cpu_if);
+		kvm_call_hyp(__vgic_v3_save_aprs, cpu_if);
 	WARN_ON(vgic_v4_put(vcpu));
 
 	if (has_vhe())
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (18 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 19/45] KVM: arm64: Eagerly save VMCR on exit Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-12  0:08   ` Oliver Upton
  2025-11-09 17:15 ` [PATCH v2 21/45] KVM: arm64: Turn kvm_vgic_vcpu_enable() into kvm_vgic_vcpu_reset() Marc Zyngier
                   ` (25 subsequent siblings)
  45 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

We currently don't use the maintenance interrupt very much, apart
from EOI on level interrupts, and for LR underflow in limited cases.

However, as we are moving toward a setup where active interrupts
can live outside of the LRs, we need to use the MIs in a more
diverse set of cases.

Add a new helper that produces a digest of the ap_list, and use
that summary to set the various control bits as required.

This slightly changes the way v2 SGIs are handled, as they used to
count for more than one interrupt, but not anymore.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 12 ++++-
 arch/arm64/kvm/vgic/vgic-v3.c | 15 +++++-
 arch/arm64/kvm/vgic/vgic.c    | 91 ++++++++++++-----------------------
 arch/arm64/kvm/vgic/vgic.h    | 19 +++++++-
 4 files changed, 71 insertions(+), 66 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 07e93acafd04d..f53bc55288978 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -26,11 +26,19 @@ void vgic_v2_init_lrs(void)
 		vgic_v2_write_lr(i, 0);
 }
 
-void vgic_v2_set_underflow(struct kvm_vcpu *vcpu)
+void vgic_v2_configure_hcr(struct kvm_vcpu *vcpu,
+			   struct ap_list_summary *als)
 {
 	struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
 
-	cpuif->vgic_hcr |= GICH_HCR_UIE;
+	cpuif->vgic_hcr = GICH_HCR_EN;
+
+	if (irqs_pending_outside_lrs(als))
+		cpuif->vgic_hcr |= GICH_HCR_NPIE;
+	if (irqs_active_outside_lrs(als))
+		cpuif->vgic_hcr |= GICH_HCR_LRENPIE;
+	if (irqs_outside_lrs(als))
+		cpuif->vgic_hcr |= GICH_HCR_UIE;
 }
 
 static bool lr_signals_eoi_mi(u32 lr_val)
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 2619e120484c5..d23db8f7b450a 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -20,11 +20,22 @@ static bool common_trap;
 static bool dir_trap;
 static bool gicv4_enable;
 
-void vgic_v3_set_underflow(struct kvm_vcpu *vcpu)
+void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
+			   struct ap_list_summary *als)
 {
 	struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
 
-	cpuif->vgic_hcr |= ICH_HCR_EL2_UIE;
+	cpuif->vgic_hcr = ICH_HCR_EL2_En;
+
+	if (irqs_pending_outside_lrs(als))
+		cpuif->vgic_hcr |= ICH_HCR_EL2_NPIE;
+	if (irqs_active_outside_lrs(als))
+		cpuif->vgic_hcr |= ICH_HCR_EL2_LRENPIE;
+	if (irqs_outside_lrs(als))
+		cpuif->vgic_hcr |= ICH_HCR_EL2_UIE;
+
+	if (!als->nr_sgi)
+		cpuif->vgic_hcr |= ICH_HCR_EL2_vSGIEOICount;
 }
 
 static bool lr_signals_eoi_mi(u64 lr_val)
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 46b31cd365466..c420f8262e4c1 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -791,38 +791,30 @@ static inline void vgic_clear_lr(struct kvm_vcpu *vcpu, int lr)
 		vgic_v3_clear_lr(vcpu, lr);
 }
 
-static inline void vgic_set_underflow(struct kvm_vcpu *vcpu)
-{
-	if (kvm_vgic_global_state.type == VGIC_V2)
-		vgic_v2_set_underflow(vcpu);
-	else
-		vgic_v3_set_underflow(vcpu);
-}
-
-/* Requires the ap_list_lock to be held. */
-static int compute_ap_list_depth(struct kvm_vcpu *vcpu,
-				 bool *multi_sgi)
+static void summarize_ap_list(struct kvm_vcpu *vcpu,
+			      struct ap_list_summary *als)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 	struct vgic_irq *irq;
-	int count = 0;
-
-	*multi_sgi = false;
 
 	lockdep_assert_held(&vgic_cpu->ap_list_lock);
 
-	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
-		int w;
+	*als = (typeof(*als)){};
 
-		raw_spin_lock(&irq->irq_lock);
-		/* GICv2 SGIs can count for more than one... */
-		w = vgic_irq_get_lr_count(irq);
-		raw_spin_unlock(&irq->irq_lock);
+	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
+		scoped_guard(raw_spinlock, &irq->irq_lock) {
+			if (vgic_target_oracle(irq) != vcpu)
+				continue;
+
+			if (!irq->active)
+				als->nr_pend++;
+			else
+				als->nr_act++;
+		}
 
-		count += w;
-		*multi_sgi |= (w > 1);
+		if (irq->intid < VGIC_NR_SGIS)
+			als->nr_sgi++;
 	}
-	return count;
 }
 
 /*
@@ -908,60 +900,39 @@ static int compute_ap_list_depth(struct kvm_vcpu *vcpu,
 static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct ap_list_summary als;
 	struct vgic_irq *irq;
-	int count;
-	bool multi_sgi;
-	u8 prio = 0xff;
-	int i = 0;
+	int count = 0;
 
 	lockdep_assert_held(&vgic_cpu->ap_list_lock);
 
-	count = compute_ap_list_depth(vcpu, &multi_sgi);
-	if (count > kvm_vgic_global_state.nr_lr || multi_sgi)
-		vgic_sort_ap_list(vcpu);
+	summarize_ap_list(vcpu, &als);
 
-	count = 0;
+	if (irqs_outside_lrs(&als))
+		vgic_sort_ap_list(vcpu);
 
 	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
-		raw_spin_lock(&irq->irq_lock);
-
-		/*
-		 * If we have multi-SGIs in the pipeline, we need to
-		 * guarantee that they are all seen before any IRQ of
-		 * lower priority. In that case, we need to filter out
-		 * these interrupts by exiting early. This is easy as
-		 * the AP list has been sorted already.
-		 */
-		if (multi_sgi && irq->priority > prio) {
-			raw_spin_unlock(&irq->irq_lock);
-			break;
+		scoped_guard(raw_spinlock,  &irq->irq_lock) {
+			if (likely(vgic_target_oracle(irq) == vcpu)) {
+				vgic_populate_lr(vcpu, irq, count++);
+			}
 		}
 
-		if (likely(vgic_target_oracle(irq) == vcpu)) {
-			vgic_populate_lr(vcpu, irq, count++);
-
-			if (irq->source)
-				prio = irq->priority;
-		}
-
-		raw_spin_unlock(&irq->irq_lock);
-
-		if (count == kvm_vgic_global_state.nr_lr) {
-			if (!list_is_last(&irq->ap_list,
-					  &vgic_cpu->ap_list_head))
-				vgic_set_underflow(vcpu);
+		if (count == kvm_vgic_global_state.nr_lr)
 			break;
-		}
 	}
 
 	/* Nuke remaining LRs */
-	for (i = count ; i < kvm_vgic_global_state.nr_lr; i++)
+	for (int i = count ; i < kvm_vgic_global_state.nr_lr; i++)
 		vgic_clear_lr(vcpu, i);
 
-	if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
+	if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) {
 		vcpu->arch.vgic_cpu.vgic_v2.used_lrs = count;
-	else
+		vgic_v2_configure_hcr(vcpu, &als);
+	} else {
 		vcpu->arch.vgic_cpu.vgic_v3.used_lrs = count;
+		vgic_v3_configure_hcr(vcpu, &als);
+	}
 }
 
 static inline bool can_access_vgic_from_kernel(void)
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 0ecadfa00397d..4a0733869cb5f 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -236,6 +236,21 @@ struct its_ite {
 	u32 event_id;
 };
 
+struct ap_list_summary {
+	unsigned int	nr_pend;	/* purely pending, not active */
+	unsigned int	nr_act;		/* active, or active+pending */
+	unsigned int	nr_sgi;		/* any SGI */
+};
+
+#define irqs_outside_lrs(s)						\
+	 (((s)->nr_pend + (s)->nr_act) > kvm_vgic_global_state.nr_lr)
+
+#define irqs_pending_outside_lrs(s)			\
+	((s)->nr_pend > kvm_vgic_global_state.nr_lr)
+
+#define irqs_active_outside_lrs(s)		\
+	((s)->nr_act &&	irqs_outside_lrs(s))
+
 int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
 		       struct vgic_reg_attr *reg_attr);
 int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
@@ -262,7 +277,7 @@ int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr,
 void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
 void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
 void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
-void vgic_v2_set_underflow(struct kvm_vcpu *vcpu);
+void vgic_v2_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
 int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
 int vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
 			 int offset, u32 *val);
@@ -302,7 +317,7 @@ static inline void vgic_get_irq_ref(struct vgic_irq *irq)
 void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);
 void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
 void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr);
-void vgic_v3_set_underflow(struct kvm_vcpu *vcpu);
+void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
 void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
 void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
 void vgic_v3_enable(struct kvm_vcpu *vcpu);
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 21/45] KVM: arm64: Turn kvm_vgic_vcpu_enable() into kvm_vgic_vcpu_reset()
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (19 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 22/45] KVM: arm64: Make vgic_target_oracle() globally available Marc Zyngier
                   ` (24 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Now that we always reconfigure the vgic HCR register on entry,
the "enable" part of kvm_vgic_vcpu_enable() is pretty useless.

Removing the enable bits from these functions makes it plain that
they are just about computing the reset state. Just rename the
functions accordingly.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-init.c | 8 ++++----
 arch/arm64/kvm/vgic/vgic-v2.c   | 5 +----
 arch/arm64/kvm/vgic/vgic-v3.c   | 5 +----
 arch/arm64/kvm/vgic/vgic.h      | 4 ++--
 4 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 1796b1a22a72a..6d5e5d708f23a 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -353,12 +353,12 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
 	return ret;
 }
 
-static void kvm_vgic_vcpu_enable(struct kvm_vcpu *vcpu)
+static void kvm_vgic_vcpu_reset(struct kvm_vcpu *vcpu)
 {
 	if (kvm_vgic_global_state.type == VGIC_V2)
-		vgic_v2_enable(vcpu);
+		vgic_v2_reset(vcpu);
 	else
-		vgic_v3_enable(vcpu);
+		vgic_v3_reset(vcpu);
 }
 
 /*
@@ -405,7 +405,7 @@ int vgic_init(struct kvm *kvm)
 	}
 
 	kvm_for_each_vcpu(idx, vcpu, kvm)
-		kvm_vgic_vcpu_enable(vcpu);
+		kvm_vgic_vcpu_reset(vcpu);
 
 	ret = kvm_vgic_setup_default_irq_routing(kvm);
 	if (ret)
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index f53bc55288978..18856186be7be 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -285,7 +285,7 @@ void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
 			GICH_VMCR_PRIMASK_SHIFT) << GICV_PMR_PRIORITY_SHIFT;
 }
 
-void vgic_v2_enable(struct kvm_vcpu *vcpu)
+void vgic_v2_reset(struct kvm_vcpu *vcpu)
 {
 	/*
 	 * By forcing VMCR to zero, the GIC will restore the binary
@@ -293,9 +293,6 @@ void vgic_v2_enable(struct kvm_vcpu *vcpu)
 	 * anyway.
 	 */
 	vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0;
-
-	/* Get the show on the road... */
-	vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN;
 }
 
 /* check for overlapping regions and for regions crossing the end of memory */
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index d23db8f7b450a..5a57f3c299b56 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -290,7 +290,7 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
 	GIC_BASER_CACHEABILITY(GICR_PENDBASER, OUTER, SameAsInner)	| \
 	GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable))
 
-void vgic_v3_enable(struct kvm_vcpu *vcpu)
+void vgic_v3_reset(struct kvm_vcpu *vcpu)
 {
 	struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3;
 
@@ -320,9 +320,6 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu)
 						    kvm_vgic_global_state.ich_vtr_el2);
 	vcpu->arch.vgic_cpu.num_pri_bits = FIELD_GET(ICH_VTR_EL2_PRIbits,
 						     kvm_vgic_global_state.ich_vtr_el2) + 1;
-
-	/* Get the show on the road... */
-	vgic_v3->vgic_hcr = ICH_HCR_EL2_En;
 }
 
 void vcpu_set_ich_hcr(struct kvm_vcpu *vcpu)
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 4a0733869cb5f..e48294521541e 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -285,7 +285,7 @@ int vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
 			  int offset, u32 *val);
 void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
 void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
-void vgic_v2_enable(struct kvm_vcpu *vcpu);
+void vgic_v2_reset(struct kvm_vcpu *vcpu);
 int vgic_v2_probe(const struct gic_kvm_info *info);
 int vgic_v2_map_resources(struct kvm *kvm);
 int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
@@ -320,7 +320,7 @@ void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr);
 void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
 void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
 void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
-void vgic_v3_enable(struct kvm_vcpu *vcpu);
+void vgic_v3_reset(struct kvm_vcpu *vcpu);
 int vgic_v3_probe(const struct gic_kvm_info *info);
 int vgic_v3_map_resources(struct kvm *kvm);
 int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq);
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 22/45] KVM: arm64: Make vgic_target_oracle() globally available
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (20 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 21/45] KVM: arm64: Turn kvm_vgic_vcpu_enable() into kvm_vgic_vcpu_reset() Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 23/45] KVM: arm64: Invert ap_list sorting to push active interrupts out Marc Zyngier
                   ` (23 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Make the internal crystal ball global, so that implementation-specific
code can use it.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic.c | 2 +-
 arch/arm64/kvm/vgic/vgic.h | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index c420f8262e4c1..570cc8fe42b87 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -237,7 +237,7 @@ void vgic_irq_set_phys_active(struct vgic_irq *irq, bool active)
  *
  * Requires the IRQ lock to be held.
  */
-static struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)
+struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)
 {
 	lockdep_assert_held(&irq->irq_lock);
 
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index e48294521541e..037efb6200823 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -261,6 +261,7 @@ vgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev,
 struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid);
 struct vgic_irq *vgic_get_vcpu_irq(struct kvm_vcpu *vcpu, u32 intid);
 void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
+struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq);
 bool vgic_get_phys_line_level(struct vgic_irq *irq);
 void vgic_irq_set_phys_pending(struct vgic_irq *irq, bool pending);
 void vgic_irq_set_phys_active(struct vgic_irq *irq, bool active);
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 23/45] KVM: arm64: Invert ap_list sorting to push active interrupts out
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (21 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 22/45] KVM: arm64: Make vgic_target_oracle() globally available Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 24/45] KVM: arm64: Move undeliverable interrupts to the end of ap_list Marc Zyngier
                   ` (22 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Having established that pending interrupts should have priority
to be moved into the LRs over the active interrupts, implement this
in the ap_list sorting.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic.c | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 570cc8fe42b87..56c61e17e1e88 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -270,10 +270,7 @@ struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)
  * well, the first items in the list being the first things populated in the
  * LRs.
  *
- * A hard rule is that active interrupts can never be pushed out of the LRs
- * (and therefore take priority) since we cannot reliably trap on deactivation
- * of IRQs and therefore they have to be present in the LRs.
- *
+ * Pending, non-active interrupts must be placed at the head of the list.
  * Otherwise things should be sorted by the priority field and the GIC
  * hardware support will take care of preemption of priority groups etc.
  *
@@ -298,21 +295,21 @@ static int vgic_irq_cmp(void *priv, const struct list_head *a,
 	raw_spin_lock(&irqa->irq_lock);
 	raw_spin_lock_nested(&irqb->irq_lock, SINGLE_DEPTH_NESTING);
 
-	if (irqa->active || irqb->active) {
-		ret = (int)irqb->active - (int)irqa->active;
-		goto out;
-	}
+	penda = irqa->enabled && irq_is_pending(irqa) && !irqa->active;
+	pendb = irqb->enabled && irq_is_pending(irqb) && !irqb->active;
 
-	penda = irqa->enabled && irq_is_pending(irqa);
-	pendb = irqb->enabled && irq_is_pending(irqb);
+	ret = (int)pendb - (int)penda;
+	if (ret)
+		goto out;
 
-	if (!penda || !pendb) {
-		ret = (int)pendb - (int)penda;
+	/* Both pending and enabled, sort by priority (lower number first) */
+	ret = (int)irqa->priority - (int)irqb->priority;
+	if (ret)
 		goto out;
-	}
 
-	/* Both pending and enabled, sort by priority */
-	ret = irqa->priority - irqb->priority;
+	/* Finally, HW bit active interrupts have priority over non-HW ones */
+	ret = (int)irqb->hw - (int)irqa->hw;
+
 out:
 	raw_spin_unlock(&irqb->irq_lock);
 	raw_spin_unlock(&irqa->irq_lock);
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 24/45] KVM: arm64: Move undeliverable interrupts to the end of ap_list
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (22 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 23/45] KVM: arm64: Invert ap_list sorting to push active interrupts out Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:15 ` [PATCH v2 25/45] KVM: arm64: Use MI to detect groups being enabled/disabled Marc Zyngier
                   ` (21 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Interrupts in the ap_list that cannot be acted upon because they
are not enabled, or that their group is not enabled, shouldn't
make it into the LRs if we are space-constrained.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 56c61e17e1e88..5c9204d18b27d 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -265,6 +265,11 @@ struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)
 	return NULL;
 }
 
+struct vgic_sort_info {
+	struct kvm_vcpu *vcpu;
+	struct vgic_vmcr vmcr;
+};
+
 /*
  * The order of items in the ap_lists defines how we'll pack things in LRs as
  * well, the first items in the list being the first things populated in the
@@ -273,6 +278,7 @@ struct kvm_vcpu *vgic_target_oracle(struct vgic_irq *irq)
  * Pending, non-active interrupts must be placed at the head of the list.
  * Otherwise things should be sorted by the priority field and the GIC
  * hardware support will take care of preemption of priority groups etc.
+ * Interrupts that are not deliverable should be at the end of the list.
  *
  * Return negative if "a" sorts before "b", 0 to preserve order, and positive
  * to sort "b" before "a".
@@ -282,6 +288,8 @@ static int vgic_irq_cmp(void *priv, const struct list_head *a,
 {
 	struct vgic_irq *irqa = container_of(a, struct vgic_irq, ap_list);
 	struct vgic_irq *irqb = container_of(b, struct vgic_irq, ap_list);
+	struct vgic_sort_info *info = priv;
+	struct kvm_vcpu *vcpu = info->vcpu;
 	bool penda, pendb;
 	int ret;
 
@@ -295,6 +303,17 @@ static int vgic_irq_cmp(void *priv, const struct list_head *a,
 	raw_spin_lock(&irqa->irq_lock);
 	raw_spin_lock_nested(&irqb->irq_lock, SINGLE_DEPTH_NESTING);
 
+	/* Undeliverable interrupts should be last */
+	ret = (int)(vgic_target_oracle(irqb) == vcpu) - (int)(vgic_target_oracle(irqa) == vcpu);
+	if (ret)
+		goto out;
+
+	/* Same thing for interrupts targeting a disabled group */
+	ret =  (int)(irqb->group ? info->vmcr.grpen1 : info->vmcr.grpen0);
+	ret -= (int)(irqa->group ? info->vmcr.grpen1 : info->vmcr.grpen0);
+	if (ret)
+		goto out;
+
 	penda = irqa->enabled && irq_is_pending(irqa) && !irqa->active;
 	pendb = irqb->enabled && irq_is_pending(irqb) && !irqb->active;
 
@@ -320,10 +339,12 @@ static int vgic_irq_cmp(void *priv, const struct list_head *a,
 static void vgic_sort_ap_list(struct kvm_vcpu *vcpu)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_sort_info info = { .vcpu = vcpu, };
 
 	lockdep_assert_held(&vgic_cpu->ap_list_lock);
 
-	list_sort(NULL, &vgic_cpu->ap_list_head, vgic_irq_cmp);
+	vgic_get_vmcr(vcpu, &info.vmcr);
+	list_sort(&info, &vgic_cpu->ap_list_head, vgic_irq_cmp);
 }
 
 /*
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 25/45] KVM: arm64: Use MI to detect groups being enabled/disabled
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (23 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 24/45] KVM: arm64: Move undeliverable interrupts to the end of ap_list Marc Zyngier
@ 2025-11-09 17:15 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 26/45] KVM: arm64: GICv3: Handle LR overflow when EOImode==0 Marc Zyngier
                   ` (20 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:15 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Add the maintenance interrupt to force an exit when the guest
enables/disables individual groups, so that we can resort the
ap_list accordingly.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 5 +++++
 arch/arm64/kvm/vgic/vgic-v3.c | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 18856186be7be..9a2de03f74c30 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -39,6 +39,11 @@ void vgic_v2_configure_hcr(struct kvm_vcpu *vcpu,
 		cpuif->vgic_hcr |= GICH_HCR_LRENPIE;
 	if (irqs_outside_lrs(als))
 		cpuif->vgic_hcr |= GICH_HCR_UIE;
+
+	cpuif->vgic_hcr |= (cpuif->vgic_vmcr & GICH_VMCR_ENABLE_GRP0_MASK) ?
+		GICH_HCR_VGrp0DIE : GICH_HCR_VGrp0EIE;
+	cpuif->vgic_hcr |= (cpuif->vgic_vmcr & GICH_VMCR_ENABLE_GRP1_MASK) ?
+		GICH_HCR_VGrp1DIE : GICH_HCR_VGrp1EIE;
 }
 
 static bool lr_signals_eoi_mi(u32 lr_val)
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 5a57f3c299b56..b5cf68bc1ea9f 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -36,6 +36,11 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
 
 	if (!als->nr_sgi)
 		cpuif->vgic_hcr |= ICH_HCR_EL2_vSGIEOICount;
+
+	cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG0_MASK) ?
+		ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
+	cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
+		ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
 }
 
 static bool lr_signals_eoi_mi(u64 lr_val)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 26/45] KVM: arm64: GICv3: Handle LR overflow when EOImode==0
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (24 preceding siblings ...)
  2025-11-09 17:15 ` [PATCH v2 25/45] KVM: arm64: Use MI to detect groups being enabled/disabled Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 27/45] KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps Marc Zyngier
                   ` (19 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Now that we can identify interrupts that have not made it into the LRs,
it becomes relatively easy to use EOIcount to walk the overflow list.

What is a bit odd is that we compute a fake LR for the original
state of the interrupt, clear the active bit, and feed into the existing
logic for processing. In a way, this is what would have happened if
the interrupt was in an LR.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c | 46 +++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index b5cf68bc1ea9f..e17eefc6a2bb6 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -109,16 +109,62 @@ static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
 	vgic_put_irq(vcpu->kvm, irq);
 }
 
+static u64 vgic_v3_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq);
+
+static void vgic_v3_deactivate_phys(u32 intid)
+{
+	if (cpus_have_final_cap(ARM64_HAS_GICV5_LEGACY))
+		gic_insn(intid | FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, 1), CDDI);
+	else
+		gic_write_dir(intid);
+}
+
 void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
+	u32 eoicount = FIELD_GET(ICH_HCR_EL2_EOIcount, cpuif->vgic_hcr);
+	struct vgic_irq *irq;
 
 	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
 
 	for (int lr = 0; lr < cpuif->used_lrs; lr++)
 		vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]);
 
+	/*
+	 * EOIMode=0: use EOIcount to emulate deactivation. We are
+	 * guaranteed to deactivate in reverse order of the activation, so
+	 * just pick one active interrupt after the other in the ap_list,
+	 * and replay the deactivation as if the CPU was doing it. We also
+	 * rely on priority drop to have taken place, and the list to be
+	 * sorted by priority.
+	 */
+	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
+		u64 lr;
+
+		/*
+		 * I would have loved to write this using a scoped_guard(),
+		 * but using 'continue' here is a total train wreck.
+		 */
+		if (!eoicount) {
+			break;
+		} else {
+			guard(raw_spinlock)(&irq->irq_lock);
+
+			if (!(likely(vgic_target_oracle(irq) == vcpu) &&
+			      irq->active))
+				continue;
+
+			lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT;
+		}
+
+		if (lr & ICH_LR_HW)
+			vgic_v3_deactivate_phys(FIELD_GET(ICH_LR_PHYS_ID_MASK, lr));
+
+		vgic_v3_fold_lr(vcpu, lr);
+		eoicount--;
+	}
+
 	cpuif->used_lrs = 0;
 }
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 27/45] KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (25 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 26/45] KVM: arm64: GICv3: Handle LR overflow when EOImode==0 Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 28/45] KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive Marc Zyngier
                   ` (18 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Deactivation via ICV_DIR_EL1 is both relatively straightforward
(we have the interrupt that needs deactivation) and really awkward.

The main issue is that the interrupt may either be in an LR on
another CPU, or ourside of any LR.

In the former case, we process the deactivation is if ot was
a write to GICD_CACTIVERn, which is already implemented as a big
hammer IPI'ing all vcpus. In the latter case, we just perform
a normal deactivation, similar to what we do for EOImode==0.

Another annoying aspect is that we need to tell the CPU owning
the interrupt that its ap_list needs laudering. We use a brand new
vcpu request to that effect.

Note that this doesn't address deactivation via the GICV MMIO view,
which will be taken care of in a later change.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/include/asm/kvm_host.h |  1 +
 arch/arm64/kvm/arm.c              |  4 ++
 arch/arm64/kvm/hyp/vgic-v3-sr.c   |  3 ++
 arch/arm64/kvm/sys_regs.c         | 19 ++++++-
 arch/arm64/kvm/vgic/vgic-v3.c     | 85 +++++++++++++++++++++++++++++++
 arch/arm64/kvm/vgic/vgic.c        | 11 ++++
 arch/arm64/kvm/vgic/vgic.h        |  1 +
 include/kvm/arm_vgic.h            |  1 +
 8 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 64302c438355c..7501a2ee4dd44 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -54,6 +54,7 @@
 #define KVM_REQ_NESTED_S2_UNMAP		KVM_ARCH_REQ(8)
 #define KVM_REQ_GUEST_HYP_IRQ_PENDING	KVM_ARCH_REQ(9)
 #define KVM_REQ_MAP_L1_VNCR_EL2		KVM_ARCH_REQ(10)
+#define KVM_REQ_VGIC_PROCESS_UPDATE	KVM_ARCH_REQ(11)
 
 #define KVM_DIRTY_LOG_MANUAL_CAPS   (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
 				     KVM_DIRTY_LOG_INITIALLY_SET)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 733195ef183e1..fe13f9777f9ca 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1041,6 +1041,10 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
 		 */
 		kvm_check_request(KVM_REQ_IRQ_PENDING, vcpu);
 
+		/* Process interrupts deactivated through a trap */
+		if (kvm_check_request(KVM_REQ_VGIC_PROCESS_UPDATE, vcpu))
+			kvm_vgic_process_async_update(vcpu);
+
 		if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu))
 			kvm_update_stolen_time(vcpu);
 
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 1e5c1cf4b9144..8d3f4c069ea63 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -1247,6 +1247,9 @@ int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
 	case SYS_ICC_DIR_EL1:
 		if (unlikely(is_read))
 			return 0;
+		/* Full exit if required to handle overflow deactivation... */
+		if (vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr & ICH_HCR_EL2_TDIR)
+			return 0;
 		fn = __vgic_v3_write_dir;
 		break;
 	case SYS_ICC_RPR_EL1:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index e67eb39ddc118..1b69d6e2d720d 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -666,6 +666,21 @@ static bool access_gic_sre(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+static bool access_gic_dir(struct kvm_vcpu *vcpu,
+			   struct sys_reg_params *p,
+			   const struct sys_reg_desc *r)
+{
+	if (!kvm_has_gicv3(vcpu->kvm))
+		return undef_access(vcpu, p, r);
+
+	if (!p->is_write)
+		return undef_access(vcpu, p, r);
+
+	vgic_v3_deactivate(vcpu, p->regval);
+
+	return true;
+}
+
 static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 			struct sys_reg_params *p,
 			const struct sys_reg_desc *r)
@@ -3370,7 +3385,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
 	{ SYS_DESC(SYS_ICC_AP1R1_EL1), undef_access },
 	{ SYS_DESC(SYS_ICC_AP1R2_EL1), undef_access },
 	{ SYS_DESC(SYS_ICC_AP1R3_EL1), undef_access },
-	{ SYS_DESC(SYS_ICC_DIR_EL1), undef_access },
+	{ SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir },
 	{ SYS_DESC(SYS_ICC_RPR_EL1), undef_access },
 	{ SYS_DESC(SYS_ICC_SGI1R_EL1), access_gic_sgi },
 	{ SYS_DESC(SYS_ICC_ASGI1R_EL1), access_gic_sgi },
@@ -4495,7 +4510,7 @@ static const struct sys_reg_desc cp15_regs[] = {
 	{ CP15_SYS_DESC(SYS_ICC_AP1R1_EL1), undef_access },
 	{ CP15_SYS_DESC(SYS_ICC_AP1R2_EL1), undef_access },
 	{ CP15_SYS_DESC(SYS_ICC_AP1R3_EL1), undef_access },
-	{ CP15_SYS_DESC(SYS_ICC_DIR_EL1), undef_access },
+	{ CP15_SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir },
 	{ CP15_SYS_DESC(SYS_ICC_RPR_EL1), undef_access },
 	{ CP15_SYS_DESC(SYS_ICC_IAR1_EL1), undef_access },
 	{ CP15_SYS_DESC(SYS_ICC_EOIR1_EL1), undef_access },
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index e17eefc6a2bb6..f2e9b96c6b65c 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -12,6 +12,7 @@
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_asm.h>
 
+#include "vgic-mmio.h"
 #include "vgic.h"
 
 static bool group0_trap;
@@ -168,6 +169,90 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 	cpuif->used_lrs = 0;
 }
 
+void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
+	struct kvm_vcpu *target_vcpu = NULL;
+	struct vgic_irq *irq;
+	unsigned long flags;
+	bool mmio = false;
+	u64 lr = 0;
+
+	/*
+	 * We only deal with DIR when EOIMode==1, and only for SGI,
+	 * PPI or SPI.
+	 */
+	if (!(cpuif->vgic_vmcr & ICH_VMCR_EOIM_MASK) ||
+	    val >= vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)
+		return;
+
+	/* Make sure we're in the same context as LR handling */
+	local_irq_save(flags);
+
+	irq = vgic_get_vcpu_irq(vcpu, val);
+	if (WARN_ON_ONCE(!irq))
+		goto out;
+
+	/*
+	 * EOIMode=1: we must rely on traps to handle deactivate of
+	 * overflowing interrupts, as there is no ordering guarantee and
+	 * EOIcount isn't being incremented. Priority drop will have taken
+	 * place, as ICV_EOIxR_EL1 only affects the APRs and not the LRs.
+	 *
+	 * Three possibities:
+	 *
+	 * - The irq is not queued on any CPU, and there is nothing to
+	 *   do,
+	 *
+	 * - Or the irq is in an LR, meaning that its state is not
+	 *   directly observable. Treat it bluntly by making it as if
+	 *   this was a write to GICD_ICACTIVER, which will force an
+	 *   exit on all vcpus. If it hurts, don't do that.
+	 *
+	 * - Or the irq is active, but not in an LR, and we can
+	 *   directly deactivate it by building a pseudo-LR, fold it,
+	 *   and queue a request to prune the resulting ap_list,
+	 */
+	scoped_guard(raw_spinlock, &irq->irq_lock) {
+		target_vcpu = irq->vcpu;
+
+		/* Not on any ap_list? */
+		if (!target_vcpu)
+			goto put;
+
+		/*
+		 * Urgh. We're deactivating something that we cannot
+		 * observe yet... Big hammer time.
+		 */
+		if (irq->on_lr) {
+			mmio = true;
+			goto put;
+		}
+
+		/* (with a Dalek voice) DEACTIVATE!!!! */
+		lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT;
+	}
+
+	if (lr & ICH_LR_HW)
+		vgic_v3_deactivate_phys(FIELD_GET(ICH_LR_PHYS_ID_MASK, lr));
+
+	vgic_v3_fold_lr(vcpu, lr);
+
+put:
+	vgic_put_irq(vcpu->kvm, irq);
+
+out:
+	local_irq_restore(flags);
+
+	if (mmio)
+		vgic_mmio_write_cactive(vcpu, (val / 32) * 4, 4, BIT(val % 32));
+
+	/* Force the ap_list to be pruned */
+	if (target_vcpu)
+		kvm_make_request(KVM_REQ_VGIC_PROCESS_UPDATE, target_vcpu);
+}
+
 /* Requires the irq to be locked already */
 static u64 vgic_v3_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
 {
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 5c9204d18b27d..8c502135199f8 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -990,6 +990,17 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
 	vgic_prune_ap_list(vcpu);
 }
 
+/* Sync interrupts that were deactivated through a DIR trap */
+void kvm_vgic_process_async_update(struct kvm_vcpu *vcpu)
+{
+	unsigned long flags;
+
+	/* Make sure we're in the same context as LR handling */
+	local_irq_save(flags);
+	vgic_prune_ap_list(vcpu);
+	local_irq_restore(flags);
+}
+
 static inline void vgic_restore_state(struct kvm_vcpu *vcpu)
 {
 	if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 037efb6200823..01ff6d4aa9dad 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -318,6 +318,7 @@ static inline void vgic_get_irq_ref(struct vgic_irq *irq)
 void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);
 void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
 void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr);
+void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val);
 void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
 void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
 void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index ec349c5a4a8b6..b798546755a34 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -421,6 +421,7 @@ bool kvm_vcpu_has_pending_irqs(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid);
+void kvm_vgic_process_async_update(struct kvm_vcpu *vcpu);
 
 void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1);
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 28/45] KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (26 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 27/45] KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity Marc Zyngier
                   ` (17 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

The GICv2 SGIs require additional handling for deactivation, as they
are effectively multiple interrrupts muxed into one. Make sure we
check for the source CPU when deactivating.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index f2e9b96c6b65c..1026031f22ff9 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -173,11 +173,20 @@ void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
+	u32 model = vcpu->kvm->arch.vgic.vgic_model;
 	struct kvm_vcpu *target_vcpu = NULL;
+	bool mmio = false, is_v2_sgi;
 	struct vgic_irq *irq;
 	unsigned long flags;
-	bool mmio = false;
 	u64 lr = 0;
+	u8 cpuid;
+
+	/* Snapshot CPUID, and remove it from the INTID */
+	cpuid = FIELD_GET(GENMASK_ULL(12, 10), val);
+	val &= ~GENMASK_ULL(12, 10);
+
+	is_v2_sgi = (model == KVM_DEV_TYPE_ARM_VGIC_V2 &&
+		     val < VGIC_NR_SGIS);
 
 	/*
 	 * We only deal with DIR when EOIMode==1, and only for SGI,
@@ -213,6 +222,9 @@ void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
 	 * - Or the irq is active, but not in an LR, and we can
 	 *   directly deactivate it by building a pseudo-LR, fold it,
 	 *   and queue a request to prune the resulting ap_list,
+	 *
+	 * Special care must be taken to match the source CPUID when
+	 * deactivating a GICv2 SGI.
 	 */
 	scoped_guard(raw_spinlock, &irq->irq_lock) {
 		target_vcpu = irq->vcpu;
@@ -230,6 +242,12 @@ void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
 			goto put;
 		}
 
+		/* GICv2 SGI: check that the cpuid matches */
+		if (is_v2_sgi && irq->active_source != cpuid) {
+			target_vcpu = NULL;
+			goto put;
+		}
+
 		/* (with a Dalek voice) DEACTIVATE!!!! */
 		lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT;
 	}
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (27 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 28/45] KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-14 14:20   ` Fuad Tabba
  2025-11-09 17:16 ` [PATCH v2 30/45] KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation Marc Zyngier
                   ` (16 subsequent siblings)
  45 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Now that we are ready to handle deactivation through ICV_DIR_EL1,
set the trap bit if we have active interrupts outside of the LRs.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 1026031f22ff9..26e17ed057f00 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
 		ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
 	cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
 		ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
+
+	/*
+	 * Note that we set the trap irrespective of EOIMode, as that
+	 * can change behind our back without any warning...
+	 */
+	if (irqs_active_outside_lrs(als))
+		cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
 }
 
 static bool lr_signals_eoi_mi(u64 lr_val)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 30/45] KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (28 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 31/45] KVM: arm64: GICv3: Handle in-LR deactivation when possible Marc Zyngier
                   ` (15 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

SPIs are specially annpying, as they can be activated on a CPU and
deactivated on another. WHich means that when an SPI is in flight
anywhere, all CPUs need to have their TDIR trap bit set.

This translates into broadcasting an IPI across all CPUs to make sure
they set their trap bit, The number of in-flight SPIs is kept in
an atomic variable so that CPUs can turn the trap bit off as soon
as possible.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-init.c |  1 +
 arch/arm64/kvm/vgic/vgic-v3.c   | 21 +++++++++++++++------
 arch/arm64/kvm/vgic/vgic.c      | 25 +++++++++++++++++++++++--
 include/kvm/arm_vgic.h          |  3 +++
 4 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 6d5e5d708f23a..52de99c0f01ce 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -188,6 +188,7 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
 	struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
 	int i;
 
+	dist->active_spis = (atomic_t)ATOMIC_INIT(0);
 	dist->spis = kcalloc(nr_spis, sizeof(struct vgic_irq), GFP_KERNEL_ACCOUNT);
 	if (!dist->spis)
 		return  -ENOMEM;
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 26e17ed057f00..bf4dde158d516 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -44,10 +44,17 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
 		ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
 
 	/*
+	 * Dealing with EOImode=1 is a massive source of headache. Not
+	 * only do we need to track that we have active interrupts
+	 * outside of the LRs and force DIR to be trapped, we also
+	 * need to deal with SPIs that can be deactivated on another
+	 * CPU.
+	 *
 	 * Note that we set the trap irrespective of EOIMode, as that
 	 * can change behind our back without any warning...
 	 */
-	if (irqs_active_outside_lrs(als))
+	if (irqs_active_outside_lrs(als)		     ||
+	    atomic_read(&vcpu->kvm->arch.vgic.active_spis))
 		cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
 }
 
@@ -75,11 +82,6 @@ static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
 	if (!irq)	/* An LPI could have been unmapped. */
 		return;
 
-	/* Notify fds when the guest EOI'ed a level-triggered IRQ */
-	if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
-		kvm_notify_acked_irq(vcpu->kvm, 0,
-				     intid - VGIC_NR_PRIVATE_IRQS);
-
 	scoped_guard(raw_spinlock, &irq->irq_lock) {
 		/* Always preserve the active bit for !LPIs, note deactivation */
 		if (irq->intid >= VGIC_MIN_LPI)
@@ -114,6 +116,13 @@ static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
 		irq->on_lr = false;
 	}
 
+	/* Notify fds when the guest EOI'ed a level-triggered SPI, and drop the refcount */
+	if (deactivated && lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid)) {
+		kvm_notify_acked_irq(vcpu->kvm, 0,
+				     intid - VGIC_NR_PRIVATE_IRQS);
+		atomic_dec_if_positive(&vcpu->kvm->arch.vgic.active_spis);
+	}
+
 	vgic_put_irq(vcpu->kvm, irq);
 }
 
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 8c502135199f8..60bdddf3472e7 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -367,6 +367,17 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level, void *owne
 	return false;
 }
 
+static bool vgic_model_needs_bcst_kick(struct kvm *kvm)
+{
+	/*
+	 * A GICv3 (or GICv3-like) system exposing a GICv3 to the
+	 * guest needs a broadcast kick to set TDIR globally, even if
+	 * the bit doesn't really exist (we still need to check for
+	 * the shadow bit in the DIR emulation fast-path).
+	 */
+	return (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3);
+}
+
 /*
  * Check whether an IRQ needs to (and can) be queued to a VCPU's ap list.
  * Do the queuing if necessary, taking the right locks in the right order.
@@ -379,6 +390,7 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
 			   unsigned long flags) __releases(&irq->irq_lock)
 {
 	struct kvm_vcpu *vcpu;
+	bool bcast;
 
 	lockdep_assert_held(&irq->irq_lock);
 
@@ -453,11 +465,20 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
 	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
 	irq->vcpu = vcpu;
 
+	/* A new SPI may result in deactivation trapping on all vcpus */
+	bcast = (vgic_model_needs_bcst_kick(vcpu->kvm) &&
+		 vgic_valid_spi(vcpu->kvm, irq->intid) &&
+		 atomic_fetch_inc(&vcpu->kvm->arch.vgic.active_spis) == 0);
+
 	raw_spin_unlock(&irq->irq_lock);
 	raw_spin_unlock_irqrestore(&vcpu->arch.vgic_cpu.ap_list_lock, flags);
 
-	kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
-	kvm_vcpu_kick(vcpu);
+	if (!bcast) {
+		kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
+		kvm_vcpu_kick(vcpu);
+	} else {
+		kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_IRQ_PENDING);
+	}
 
 	return true;
 }
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index b798546755a34..6a4d3d2055966 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -263,6 +263,9 @@ struct vgic_dist {
 	/* The GIC maintenance IRQ for nested hypervisors. */
 	u32			mi_intid;
 
+	/* Track the number of in-flight active SPIs */
+	atomic_t		active_spis;
+
 	/* base addresses in guest physical address space: */
 	gpa_t			vgic_dist_base;		/* distributor */
 	union {
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 31/45] KVM: arm64: GICv3: Handle in-LR deactivation when possible
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (29 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 30/45] KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 32/45] KVM: arm64: GICv3: Avoid broadcast kick on CPUs lacking TDIR Marc Zyngier
                   ` (14 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Even when we have either an LR overflow or SPIs in flight, it is
extremely likely that the interrupt being deactivated is still in
the LRs, and that going all the way back to the the generic trap
handling code is a waste of time.

Instead, try and deactivate in place when possible, and only if
this fails, perform a full exit.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/hyp/vgic-v3-sr.c | 38 ++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index 8d3f4c069ea63..e950efa225478 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -792,7 +792,7 @@ static void __vgic_v3_bump_eoicount(void)
 	write_gicreg(hcr, ICH_HCR_EL2);
 }
 
-static void __vgic_v3_write_dir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
+static int ___vgic_v3_write_dir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
 {
 	u32 vid = vcpu_get_reg(vcpu, rt);
 	u64 lr_val;
@@ -800,19 +800,25 @@ static void __vgic_v3_write_dir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
 
 	/* EOImode == 0, nothing to be done here */
 	if (!(vmcr & ICH_VMCR_EOIM_MASK))
-		return;
+		return 1;
 
 	/* No deactivate to be performed on an LPI */
 	if (vid >= VGIC_MIN_LPI)
-		return;
+		return 1;
 
 	lr = __vgic_v3_find_active_lr(vcpu, vid, &lr_val);
-	if (lr == -1) {
-		__vgic_v3_bump_eoicount();
-		return;
+	if (lr != -1) {
+		__vgic_v3_clear_active_lr(lr, lr_val);
+		return 1;
 	}
 
-	__vgic_v3_clear_active_lr(lr, lr_val);
+	return 0;
+}
+
+static void __vgic_v3_write_dir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
+{
+	if (!___vgic_v3_write_dir(vcpu, vmcr, rt))
+		__vgic_v3_bump_eoicount();
 }
 
 static void __vgic_v3_write_eoir(struct kvm_vcpu *vcpu, u32 vmcr, int rt)
@@ -1247,9 +1253,21 @@ int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu)
 	case SYS_ICC_DIR_EL1:
 		if (unlikely(is_read))
 			return 0;
-		/* Full exit if required to handle overflow deactivation... */
-		if (vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr & ICH_HCR_EL2_TDIR)
-			return 0;
+		/*
+		 * Full exit if required to handle overflow deactivation,
+		 * unless we can emulate it in the LRs (likely the majority
+		 * of the cases).
+		 */
+		if (vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr & ICH_HCR_EL2_TDIR) {
+			int ret;
+
+			ret = ___vgic_v3_write_dir(vcpu, __vgic_v3_read_vmcr(),
+						   kvm_vcpu_sys_get_rt(vcpu));
+			if (ret)
+				__kvm_skip_instr(vcpu);
+
+			return ret;
+		}
 		fn = __vgic_v3_write_dir;
 		break;
 	case SYS_ICC_RPR_EL1:
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 32/45] KVM: arm64: GICv3: Avoid broadcast kick on CPUs lacking TDIR
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (30 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 31/45] KVM: arm64: GICv3: Handle in-LR deactivation when possible Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 33/45] KVM: arm64: GICv2: Handle LR overflow when EOImode==0 Marc Zyngier
                   ` (13 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

CPUs lacking TDIR always trap ICV_DIR_EL1, no matter what, since
we have ICH_HCR_EL2.TC set permanently. For these CPUs, it is
useless to use a broadcast kick on SPI injection, as the sole
purpose of this is to set TDIR.

We can therefore skip this on these CPUs, which are challenged
enough not to be burdened by extra IPIs. As a consequence,
permanently set the TDIR bit in the shadow state to notify the
fast-path emulation code of the exit reason.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v3.c |  6 +++++-
 arch/arm64/kvm/vgic/vgic.c    | 13 ++++++++-----
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index bf4dde158d516..32654ea51418b 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -50,10 +50,14 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
 	 * need to deal with SPIs that can be deactivated on another
 	 * CPU.
 	 *
+	 * On systems that do not implement TDIR, force the bit in the
+	 * shadow state anyway to avoid IPI-ing on these poor sods.
+	 *
 	 * Note that we set the trap irrespective of EOIMode, as that
 	 * can change behind our back without any warning...
 	 */
-	if (irqs_active_outside_lrs(als)		     ||
+	if (!cpus_have_final_cap(ARM64_HAS_ICH_HCR_EL2_TDIR) ||
+	    irqs_active_outside_lrs(als)		     ||
 	    atomic_read(&vcpu->kvm->arch.vgic.active_spis))
 		cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
 }
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 60bdddf3472e7..a4c42884f1e16 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -370,12 +370,15 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level, void *owne
 static bool vgic_model_needs_bcst_kick(struct kvm *kvm)
 {
 	/*
-	 * A GICv3 (or GICv3-like) system exposing a GICv3 to the
-	 * guest needs a broadcast kick to set TDIR globally, even if
-	 * the bit doesn't really exist (we still need to check for
-	 * the shadow bit in the DIR emulation fast-path).
+	 * A GICv3 (or GICv3-like) system exposing a GICv3 to the guest
+	 * needs a broadcast kick to set TDIR globally.
+	 *
+	 * For systems that do not have TDIR (ARM's own v8.0 CPUs), the
+	 * shadow TDIR bit is always set, and so is the register's TC bit,
+	 * so no need to kick the CPUs.
 	 */
-	return (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3);
+	return (cpus_have_final_cap(ARM64_HAS_ICH_HCR_EL2_TDIR) &&
+		kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3);
 }
 
 /*
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 33/45] KVM: arm64: GICv2: Handle LR overflow when EOImode==0
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (31 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 32/45] KVM: arm64: GICv3: Avoid broadcast kick on CPUs lacking TDIR Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 34/45] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps Marc Zyngier
                   ` (12 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Similarly to the GICv3 version, handle the EOIcount-driven deactivation
by walking the overflow list.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-v2.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 9a2de03f74c30..bbd4d003fde86 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -100,6 +100,8 @@ static void vgic_v2_fold_lr(struct kvm_vcpu *vcpu, u32 val)
 	vgic_put_irq(vcpu->kvm, irq);
 }
 
+static u32 vgic_v2_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq);
+
 /*
  * transfer the content of the LRs back into the corresponding ap_list:
  * - active bit is transferred as is
@@ -111,12 +113,37 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 {
 	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 	struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
+	u32 eoicount = FIELD_GET(GICH_HCR_EOICOUNT, cpuif->vgic_hcr);
+	struct vgic_irq *irq;
 
 	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
 
 	for (int lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++)
 		vgic_v2_fold_lr(vcpu, cpuif->vgic_lr[lr]);
 
+	/* See the GICv3 equivalent for the EOIcount handling rationale */
+	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
+		u32 lr;
+
+		if (!eoicount) {
+			break;
+		} else {
+			guard(raw_spinlock)(&irq->irq_lock);
+
+			if (!(likely(vgic_target_oracle(irq) == vcpu) &&
+			      irq->active))
+				continue;
+
+			lr = vgic_v2_compute_lr(vcpu, irq) & ~GICH_LR_ACTIVE_BIT;
+		}
+
+		if (lr & GICH_LR_HW)
+			writel_relaxed(FIELD_GET(GICH_LR_PHYSID_CPUID, lr),
+				       kvm_vgic_global_state.gicc_base + GIC_CPU_DEACTIVATE);
+		vgic_v2_fold_lr(vcpu, lr);
+		eoicount--;
+	}
+
 	cpuif->used_lrs = 0;
 }
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 34/45] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (32 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 33/45] KVM: arm64: GICv2: Handle LR overflow when EOImode==0 Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 35/45] KVM: arm64: GICv2: Always trap GICV_DIR register Marc Zyngier
                   ` (11 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Add the plumbing of GICv2 interrupt deactivation via GICV_DIR.
This requires adding a new device so that we can easily decode
the DIR address.

The deactivation itself is very similar to the GICv3 version.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/vgic/vgic-mmio-v2.c | 24 +++++++++
 arch/arm64/kvm/vgic/vgic-mmio.h    |  1 +
 arch/arm64/kvm/vgic/vgic-v2.c      | 85 ++++++++++++++++++++++++++++++
 arch/arm64/kvm/vgic/vgic.h         |  1 +
 include/kvm/arm_vgic.h             |  1 +
 5 files changed, 112 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v2.c b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
index f25fccb1f8e63..406845b3117cf 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
@@ -359,6 +359,16 @@ static void vgic_mmio_write_vcpuif(struct kvm_vcpu *vcpu,
 	vgic_set_vmcr(vcpu, &vmcr);
 }
 
+static void vgic_mmio_write_dir(struct kvm_vcpu *vcpu,
+				gpa_t addr, unsigned int len,
+				unsigned long val)
+{
+	if (kvm_vgic_global_state.type == VGIC_V2)
+		vgic_v2_deactivate(vcpu, val);
+	else
+		vgic_v3_deactivate(vcpu, val);
+}
+
 static unsigned long vgic_mmio_read_apr(struct kvm_vcpu *vcpu,
 					gpa_t addr, unsigned int len)
 {
@@ -482,6 +492,10 @@ static const struct vgic_register_region vgic_v2_cpu_registers[] = {
 	REGISTER_DESC_WITH_LENGTH(GIC_CPU_IDENT,
 		vgic_mmio_read_vcpuif, vgic_mmio_write_vcpuif, 4,
 		VGIC_ACCESS_32bit),
+	REGISTER_DESC_WITH_LENGTH_UACCESS(GIC_CPU_DEACTIVATE,
+		vgic_mmio_read_raz, vgic_mmio_write_dir,
+		vgic_mmio_read_raz, vgic_mmio_uaccess_write_wi,
+		4, VGIC_ACCESS_32bit),
 };
 
 unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
@@ -494,6 +508,16 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev)
 	return SZ_4K;
 }
 
+unsigned int vgic_v2_init_cpuif_iodev(struct vgic_io_device *dev)
+{
+	dev->regions = vgic_v2_cpu_registers;
+	dev->nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers);
+
+	kvm_iodevice_init(&dev->dev, &kvm_io_gic_ops);
+
+	return KVM_VGIC_V2_CPU_SIZE;
+}
+
 int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr)
 {
 	const struct vgic_register_region *region;
diff --git a/arch/arm64/kvm/vgic/vgic-mmio.h b/arch/arm64/kvm/vgic/vgic-mmio.h
index 5b490a4dfa5e9..50dc80220b0f3 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio.h
+++ b/arch/arm64/kvm/vgic/vgic-mmio.h
@@ -213,6 +213,7 @@ void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid,
 				    const u32 val);
 
 unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
+unsigned int vgic_v2_init_cpuif_iodev(struct vgic_io_device *dev);
 
 unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
 
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index bbd4d003fde86..bc52d44a573d5 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -9,6 +9,7 @@
 #include <kvm/arm_vgic.h>
 #include <asm/kvm_mmu.h>
 
+#include "vgic-mmio.h"
 #include "vgic.h"
 
 static inline void vgic_v2_write_lr(int lr, u32 val)
@@ -147,6 +148,79 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 	cpuif->used_lrs = 0;
 }
 
+void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	struct vgic_v2_cpu_if *cpuif = &vgic_cpu->vgic_v2;
+	struct kvm_vcpu *target_vcpu = NULL;
+	bool mmio = false;
+	struct vgic_irq *irq;
+	unsigned long flags;
+	u64 lr = 0;
+	u8 cpuid;
+
+	/* Snapshot CPUID, and remove it from the INTID */
+	cpuid = FIELD_GET(GENMASK_ULL(12, 10), val);
+	val &= ~GENMASK_ULL(12, 10);
+
+	/* We only deal with DIR when EOIMode==1 */
+	if (!(cpuif->vgic_vmcr & GICH_VMCR_EOI_MODE_MASK))
+		return;
+
+	/* Make sure we're in the same context as LR handling */
+	local_irq_save(flags);
+
+	irq = vgic_get_vcpu_irq(vcpu, val);
+	if (WARN_ON_ONCE(!irq))
+		goto out;
+
+	/* See the corresponding v3 code for the rationale */
+	scoped_guard(raw_spinlock, &irq->irq_lock) {
+		target_vcpu = irq->vcpu;
+
+		/* Not on any ap_list? */
+		if (!target_vcpu)
+			goto put;
+
+		/*
+		 * Urgh. We're deactivating something that we cannot
+		 * observe yet... Big hammer time.
+		 */
+		if (irq->on_lr) {
+			mmio = true;
+			goto put;
+		}
+
+		/* SGI: check that the cpuid matches */
+		if (val < VGIC_NR_SGIS && irq->active_source != cpuid) {
+			target_vcpu = NULL;
+			goto put;
+		}
+
+		/* (with a Dalek voice) DEACTIVATE!!!! */
+		lr = vgic_v2_compute_lr(vcpu, irq) & ~GICH_LR_ACTIVE_BIT;
+	}
+
+	if (lr & GICH_LR_HW)
+		writel_relaxed(FIELD_GET(GICH_LR_PHYSID_CPUID, lr),
+			       kvm_vgic_global_state.gicc_base + GIC_CPU_DEACTIVATE);
+
+	vgic_v2_fold_lr(vcpu, lr);
+
+put:
+	vgic_put_irq(vcpu->kvm, irq);
+
+out:
+	local_irq_restore(flags);
+
+	if (mmio)
+		vgic_mmio_write_cactive(vcpu, (val / 32) * 4, 4, BIT(val % 32));
+
+	/* Force the ap_list to be pruned */
+	if (target_vcpu)
+		kvm_make_request(KVM_REQ_VGIC_PROCESS_UPDATE, target_vcpu);
+}
+
 static u32 vgic_v2_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
 {
 	u32 val = irq->intid;
@@ -346,6 +420,7 @@ static bool vgic_v2_check_base(gpa_t dist_base, gpa_t cpu_base)
 int vgic_v2_map_resources(struct kvm *kvm)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
+	unsigned int len;
 	int ret = 0;
 
 	if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
@@ -369,6 +444,16 @@ int vgic_v2_map_resources(struct kvm *kvm)
 		return ret;
 	}
 
+	len = vgic_v2_init_cpuif_iodev(&dist->cpuif_iodev);
+	dist->cpuif_iodev.base_addr = dist->vgic_cpu_base;
+	dist->cpuif_iodev.iodev_type = IODEV_CPUIF;
+	dist->cpuif_iodev.redist_vcpu = NULL;
+
+	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, dist->vgic_cpu_base,
+				      len, &dist->cpuif_iodev.dev);
+	if (ret)
+		return ret;
+
 	if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
 		ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
 					    kvm_vgic_global_state.vcpu_base,
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 01ff6d4aa9dad..ec3a61e8e6b30 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -277,6 +277,7 @@ int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr,
 
 void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
 void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
+void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val);
 void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr);
 void vgic_v2_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als);
 int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr);
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 6a4d3d2055966..b261fb3968d03 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -287,6 +287,7 @@ struct vgic_dist {
 	struct vgic_irq		*spis;
 
 	struct vgic_io_device	dist_iodev;
+	struct vgic_io_device	cpuif_iodev;
 
 	bool			has_its;
 	bool			table_write_in_progress;
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 35/45] KVM: arm64: GICv2: Always trap GICV_DIR register
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (33 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 34/45] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 36/45] KVM: arm64: selftests: gic_v3: Add irq group setting helper Marc Zyngier
                   ` (10 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Since we can't decide to trap the DIR register on a per-vcpu basis,
always trap the second page of the GIC CPU interface. Yes, this is
costly. On the bright side, no sane SW should use EOImode==1 on
GICv2...

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c | 4 ++++
 arch/arm64/kvm/vgic/vgic-v2.c            | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c b/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c
index 78579b31a4205..5fd99763b54de 100644
--- a/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c
+++ b/arch/arm64/kvm/hyp/vgic-v2-cpuif-proxy.c
@@ -63,6 +63,10 @@ int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
 		return -1;
 	}
 
+	/* Handle deactivation as a normal exit */
+	if ((fault_ipa - vgic->vgic_cpu_base) >= GIC_CPU_DEACTIVATE)
+		return 0;
+
 	rd = kvm_vcpu_dabt_get_rd(vcpu);
 	addr  = kvm_vgic_global_state.vcpu_hyp_va;
 	addr += fault_ipa - vgic->vgic_cpu_base;
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index bc52d44a573d5..585491fbda807 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -457,7 +457,7 @@ int vgic_v2_map_resources(struct kvm *kvm)
 	if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
 		ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
 					    kvm_vgic_global_state.vcpu_base,
-					    KVM_VGIC_V2_CPU_SIZE, true);
+					    KVM_VGIC_V2_CPU_SIZE - SZ_4K, true);
 		if (ret) {
 			kvm_err("Unable to remap VGIC CPU to VCPU\n");
 			return ret;
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 36/45] KVM: arm64: selftests: gic_v3: Add irq group setting helper
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (34 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 35/45] KVM: arm64: GICv2: Always trap GICV_DIR register Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 37/45] KVM: arm64: selftests: gic_v3: Disable Group-0 interrupts by default Marc Zyngier
                   ` (9 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Being able to set the group of an interrupt is pretty useful.
Add such a helper.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/include/arm64/gic.h   |  1 +
 tools/testing/selftests/kvm/lib/arm64/gic.c       |  6 ++++++
 .../testing/selftests/kvm/lib/arm64/gic_private.h |  1 +
 tools/testing/selftests/kvm/lib/arm64/gic_v3.c    | 15 +++++++++++++++
 4 files changed, 23 insertions(+)

diff --git a/tools/testing/selftests/kvm/include/arm64/gic.h b/tools/testing/selftests/kvm/include/arm64/gic.h
index baeb3c859389d..cc7a7f34ed377 100644
--- a/tools/testing/selftests/kvm/include/arm64/gic.h
+++ b/tools/testing/selftests/kvm/include/arm64/gic.h
@@ -57,6 +57,7 @@ void gic_irq_set_pending(unsigned int intid);
 void gic_irq_clear_pending(unsigned int intid);
 bool gic_irq_get_pending(unsigned int intid);
 void gic_irq_set_config(unsigned int intid, bool is_edge);
+void gic_irq_set_group(unsigned int intid, bool group);
 
 void gic_rdist_enable_lpis(vm_paddr_t cfg_table, size_t cfg_table_size,
 			   vm_paddr_t pend_table);
diff --git a/tools/testing/selftests/kvm/lib/arm64/gic.c b/tools/testing/selftests/kvm/lib/arm64/gic.c
index 7abbf8866512a..b023868fe0b82 100644
--- a/tools/testing/selftests/kvm/lib/arm64/gic.c
+++ b/tools/testing/selftests/kvm/lib/arm64/gic.c
@@ -155,3 +155,9 @@ void gic_irq_set_config(unsigned int intid, bool is_edge)
 	GUEST_ASSERT(gic_common_ops);
 	gic_common_ops->gic_irq_set_config(intid, is_edge);
 }
+
+void gic_irq_set_group(unsigned int intid, bool group)
+{
+	GUEST_ASSERT(gic_common_ops);
+	gic_common_ops->gic_irq_set_group(intid, group);
+}
diff --git a/tools/testing/selftests/kvm/lib/arm64/gic_private.h b/tools/testing/selftests/kvm/lib/arm64/gic_private.h
index d24e9ecc96c6d..b6a7e30c3eb1f 100644
--- a/tools/testing/selftests/kvm/lib/arm64/gic_private.h
+++ b/tools/testing/selftests/kvm/lib/arm64/gic_private.h
@@ -25,6 +25,7 @@ struct gic_common_ops {
 	void (*gic_irq_clear_pending)(uint32_t intid);
 	bool (*gic_irq_get_pending)(uint32_t intid);
 	void (*gic_irq_set_config)(uint32_t intid, bool is_edge);
+	void (*gic_irq_set_group)(uint32_t intid, bool group);
 };
 
 extern const struct gic_common_ops gicv3_ops;
diff --git a/tools/testing/selftests/kvm/lib/arm64/gic_v3.c b/tools/testing/selftests/kvm/lib/arm64/gic_v3.c
index 66d05506f78b1..3e4e1a6a4f7c3 100644
--- a/tools/testing/selftests/kvm/lib/arm64/gic_v3.c
+++ b/tools/testing/selftests/kvm/lib/arm64/gic_v3.c
@@ -293,6 +293,20 @@ static void gicv3_enable_redist(volatile void *redist_base)
 	}
 }
 
+static void gicv3_set_group(uint32_t intid, bool grp)
+{
+	uint32_t cpu_or_dist;
+	uint32_t val;
+
+	cpu_or_dist = (get_intid_range(intid) == SPI_RANGE) ? DIST_BIT : guest_get_vcpuid();
+	val = gicv3_reg_readl(cpu_or_dist, GICD_IGROUPR + (intid / 32) * 4);
+	if (grp)
+		val |= BIT(intid % 32);
+	else
+		val &= ~BIT(intid % 32);
+	gicv3_reg_writel(cpu_or_dist, GICD_IGROUPR + (intid / 32) * 4, val);
+}
+
 static void gicv3_cpu_init(unsigned int cpu)
 {
 	volatile void *sgi_base;
@@ -400,6 +414,7 @@ const struct gic_common_ops gicv3_ops = {
 	.gic_irq_clear_pending = gicv3_irq_clear_pending,
 	.gic_irq_get_pending = gicv3_irq_get_pending,
 	.gic_irq_set_config = gicv3_irq_set_config,
+	.gic_irq_set_group = gicv3_set_group,
 };
 
 void gic_rdist_enable_lpis(vm_paddr_t cfg_table, size_t cfg_table_size,
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 37/45] KVM: arm64: selftests: gic_v3: Disable Group-0 interrupts by default
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (35 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 36/45] KVM: arm64: selftests: gic_v3: Add irq group setting helper Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 38/45] KVM: arm64: selftests: vgic_irq: Fix GUEST_ASSERT_IAR_EMPTY() helper Marc Zyngier
                   ` (8 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Make sure G0 is disabled at the point of initialising the GIC.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/lib/arm64/gic_v3.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/testing/selftests/kvm/lib/arm64/gic_v3.c b/tools/testing/selftests/kvm/lib/arm64/gic_v3.c
index 3e4e1a6a4f7c3..5b0fd95c6b48a 100644
--- a/tools/testing/selftests/kvm/lib/arm64/gic_v3.c
+++ b/tools/testing/selftests/kvm/lib/arm64/gic_v3.c
@@ -342,6 +342,8 @@ static void gicv3_cpu_init(unsigned int cpu)
 	/* Set a default priority threshold */
 	write_sysreg_s(ICC_PMR_DEF_PRIO, SYS_ICC_PMR_EL1);
 
+	/* Disable Group-0 interrupts */
+	write_sysreg_s(ICC_IGRPEN0_EL1_MASK, SYS_ICC_IGRPEN1_EL1);
 	/* Enable non-secure Group-1 interrupts */
 	write_sysreg_s(ICC_IGRPEN1_EL1_MASK, SYS_ICC_IGRPEN1_EL1);
 }
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 38/45] KVM: arm64: selftests: vgic_irq: Fix GUEST_ASSERT_IAR_EMPTY() helper
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (36 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 37/45] KVM: arm64: selftests: gic_v3: Disable Group-0 interrupts by default Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 39/45] KVM: arm64: selftests: vgic_irq: Change configuration before enabling interrupt Marc Zyngier
                   ` (7 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

No, 0 is not a spurious INTID. Never been, never was.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index 6338f5bbdb705..a77562b2976ae 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -205,7 +205,7 @@ static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid,
 do { 										\
 	uint32_t _intid;							\
 	_intid = gic_get_and_ack_irq();						\
-	GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS);			\
+	GUEST_ASSERT(_intid == IAR_SPURIOUS);					\
 } while (0)
 
 #define CAT_HELPER(a, b) a ## b
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 39/45] KVM: arm64: selftests: vgic_irq: Change configuration before enabling interrupt
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (37 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 38/45] KVM: arm64: selftests: vgic_irq: Fix GUEST_ASSERT_IAR_EMPTY() helper Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 40/45] KVM: arm64: selftests: vgic_irq: Exclude timer-controlled interrupts Marc Zyngier
                   ` (6 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

The architecture is pretty clear that changing the configuration of
an enable interrupt is not OK. It doesn't really matter here, but
doing the right thing is not more expensive.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index a77562b2976ae..a8919ef3cea2e 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -473,12 +473,12 @@ static void guest_code(struct test_args *args)
 
 	gic_init(GIC_V3, 1);
 
-	for (i = 0; i < nr_irqs; i++)
-		gic_irq_enable(i);
-
 	for (i = MIN_SPI; i < nr_irqs; i++)
 		gic_irq_set_config(i, !level_sensitive);
 
+	for (i = 0; i < nr_irqs; i++)
+		gic_irq_enable(i);
+
 	gic_set_eoi_split(args->eoi_split);
 
 	reset_priorities(args);
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 40/45] KVM: arm64: selftests: vgic_irq: Exclude timer-controlled interrupts
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (38 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 39/45] KVM: arm64: selftests: vgic_irq: Change configuration before enabling interrupt Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 41/45] KVM: arm64: selftests: vgic_irq: Remove LR-bound limitation Marc Zyngier
                   ` (5 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

The PPI injection API is clear that you can't inject the timer PPIs
from userspace, since they are controlled by the timers themselves.

Add an exclusion list for this purpose.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 31 ++++++++++++++++----
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index a8919ef3cea2e..b0415bdb89524 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -359,8 +359,9 @@ static uint32_t wait_for_and_activate_irq(void)
  * interrupts for the whole test.
  */
 static void test_inject_preemption(struct test_args *args,
-		uint32_t first_intid, int num,
-		kvm_inject_cmd cmd)
+				   uint32_t first_intid, int num,
+				   const unsigned long *exclude,
+				   kvm_inject_cmd cmd)
 {
 	uint32_t intid, prio, step = KVM_PRIO_STEPS;
 	int i;
@@ -379,6 +380,10 @@ static void test_inject_preemption(struct test_args *args,
 	for (i = 0; i < num; i++) {
 		uint32_t tmp;
 		intid = i + first_intid;
+
+		if (exclude && test_bit(i, exclude))
+			continue;
+
 		KVM_INJECT(cmd, intid);
 		/* Each successive IRQ will preempt the previous one. */
 		tmp = wait_for_and_activate_irq();
@@ -390,6 +395,10 @@ static void test_inject_preemption(struct test_args *args,
 	/* finish handling the IRQs starting with the highest priority one. */
 	for (i = 0; i < num; i++) {
 		intid = num - i - 1 + first_intid;
+
+		if (exclude && test_bit(intid - first_intid, exclude))
+			continue;
+
 		gic_set_eoi(intid);
 		if (args->eoi_split)
 			gic_set_dir(intid);
@@ -397,8 +406,12 @@ static void test_inject_preemption(struct test_args *args,
 
 	local_irq_enable();
 
-	for (i = 0; i < num; i++)
+	for (i = 0; i < num; i++) {
+		if (exclude && test_bit(i, exclude))
+			continue;
+
 		GUEST_ASSERT(!gic_irq_get_active(i + first_intid));
+	}
 	GUEST_ASSERT_EQ(gic_read_ap1r0(), 0);
 	GUEST_ASSERT_IAR_EMPTY();
 
@@ -442,14 +455,20 @@ static void test_preemption(struct test_args *args, struct kvm_inject_desc *f)
 	 * number of concurrently active IRQs. The number of LRs implemented is
 	 * IMPLEMENTATION DEFINED, however, it seems that most implement 4.
 	 */
+	/* Timer PPIs cannot be injected from userspace */
+	static const unsigned long ppi_exclude = (BIT(27 - MIN_PPI) |
+						  BIT(30 - MIN_PPI) |
+						  BIT(28 - MIN_PPI) |
+						  BIT(26 - MIN_PPI));
+
 	if (f->sgi)
-		test_inject_preemption(args, MIN_SGI, 4, f->cmd);
+		test_inject_preemption(args, MIN_SGI, 4, NULL, f->cmd);
 
 	if (f->ppi)
-		test_inject_preemption(args, MIN_PPI, 4, f->cmd);
+		test_inject_preemption(args, MIN_PPI, 4, &ppi_exclude, f->cmd);
 
 	if (f->spi)
-		test_inject_preemption(args, MIN_SPI, 4, f->cmd);
+		test_inject_preemption(args, MIN_SPI, 4, NULL, f->cmd);
 }
 
 static void test_restore_active(struct test_args *args, struct kvm_inject_desc *f)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 41/45] KVM: arm64: selftests: vgic_irq: Remove LR-bound limitation
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (39 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 40/45] KVM: arm64: selftests: vgic_irq: Exclude timer-controlled interrupts Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 42/45] KVM: arm64: selftests: vgic_irq: Perform EOImode==1 deactivation in ack order Marc Zyngier
                   ` (4 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Good news: our GIC emulation is not completely broken, and we can
activate as many interrupts as we want.

Bump the test to cover all the SGIs, all the allowed PPIs, and
31 SPIs. Yes, 31, because we have 31 available priorities, and the
test is not happy with having two interrupts with the same priority.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index b0415bdb89524..9d4761f1a3204 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -449,12 +449,6 @@ static void test_injection_failure(struct test_args *args,
 
 static void test_preemption(struct test_args *args, struct kvm_inject_desc *f)
 {
-	/*
-	 * Test up to 4 levels of preemption. The reason is that KVM doesn't
-	 * currently implement the ability to have more than the number-of-LRs
-	 * number of concurrently active IRQs. The number of LRs implemented is
-	 * IMPLEMENTATION DEFINED, however, it seems that most implement 4.
-	 */
 	/* Timer PPIs cannot be injected from userspace */
 	static const unsigned long ppi_exclude = (BIT(27 - MIN_PPI) |
 						  BIT(30 - MIN_PPI) |
@@ -462,26 +456,25 @@ static void test_preemption(struct test_args *args, struct kvm_inject_desc *f)
 						  BIT(26 - MIN_PPI));
 
 	if (f->sgi)
-		test_inject_preemption(args, MIN_SGI, 4, NULL, f->cmd);
+		test_inject_preemption(args, MIN_SGI, 16, NULL, f->cmd);
 
 	if (f->ppi)
-		test_inject_preemption(args, MIN_PPI, 4, &ppi_exclude, f->cmd);
+		test_inject_preemption(args, MIN_PPI, 16, &ppi_exclude, f->cmd);
 
 	if (f->spi)
-		test_inject_preemption(args, MIN_SPI, 4, NULL, f->cmd);
+		test_inject_preemption(args, MIN_SPI, 31, NULL, f->cmd);
 }
 
 static void test_restore_active(struct test_args *args, struct kvm_inject_desc *f)
 {
-	/* Test up to 4 active IRQs. Same reason as in test_preemption. */
 	if (f->sgi)
-		guest_restore_active(args, MIN_SGI, 4, f->cmd);
+		guest_restore_active(args, MIN_SGI, 16, f->cmd);
 
 	if (f->ppi)
-		guest_restore_active(args, MIN_PPI, 4, f->cmd);
+		guest_restore_active(args, MIN_PPI, 16, f->cmd);
 
 	if (f->spi)
-		guest_restore_active(args, MIN_SPI, 4, f->cmd);
+		guest_restore_active(args, MIN_SPI, 31, f->cmd);
 }
 
 static void guest_code(struct test_args *args)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 42/45] KVM: arm64: selftests: vgic_irq: Perform EOImode==1 deactivation in ack order
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (40 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 41/45] KVM: arm64: selftests: vgic_irq: Remove LR-bound limitation Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 43/45] KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test Marc Zyngier
                   ` (3 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

When EOImode==1, perform the deactivation in the order of activation,
just to make things a bit worse for KVM. Yes, I'm nasty.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index 9d4761f1a3204..72f7bb0d201e5 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -400,8 +400,18 @@ static void test_inject_preemption(struct test_args *args,
 			continue;
 
 		gic_set_eoi(intid);
-		if (args->eoi_split)
-			gic_set_dir(intid);
+	}
+
+	if (args->eoi_split) {
+		for (i = 0; i < num; i++) {
+			intid = i + first_intid;
+
+			if (exclude && test_bit(i, exclude))
+				continue;
+
+			if (args->eoi_split)
+				gic_set_dir(intid);
+		}
 	}
 
 	local_irq_enable();
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 43/45] KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (41 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 42/45] KVM: arm64: selftests: vgic_irq: Perform EOImode==1 deactivation in ack order Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 44/45] KVM: arm64: selftests: vgic_irq: Add Group-0 enable test Marc Zyngier
                   ` (2 subsequent siblings)
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Add a new test case that makes an interrupt pending on a vcpu,
activates it, do the priority drop, and then get *another* vcpu
to do the deactivation.

Special care is taken not to trigger an exit in the process, so
that we are sure that the active interrupt is in an LR. Joy.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 105 +++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index 72f7bb0d201e5..a53ab809fe8ae 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -29,6 +29,7 @@ struct test_args {
 	bool level_sensitive; /* 1 is level, 0 is edge */
 	int kvm_max_routes; /* output of KVM_CAP_IRQ_ROUTING */
 	bool kvm_supports_irqfd; /* output of KVM_CAP_IRQFD */
+	uint32_t shared_data;
 };
 
 /*
@@ -801,6 +802,109 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split)
 	kvm_vm_free(vm);
 }
 
+static void guest_code_asym_dir(struct test_args *args, int cpuid)
+{
+	gic_init(GIC_V3, 2);
+
+	gic_set_eoi_split(1);
+	gic_set_priority_mask(CPU_PRIO_MASK);
+
+	if (cpuid == 0) {
+		uint32_t intid;
+
+		local_irq_disable();
+
+		gic_set_priority(MIN_PPI, IRQ_DEFAULT_PRIO);
+		gic_irq_enable(MIN_SPI);
+		gic_irq_set_pending(MIN_SPI);
+
+		intid = wait_for_and_activate_irq();
+		GUEST_ASSERT_EQ(intid, MIN_SPI);
+
+		gic_set_eoi(intid);
+		isb();
+
+		WRITE_ONCE(args->shared_data, MIN_SPI);
+		dsb(ishst);
+
+		do {
+			dsb(ishld);
+		} while (READ_ONCE(args->shared_data) == MIN_SPI);
+		GUEST_ASSERT(!gic_irq_get_active(MIN_SPI));
+	} else {
+		do {
+			dsb(ishld);
+		} while (READ_ONCE(args->shared_data) != MIN_SPI);
+
+		gic_set_dir(MIN_SPI);
+		isb();
+
+		WRITE_ONCE(args->shared_data, 0);
+		dsb(ishst);
+	}
+
+	GUEST_DONE();
+}
+
+static void *test_vcpu_run(void *arg)
+{
+	struct kvm_vcpu *vcpu = arg;
+	struct ucall uc;
+
+	while (1) {
+		vcpu_run(vcpu);
+
+		switch (get_ucall(vcpu, &uc)) {
+		case UCALL_ABORT:
+			REPORT_GUEST_ASSERT(uc);
+			break;
+		case UCALL_DONE:
+			return NULL;
+		default:
+			TEST_FAIL("Unknown ucall %lu", uc.cmd);
+		}
+	}
+
+	return NULL;
+}
+
+static void test_vgic_two_cpus(void *gcode)
+{
+	pthread_t thr[2];
+	struct kvm_vcpu *vcpus[2];
+	struct test_args args = {};
+	struct kvm_vm *vm;
+	vm_vaddr_t args_gva;
+	int gic_fd, ret;
+
+	vm = vm_create_with_vcpus(2, gcode, vcpus);
+
+	vm_init_descriptor_tables(vm);
+	vcpu_init_descriptor_tables(vcpus[0]);
+	vcpu_init_descriptor_tables(vcpus[1]);
+
+	/* Setup the guest args page (so it gets the args). */
+	args_gva = vm_vaddr_alloc_page(vm);
+	memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args));
+	vcpu_args_set(vcpus[0], 2, args_gva, 0);
+	vcpu_args_set(vcpus[1], 2, args_gva, 1);
+
+	gic_fd = vgic_v3_setup(vm, 2, 64);
+
+	ret = pthread_create(&thr[0], NULL, test_vcpu_run, vcpus[0]);
+	if (ret)
+		TEST_FAIL("Can't create thread for vcpu 0 (%d)\n", ret);
+	ret = pthread_create(&thr[1], NULL, test_vcpu_run, vcpus[1]);
+	if (ret)
+		TEST_FAIL("Can't create thread for vcpu 1 (%d)\n", ret);
+
+	pthread_join(thr[0], NULL);
+	pthread_join(thr[1], NULL);
+
+	close(gic_fd);
+	kvm_vm_free(vm);
+}
+
 static void help(const char *name)
 {
 	printf(
@@ -857,6 +961,7 @@ int main(int argc, char **argv)
 		test_vgic(nr_irqs, false /* level */, true /* eoi_split */);
 		test_vgic(nr_irqs, true /* level */, false /* eoi_split */);
 		test_vgic(nr_irqs, true /* level */, true /* eoi_split */);
+		test_vgic_two_cpus(guest_code_asym_dir);
 	} else {
 		test_vgic(nr_irqs, level_sensitive, eoi_split);
 	}
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 44/45] KVM: arm64: selftests: vgic_irq: Add Group-0 enable test
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (42 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 43/45] KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-09 17:16 ` [PATCH v2 45/45] KVM: arm64: selftests: vgic_irq: Add timer deactivation test Marc Zyngier
  2025-11-12  9:13 ` [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Oliver Upton
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Add a new test case that inject a Group-0 interrupt together
with a bunch of Group-1 interrupts, Ack/EOI the G1 interrupts,
and only then enable G0, expecting to get the G0 interrupt.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 49 ++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index a53ab809fe8ae..ff2c75749f5c5 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -846,6 +846,54 @@ static void guest_code_asym_dir(struct test_args *args, int cpuid)
 	GUEST_DONE();
 }
 
+static void guest_code_group_en(struct test_args *args, int cpuid)
+{
+	uint32_t intid;
+
+	gic_init(GIC_V3, 2);
+
+	gic_set_eoi_split(0);
+	gic_set_priority_mask(CPU_PRIO_MASK);
+	/* SGI0 is G0, which is disabled */
+	gic_irq_set_group(0, 0);
+
+	/* Configure all SGIs with decreasing priority */
+	for (intid = 0; intid < MIN_PPI; intid++) {
+		gic_set_priority(intid, (intid + 1) * 8);
+		gic_irq_enable(intid);
+		gic_irq_set_pending(intid);
+	}
+
+	/* Ack and EOI all G1 interrupts */
+	for (int i = 1; i < MIN_PPI; i++) {
+		intid = wait_for_and_activate_irq();
+
+		GUEST_ASSERT(intid < MIN_PPI);
+		gic_set_eoi(intid);
+		isb();
+	}
+
+	/*
+	 * Check that SGI0 is still pending, inactive, and that we cannot
+	 * ack anything.
+	 */
+	GUEST_ASSERT(gic_irq_get_pending(0));
+	GUEST_ASSERT(!gic_irq_get_active(0));
+	GUEST_ASSERT_IAR_EMPTY();
+	GUEST_ASSERT(read_sysreg_s(SYS_ICC_IAR0_EL1) == IAR_SPURIOUS);
+
+	/* Open the G0 gates, and verify we can ack SGI0 */
+	write_sysreg_s(1, SYS_ICC_IGRPEN0_EL1);
+	isb();
+
+	do {
+		intid = read_sysreg_s(SYS_ICC_IAR0_EL1);
+	} while (intid == IAR_SPURIOUS);
+
+	GUEST_ASSERT(intid == 0);
+	GUEST_DONE();
+}
+
 static void *test_vcpu_run(void *arg)
 {
 	struct kvm_vcpu *vcpu = arg;
@@ -962,6 +1010,7 @@ int main(int argc, char **argv)
 		test_vgic(nr_irqs, true /* level */, false /* eoi_split */);
 		test_vgic(nr_irqs, true /* level */, true /* eoi_split */);
 		test_vgic_two_cpus(guest_code_asym_dir);
+		test_vgic_two_cpus(guest_code_group_en);
 	} else {
 		test_vgic(nr_irqs, level_sensitive, eoi_split);
 	}
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v2 45/45] KVM: arm64: selftests: vgic_irq: Add timer deactivation test
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (43 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 44/45] KVM: arm64: selftests: vgic_irq: Add Group-0 enable test Marc Zyngier
@ 2025-11-09 17:16 ` Marc Zyngier
  2025-11-12  9:13 ` [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Oliver Upton
  45 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-09 17:16 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Add a new test case that triggers the HW deactivation emulation path
when trapping ICV_DIR_EL1. This is obviously tied to the way KVM
works now, but the test follows the expected architectural behaviour.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 tools/testing/selftests/kvm/arm64/vgic_irq.c | 65 ++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c
index ff2c75749f5c5..9858187c7b6ea 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_irq.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c
@@ -894,6 +894,70 @@ static void guest_code_group_en(struct test_args *args, int cpuid)
 	GUEST_DONE();
 }
 
+static void guest_code_timer_spi(struct test_args *args, int cpuid)
+{
+	uint32_t intid;
+	u64 val;
+
+	gic_init(GIC_V3, 2);
+
+	gic_set_eoi_split(1);
+	gic_set_priority_mask(CPU_PRIO_MASK);
+
+	/* Add a pending SPI so that KVM starts trapping DIR */
+	gic_set_priority(MIN_SPI + cpuid, IRQ_DEFAULT_PRIO);
+	gic_irq_set_pending(MIN_SPI + cpuid);
+
+	/* Configure the timer with a higher priority, make it pending */
+	gic_set_priority(27, IRQ_DEFAULT_PRIO - 8);
+
+	isb();
+	val = read_sysreg(cntvct_el0);
+	write_sysreg(val, cntv_cval_el0);
+	write_sysreg(1, cntv_ctl_el0);
+	isb();
+
+	GUEST_ASSERT(gic_irq_get_pending(27));
+
+	/* Enable both interrupts */
+	gic_irq_enable(MIN_SPI + cpuid);
+	gic_irq_enable(27);
+
+	/* The timer must fire */
+	intid = wait_for_and_activate_irq();
+	GUEST_ASSERT(intid == 27);
+
+	/* Check that we can deassert it */
+	write_sysreg(0, cntv_ctl_el0);
+	isb();
+
+	GUEST_ASSERT(!gic_irq_get_pending(27));
+
+	/*
+	 * Priority drop, deactivation -- we expect that the host
+	 * deactivation will have been effective
+	 */
+	gic_set_eoi(27);
+	gic_set_dir(27);
+
+	GUEST_ASSERT(!gic_irq_get_active(27));
+
+	/* Do it one more time */
+	isb();
+	val = read_sysreg(cntvct_el0);
+	write_sysreg(val, cntv_cval_el0);
+	write_sysreg(1, cntv_ctl_el0);
+	isb();
+
+	GUEST_ASSERT(gic_irq_get_pending(27));
+
+	/* The timer must fire again */
+	intid = wait_for_and_activate_irq();
+	GUEST_ASSERT(intid == 27);
+
+	GUEST_DONE();
+}
+
 static void *test_vcpu_run(void *arg)
 {
 	struct kvm_vcpu *vcpu = arg;
@@ -1011,6 +1075,7 @@ int main(int argc, char **argv)
 		test_vgic(nr_irqs, true /* level */, true /* eoi_split */);
 		test_vgic_two_cpus(guest_code_asym_dir);
 		test_vgic_two_cpus(guest_code_group_en);
+		test_vgic_two_cpus(guest_code_timer_spi);
 	} else {
 		test_vgic(nr_irqs, level_sensitive, eoi_split);
 	}
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive
  2025-11-09 17:15 ` [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive Marc Zyngier
@ 2025-11-10  9:01   ` Yao Yuan
  2025-11-10  9:18     ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Yao Yuan @ 2025-11-10  9:01 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk

On Sun, Nov 09, 2025 at 05:15:46PM +0800, Marc Zyngier wrote:
> As we are going to need to handle deactivation for interrupts that
> are not in the LRs, split vgic_v3_fold_lr_state() into a helper
> that deals with a single interrupt, and the function that loops
> over the used LRs.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
>  arch/arm64/kvm/vgic/vgic-v3.c | 88 +++++++++++++++++------------------
>  1 file changed, 43 insertions(+), 45 deletions(-)
>
> diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> index 3ede79e381513..0fccfe9e3e8dd 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> @@ -33,78 +33,76 @@ static bool lr_signals_eoi_mi(u64 lr_val)
>  	       !(lr_val & ICH_LR_HW);
>  }
>
> -void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
> +static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
>  {
> -	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> -	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
> -	u32 model = vcpu->kvm->arch.vgic.vgic_model;
> -	int lr;
> -
> -	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
> -
> -	cpuif->vgic_hcr &= ~ICH_HCR_EL2_UIE;
> -
> -	for (lr = 0; lr < cpuif->used_lrs; lr++) {
> -		u64 val = cpuif->vgic_lr[lr];
> -		u32 intid, cpuid;
> -		struct vgic_irq *irq;
> -		bool is_v2_sgi = false;
> -		bool deactivated;
> -
> -		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 {
> -			intid = val & GICH_LR_VIRTUALID;
> -			is_v2_sgi = vgic_irq_is_sgi(intid);
> -		}
> +	struct vgic_irq *irq;
> +	bool is_v2_sgi = false;
> +	bool deactivated;
> +	u32 intid;
>
> -		/* Notify fds when the guest EOI'ed a level-triggered IRQ */
> -		if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
> -			kvm_notify_acked_irq(vcpu->kvm, 0,
> -					     intid - VGIC_NR_PRIVATE_IRQS);
> +	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> +		intid = val & ICH_LR_VIRTUAL_ID_MASK;
> +	} else {
> +		intid = val & GICH_LR_VIRTUALID;
> +		is_v2_sgi = vgic_irq_is_sgi(intid);
> +	}
>
> -		irq = vgic_get_vcpu_irq(vcpu, intid);
> -		if (!irq)	/* An LPI could have been unmapped. */
> -			continue;
> +	irq = vgic_get_vcpu_irq(vcpu, intid);
> +	if (!irq)	/* An LPI could have been unmapped. */
> +		return;
>
> -		raw_spin_lock(&irq->irq_lock);
> +	/* Notify fds when the guest EOI'ed a level-triggered IRQ */
> +	if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
> +		kvm_notify_acked_irq(vcpu->kvm, 0,
> +				     intid - VGIC_NR_PRIVATE_IRQS);

The fds notifiy happens before checking irq's mapping before
this patch, and now in reversal order w/ above change. It's
fine for vLPI, and for vSPI no necessary call
kvm_notify_acked_irq() if the it has been remapped, no
gsi<->pin mapping there. Is above understanding correct ?

>
> +	scoped_guard(raw_spinlock, &irq->irq_lock) {
>  		/* Always preserve the active bit for !LPIs, note deactivation */
>  		if (irq->intid >= VGIC_MIN_LPI)
>  			val &= ~ICH_LR_ACTIVE_BIT;
>  		deactivated = irq->active && !(val & ICH_LR_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)) {
> +		    (val & ICH_LR_PENDING_BIT))
>  			irq->pending_latch = true;
>
> -			if (is_v2_sgi)
> -				irq->source |= (1 << cpuid);
> -		}
> -
>  		/*
>  		 * Clear soft pending state when level irqs have been acked.
>  		 */
>  		if (irq->config == VGIC_CONFIG_LEVEL && !(val & ICH_LR_STATE))
>  			irq->pending_latch = false;
>
> +		if (is_v2_sgi) {
> +			u8 cpuid = FIELD_GET(GICH_LR_PHYSID_CPUID, val);
> +
> +			if (irq->active)
> +				irq->active_source = cpuid;
> +
> +			if (val & ICH_LR_PENDING_BIT)
> +				irq->source |= BIT(cpuid);
> +		}
> +
>  		/* Handle resampling for mapped interrupts if required */
>  		vgic_irq_handle_resampling(irq, deactivated, val & ICH_LR_PENDING_BIT);
>
>  		irq->on_lr = false;
> -
> -		raw_spin_unlock(&irq->irq_lock);
> -		vgic_put_irq(vcpu->kvm, irq);
>  	}
>
> +	vgic_put_irq(vcpu->kvm, irq);
> +}
> +
> +void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
> +{
> +	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> +	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
> +
> +	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
> +
> +	for (int lr = 0; lr < cpuif->used_lrs; lr++)
> +		vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]);
> +
>  	cpuif->used_lrs = 0;
>  }
>
> --
> 2.47.3


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive
  2025-11-10  9:01   ` Yao Yuan
@ 2025-11-10  9:18     ` Marc Zyngier
  2025-11-10  9:48       ` Yao Yuan
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-10  9:18 UTC (permalink / raw)
  To: Yao Yuan
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk

On Mon, 10 Nov 2025 09:01:21 +0000,
Yao Yuan <yaoyuan@linux.alibaba.com> wrote:
> 
> On Sun, Nov 09, 2025 at 05:15:46PM +0800, Marc Zyngier wrote:
> > As we are going to need to handle deactivation for interrupts that
> > are not in the LRs, split vgic_v3_fold_lr_state() into a helper
> > that deals with a single interrupt, and the function that loops
> > over the used LRs.
> >
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > ---
> >  arch/arm64/kvm/vgic/vgic-v3.c | 88 +++++++++++++++++------------------
> >  1 file changed, 43 insertions(+), 45 deletions(-)
> >
> > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > index 3ede79e381513..0fccfe9e3e8dd 100644
> > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > @@ -33,78 +33,76 @@ static bool lr_signals_eoi_mi(u64 lr_val)
> >  	       !(lr_val & ICH_LR_HW);
> >  }
> >
> > -void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
> > +static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
> >  {
> > -	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> > -	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
> > -	u32 model = vcpu->kvm->arch.vgic.vgic_model;
> > -	int lr;
> > -
> > -	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
> > -
> > -	cpuif->vgic_hcr &= ~ICH_HCR_EL2_UIE;
> > -
> > -	for (lr = 0; lr < cpuif->used_lrs; lr++) {
> > -		u64 val = cpuif->vgic_lr[lr];
> > -		u32 intid, cpuid;
> > -		struct vgic_irq *irq;
> > -		bool is_v2_sgi = false;
> > -		bool deactivated;
> > -
> > -		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 {
> > -			intid = val & GICH_LR_VIRTUALID;
> > -			is_v2_sgi = vgic_irq_is_sgi(intid);
> > -		}
> > +	struct vgic_irq *irq;
> > +	bool is_v2_sgi = false;
> > +	bool deactivated;
> > +	u32 intid;
> >
> > -		/* Notify fds when the guest EOI'ed a level-triggered IRQ */
> > -		if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
> > -			kvm_notify_acked_irq(vcpu->kvm, 0,
> > -					     intid - VGIC_NR_PRIVATE_IRQS);
> > +	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> > +		intid = val & ICH_LR_VIRTUAL_ID_MASK;
> > +	} else {
> > +		intid = val & GICH_LR_VIRTUALID;
> > +		is_v2_sgi = vgic_irq_is_sgi(intid);
> > +	}
> >
> > -		irq = vgic_get_vcpu_irq(vcpu, intid);
> > -		if (!irq)	/* An LPI could have been unmapped. */
> > -			continue;
> > +	irq = vgic_get_vcpu_irq(vcpu, intid);
> > +	if (!irq)	/* An LPI could have been unmapped. */
> > +		return;
> >
> > -		raw_spin_lock(&irq->irq_lock);
> > +	/* Notify fds when the guest EOI'ed a level-triggered IRQ */
> > +	if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
> > +		kvm_notify_acked_irq(vcpu->kvm, 0,
> > +				     intid - VGIC_NR_PRIVATE_IRQS);
> 
> The fds notifiy happens before checking irq's mapping before
> this patch, and now in reversal order w/ above change. It's
> fine for vLPI, and for vSPI no necessary call
> kvm_notify_acked_irq() if the it has been remapped, no
> gsi<->pin mapping there. Is above understanding correct ?

We can only notify an irqfd for an SPI, never for an LPI. Given that
only looking up an LPI can result in a NULL pointer (if it has been
concurrently removed), this change is immaterial

This results in something that is easier to understand, as I find it
more logical to weed out the error cases first before taking any
significant action.

Thanks,

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive
  2025-11-10  9:18     ` Marc Zyngier
@ 2025-11-10  9:48       ` Yao Yuan
  0 siblings, 0 replies; 83+ messages in thread
From: Yao Yuan @ 2025-11-10  9:48 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk

On Mon, Nov 10, 2025 at 09:18:45AM +0800, Marc Zyngier wrote:
> On Mon, 10 Nov 2025 09:01:21 +0000,
> Yao Yuan <yaoyuan@linux.alibaba.com> wrote:
> >
> > On Sun, Nov 09, 2025 at 05:15:46PM +0800, Marc Zyngier wrote:
> > > As we are going to need to handle deactivation for interrupts that
> > > are not in the LRs, split vgic_v3_fold_lr_state() into a helper
> > > that deals with a single interrupt, and the function that loops
> > > over the used LRs.
> > >
> > > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > > ---
> > >  arch/arm64/kvm/vgic/vgic-v3.c | 88 +++++++++++++++++------------------
> > >  1 file changed, 43 insertions(+), 45 deletions(-)
> > >
> > > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > > index 3ede79e381513..0fccfe9e3e8dd 100644
> > > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > > @@ -33,78 +33,76 @@ static bool lr_signals_eoi_mi(u64 lr_val)
> > >  	       !(lr_val & ICH_LR_HW);
> > >  }
> > >
> > > -void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
> > > +static void vgic_v3_fold_lr(struct kvm_vcpu *vcpu, u64 val)
> > >  {
> > > -	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> > > -	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
> > > -	u32 model = vcpu->kvm->arch.vgic.vgic_model;
> > > -	int lr;
> > > -
> > > -	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
> > > -
> > > -	cpuif->vgic_hcr &= ~ICH_HCR_EL2_UIE;
> > > -
> > > -	for (lr = 0; lr < cpuif->used_lrs; lr++) {
> > > -		u64 val = cpuif->vgic_lr[lr];
> > > -		u32 intid, cpuid;
> > > -		struct vgic_irq *irq;
> > > -		bool is_v2_sgi = false;
> > > -		bool deactivated;
> > > -
> > > -		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 {
> > > -			intid = val & GICH_LR_VIRTUALID;
> > > -			is_v2_sgi = vgic_irq_is_sgi(intid);
> > > -		}
> > > +	struct vgic_irq *irq;
> > > +	bool is_v2_sgi = false;
> > > +	bool deactivated;
> > > +	u32 intid;
> > >
> > > -		/* Notify fds when the guest EOI'ed a level-triggered IRQ */
> > > -		if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
> > > -			kvm_notify_acked_irq(vcpu->kvm, 0,
> > > -					     intid - VGIC_NR_PRIVATE_IRQS);
> > > +	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
> > > +		intid = val & ICH_LR_VIRTUAL_ID_MASK;
> > > +	} else {
> > > +		intid = val & GICH_LR_VIRTUALID;
> > > +		is_v2_sgi = vgic_irq_is_sgi(intid);
> > > +	}
> > >
> > > -		irq = vgic_get_vcpu_irq(vcpu, intid);
> > > -		if (!irq)	/* An LPI could have been unmapped. */
> > > -			continue;
> > > +	irq = vgic_get_vcpu_irq(vcpu, intid);
> > > +	if (!irq)	/* An LPI could have been unmapped. */
> > > +		return;
> > >
> > > -		raw_spin_lock(&irq->irq_lock);
> > > +	/* Notify fds when the guest EOI'ed a level-triggered IRQ */
> > > +	if (lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid))
> > > +		kvm_notify_acked_irq(vcpu->kvm, 0,
> > > +				     intid - VGIC_NR_PRIVATE_IRQS);
> >
> > The fds notifiy happens before checking irq's mapping before
> > this patch, and now in reversal order w/ above change. It's
> > fine for vLPI, and for vSPI no necessary call
> > kvm_notify_acked_irq() if the it has been remapped, no

Oops.. I want to say "and for vSPI no necessary call
kvm_notify_acked_irq() if it has been unmapped...".

> > gsi<->pin mapping there. Is above understanding correct ?
>
> We can only notify an irqfd for an SPI, never for an LPI. Given that

> only looking up an LPI can result in a NULL pointer (if it has been
> concurrently removed), this change is immaterial

You're right. The spis is allocated in vgic_init().

>
> This results in something that is easier to understand, as I find it
> more logical to weed out the error cases first before taking any
> significant action.

Yes, this makes the code better than beforem and thanks for your explanation!

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
@ 2025-11-10 10:40   ` Suzuki K Poulose
  2025-11-10 11:47     ` Marc Zyngier
  2025-11-11 23:53   ` Oliver Upton
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 83+ messages in thread
From: Suzuki K Poulose @ 2025-11-10 10:40 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

Hi Marc,

On 09/11/2025 17:15, Marc Zyngier wrote:
> The trap bits are currently only set to manage CPU errata. However,
> we are about to make use of them for purposes beyond beating broken
> CPUs into submission.
> 
> For this purpose, turn these errata-driven bits into a patched-in
> constant that is merged with the KVM-driven value at the point of
> programming the ICH_HCR_EL2 register, rather than being directly
> stored with with the shadow value..
> 
> This allows the KVM code to distinguish between a trap being handled
> for the purpose of an erratum workaround, or for KVM's own need.
> 
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---

...

> diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
> index ac5f9c5d2b980..0ecadfa00397d 100644
> --- a/arch/arm64/kvm/vgic/vgic.h
> +++ b/arch/arm64/kvm/vgic/vgic.h
> @@ -164,6 +164,22 @@ static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
>   	return ret;
>   }
>   
> +void kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
> +				   __le32 *origptr, __le32 *updptr, int nr_inst);
> +
> +static inline u64 vgic_ich_hcr_trap_bits(void)
> +{
> +	u64 hcr;

minor nit: Do we need a guard to make sure this isn't called before the 
capabilities are finalized (given we may use it outside VM context, e.g. 
VGIC probe). perhaps :

WARN_ON(!system_capabilities_finalized());


> +
> +	/* All the traps are in the bottom 16bits */
> +	asm volatile(ALTERNATIVE_CB("movz %0, #0\n",
> +				    ARM64_ALWAYS_SYSTEM,
> +				    kvm_compute_ich_hcr_trap_bits)
> +		     : "=r" (hcr));


Suzuki


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-10 10:40   ` Suzuki K Poulose
@ 2025-11-10 11:47     ` Marc Zyngier
  0 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-10 11:47 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Oliver Upton,
	Zenghui Yu, Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On Mon, 10 Nov 2025 10:40:21 +0000,
Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> 
> Hi Marc,
> 
> On 09/11/2025 17:15, Marc Zyngier wrote:
> > The trap bits are currently only set to manage CPU errata. However,
> > we are about to make use of them for purposes beyond beating broken
> > CPUs into submission.
> > 
> > For this purpose, turn these errata-driven bits into a patched-in
> > constant that is merged with the KVM-driven value at the point of
> > programming the ICH_HCR_EL2 register, rather than being directly
> > stored with with the shadow value..
> > 
> > This allows the KVM code to distinguish between a trap being handled
> > for the purpose of an erratum workaround, or for KVM's own need.
> > 
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > ---
> 
> ...
> 
> > diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
> > index ac5f9c5d2b980..0ecadfa00397d 100644
> > --- a/arch/arm64/kvm/vgic/vgic.h
> > +++ b/arch/arm64/kvm/vgic/vgic.h
> > @@ -164,6 +164,22 @@ static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
> >   	return ret;
> >   }
> >   +void kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
> > +				   __le32 *origptr, __le32 *updptr, int nr_inst);
> > +
> > +static inline u64 vgic_ich_hcr_trap_bits(void)
> > +{
> > +	u64 hcr;
> 
> minor nit: Do we need a guard to make sure this isn't called before
> the capabilities are finalized (given we may use it outside VM
> context, e.g. VGIC probe). perhaps :
> 
> WARN_ON(!system_capabilities_finalized());

We already have a BUG_ON() for that at the point of setting up the
vectors for pKVM. It wouldn't hurt to move this up as a general check
for KVM, but I don't think placing these checks in random leaf
functions is very appealing.

And if we do, it should be gated to not emit code in the hot path,
with something like this (which I find awful):

diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index ec3a61e8e6b30..43f202cb83b48 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -171,6 +171,9 @@ static inline u64 vgic_ich_hcr_trap_bits(void)
 {
 	u64 hcr;
 
+#if !defined(__KVM_VHE_HYPERVISOR__) && !defined(__KVM_NVHE_HYPERVISOR__)
+	WARN_ON(!system_capabilities_finalized());
+#endif
 	/* All the traps are in the bottom 16bits */
 	asm volatile(ALTERNATIVE_CB("movz %0, #0\n",
 				    ARM64_ALWAYS_SYSTEM,

Thanks,

	M.

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


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
  2025-11-10 10:40   ` Suzuki K Poulose
@ 2025-11-11 23:53   ` Oliver Upton
  2025-11-13  9:52   ` Marek Szyprowski
  2025-11-13 18:01   ` Mark Brown
  3 siblings, 0 replies; 83+ messages in thread
From: Oliver Upton @ 2025-11-11 23:53 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Christoffer Dall, Volodymyr Babchuk, Yao Yuan

Hey,

On Sun, Nov 09, 2025 at 05:15:38PM +0000, Marc Zyngier wrote:
> The trap bits are currently only set to manage CPU errata. However,
> we are about to make use of them for purposes beyond beating broken
> CPUs into submission.

There's also the command-line hacks for configuring traps, which
should still work given the relative ordering of alternatives
patching. But might be worth a mention.

Thanks,
Oliver

> For this purpose, turn these errata-driven bits into a patched-in
> constant that is merged with the KVM-driven value at the point of
> programming the ICH_HCR_EL2 register, rather than being directly
> stored with with the shadow value..
> 
> This allows the KVM code to distinguish between a trap being handled
> for the purpose of an erratum workaround, or for KVM's own need.
> 
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
>  arch/arm64/kernel/image-vars.h       |  1 +
>  arch/arm64/kvm/hyp/vgic-v3-sr.c      | 21 +++++---
>  arch/arm64/kvm/vgic/vgic-v3-nested.c |  9 ----
>  arch/arm64/kvm/vgic/vgic-v3.c        | 81 +++++++++++++++++-----------
>  arch/arm64/kvm/vgic/vgic.h           | 16 ++++++
>  5 files changed, 82 insertions(+), 46 deletions(-)
> 
> diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
> index 5369763606e71..85bc629270bd9 100644
> --- a/arch/arm64/kernel/image-vars.h
> +++ b/arch/arm64/kernel/image-vars.h
> @@ -91,6 +91,7 @@ KVM_NVHE_ALIAS(spectre_bhb_patch_loop_mitigation_enable);
>  KVM_NVHE_ALIAS(spectre_bhb_patch_wa3);
>  KVM_NVHE_ALIAS(spectre_bhb_patch_clearbhb);
>  KVM_NVHE_ALIAS(alt_cb_patch_nops);
> +KVM_NVHE_ALIAS(kvm_compute_ich_hcr_trap_bits);
>  
>  /* Global kernel state accessed by nVHE hyp code. */
>  KVM_NVHE_ALIAS(kvm_vgic_global_state);
> diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
> index acd909b7f2257..00ad89d71bb3f 100644
> --- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
> +++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
> @@ -14,6 +14,8 @@
>  #include <asm/kvm_hyp.h>
>  #include <asm/kvm_mmu.h>
>  
> +#include "../../vgic/vgic.h"
> +
>  #define vtr_to_max_lr_idx(v)		((v) & 0xf)
>  #define vtr_to_nr_pre_bits(v)		((((u32)(v) >> 26) & 7) + 1)
>  #define vtr_to_nr_apr_regs(v)		(1 << (vtr_to_nr_pre_bits(v) - 5))
> @@ -196,6 +198,11 @@ static u32 __vgic_v3_read_ap1rn(int n)
>  	return val;
>  }
>  
> +static u64 compute_ich_hcr(struct vgic_v3_cpu_if *cpu_if)
> +{
> +	return cpu_if->vgic_hcr | vgic_ich_hcr_trap_bits();
> +}
> +
>  void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
>  {
>  	u64 used_lrs = cpu_if->used_lrs;
> @@ -218,7 +225,7 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
>  
>  		elrsr = read_gicreg(ICH_ELRSR_EL2);
>  
> -		write_gicreg(cpu_if->vgic_hcr & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
> +		write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
>  
>  		for (i = 0; i < used_lrs; i++) {
>  			if (elrsr & (1 << i))
> @@ -237,7 +244,7 @@ void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
>  	int i;
>  
>  	if (used_lrs || cpu_if->its_vpe.its_vm) {
> -		write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
> +		write_gicreg(compute_ich_hcr(cpu_if), ICH_HCR_EL2);
>  
>  		for (i = 0; i < used_lrs; i++)
>  			__gic_v3_set_lr(cpu_if->vgic_lr[i], i);
> @@ -307,14 +314,14 @@ void __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if)
>  	}
>  
>  	/*
> -	 * If we need to trap system registers, we must write
> -	 * ICH_HCR_EL2 anyway, even if no interrupts are being
> -	 * injected. Note that this also applies if we don't expect
> -	 * any system register access (no vgic at all).
> +	 * If we need to trap system registers, we must write ICH_HCR_EL2
> +	 * anyway, even if no interrupts are being injected. Note that this
> +	 * also applies if we don't expect any system register access (no
> +	 * vgic at all). In any case, no need to provide MI configuration.
>  	 */
>  	if (static_branch_unlikely(&vgic_v3_cpuif_trap) ||
>  	    cpu_if->its_vpe.its_vm || !cpu_if->vgic_sre)
> -		write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
> +		write_gicreg(vgic_ich_hcr_trap_bits() | ICH_HCR_EL2_En, ICH_HCR_EL2);
>  }
>  
>  void __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if)
> diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c
> index 7f1259b49c505..387557e20a272 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3-nested.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c
> @@ -301,15 +301,6 @@ static void vgic_v3_create_shadow_state(struct kvm_vcpu *vcpu,
>  	u64 val = 0;
>  	int i;
>  
> -	/*
> -	 * If we're on a system with a broken vgic that requires
> -	 * trapping, propagate the trapping requirements.
> -	 *
> -	 * Ah, the smell of rotten fruits...
> -	 */
> -	if (static_branch_unlikely(&vgic_v3_cpuif_trap))
> -		val = host_if->vgic_hcr & (ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 |
> -					   ICH_HCR_EL2_TC | ICH_HCR_EL2_TDIR);
>  	s_cpu_if->vgic_hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2) | val;
>  	s_cpu_if->vgic_vmcr = __vcpu_sys_reg(vcpu, ICH_VMCR_EL2);
>  	s_cpu_if->vgic_sre = host_if->vgic_sre;
> diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> index 6fbb4b0998552..236d0beef561d 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> @@ -301,20 +301,9 @@ void vcpu_set_ich_hcr(struct kvm_vcpu *vcpu)
>  		return;
>  
>  	/* Hide GICv3 sysreg if necessary */
> -	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) {
> +	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2)
>  		vgic_v3->vgic_hcr |= (ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 |
>  				      ICH_HCR_EL2_TC);
> -		return;
> -	}
> -
> -	if (group0_trap)
> -		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TALL0;
> -	if (group1_trap)
> -		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TALL1;
> -	if (common_trap)
> -		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TC;
> -	if (dir_trap)
> -		vgic_v3->vgic_hcr |= ICH_HCR_EL2_TDIR;
>  }
>  
>  int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq)
> @@ -635,10 +624,52 @@ static const struct midr_range broken_seis[] = {
>  
>  static bool vgic_v3_broken_seis(void)
>  {
> -	return ((kvm_vgic_global_state.ich_vtr_el2 & ICH_VTR_EL2_SEIS) &&
> +	return (is_kernel_in_hyp_mode() &&
> +		(read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS) &&
>  		is_midr_in_range_list(broken_seis));
>  }
>  
> +void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
> +					   __le32 *origptr, __le32 *updptr,
> +					   int nr_inst)
> +{
> +	u32 insn, oinsn, rd;
> +	u64 hcr = 0;
> +
> +	if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
> +		group0_trap = true;
> +		group1_trap = true;
> +	}
> +
> +	if (vgic_v3_broken_seis()) {
> +		/* We know that these machines have ICH_HCR_EL2.TDIR */
> +		group0_trap = true;
> +		group1_trap = true;
> +		dir_trap = true;
> +	}
> +
> +	if (group0_trap)
> +		hcr |= ICH_HCR_EL2_TALL0;
> +	if (group1_trap)
> +		hcr |= ICH_HCR_EL2_TALL1;
> +	if (common_trap)
> +		hcr |= ICH_HCR_EL2_TC;
> +	if (dir_trap)
> +		hcr |= ICH_HCR_EL2_TDIR;
> +
> +	/* Compute target register */
> +	oinsn = le32_to_cpu(*origptr);
> +	rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn);
> +
> +	/* movz rd, #(val & 0xffff) */
> +	insn = aarch64_insn_gen_movewide(rd,
> +					 (u16)hcr,
> +					 0,
> +					 AARCH64_INSN_VARIANT_64BIT,
> +					 AARCH64_INSN_MOVEWIDE_ZERO);
> +	*updptr = cpu_to_le32(insn);
> +}
> +
>  /**
>   * vgic_v3_probe - probe for a VGICv3 compatible interrupt controller
>   * @info:	pointer to the GIC description
> @@ -650,6 +681,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
>  {
>  	u64 ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
>  	bool has_v2;
> +	u64 traps;
>  	int ret;
>  
>  	has_v2 = ich_vtr_el2 >> 63;
> @@ -708,29 +740,18 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
>  	if (has_v2)
>  		static_branch_enable(&vgic_v3_has_v2_compat);
>  
> -	if (cpus_have_final_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
> -		group0_trap = true;
> -		group1_trap = true;
> -	}
> -
>  	if (vgic_v3_broken_seis()) {
>  		kvm_info("GICv3 with broken locally generated SEI\n");
> -
>  		kvm_vgic_global_state.ich_vtr_el2 &= ~ICH_VTR_EL2_SEIS;
> -		group0_trap = true;
> -		group1_trap = true;
> -		if (ich_vtr_el2 & ICH_VTR_EL2_TDS)
> -			dir_trap = true;
> -		else
> -			common_trap = true;
>  	}
>  
> -	if (group0_trap || group1_trap || common_trap | dir_trap) {
> +	traps = vgic_ich_hcr_trap_bits();
> +	if (traps) {
>  		kvm_info("GICv3 sysreg trapping enabled ([%s%s%s%s], reduced performance)\n",
> -			 group0_trap ? "G0" : "",
> -			 group1_trap ? "G1" : "",
> -			 common_trap ? "C"  : "",
> -			 dir_trap    ? "D"  : "");
> +			 (traps & ICH_HCR_EL2_TALL0) ? "G0" : "",
> +			 (traps & ICH_HCR_EL2_TALL1) ? "G1" : "",
> +			 (traps & ICH_HCR_EL2_TC)    ? "C"  : "",
> +			 (traps & ICH_HCR_EL2_TDIR)  ? "D"  : "");
>  		static_branch_enable(&vgic_v3_cpuif_trap);
>  	}
>  
> diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
> index ac5f9c5d2b980..0ecadfa00397d 100644
> --- a/arch/arm64/kvm/vgic/vgic.h
> +++ b/arch/arm64/kvm/vgic/vgic.h
> @@ -164,6 +164,22 @@ static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
>  	return ret;
>  }
>  
> +void kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
> +				   __le32 *origptr, __le32 *updptr, int nr_inst);
> +
> +static inline u64 vgic_ich_hcr_trap_bits(void)
> +{
> +	u64 hcr;
> +
> +	/* All the traps are in the bottom 16bits */
> +	asm volatile(ALTERNATIVE_CB("movz %0, #0\n",
> +				    ARM64_ALWAYS_SYSTEM,
> +				    kvm_compute_ich_hcr_trap_bits)
> +		     : "=r" (hcr));
> +
> +	return hcr;
> +}
> +
>  /*
>   * This struct provides an intermediate representation of the fields contained
>   * in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC
> -- 
> 2.47.3
> 


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration
  2025-11-09 17:15 ` [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration Marc Zyngier
@ 2025-11-12  0:08   ` Oliver Upton
  2025-11-12  8:33     ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Oliver Upton @ 2025-11-12  0:08 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On Sun, Nov 09, 2025 at 05:15:54PM +0000, Marc Zyngier wrote:
> +static void summarize_ap_list(struct kvm_vcpu *vcpu,
> +			      struct ap_list_summary *als)
>  {
>  	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
>  	struct vgic_irq *irq;
> -	int count = 0;
> -
> -	*multi_sgi = false;
>  
>  	lockdep_assert_held(&vgic_cpu->ap_list_lock);
>  
> -	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> -		int w;
> +	*als = (typeof(*als)){};
>  
> -		raw_spin_lock(&irq->irq_lock);
> -		/* GICv2 SGIs can count for more than one... */
> -		w = vgic_irq_get_lr_count(irq);
> -		raw_spin_unlock(&irq->irq_lock);
> +	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> +		scoped_guard(raw_spinlock, &irq->irq_lock) {
> +			if (vgic_target_oracle(irq) != vcpu)
> +				continue;

From our conversation about this sort of thing a few weeks ago, wont
this 'continue' interact pooly with the for loop that scoped_guard()
expands to?

Consistent with the other checks against the destination oracle you'll
probably want a branch hint too.

Thanks,
Oliver


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration
  2025-11-12  0:08   ` Oliver Upton
@ 2025-11-12  8:33     ` Marc Zyngier
  2025-11-12  8:45       ` Oliver Upton
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-12  8:33 UTC (permalink / raw)
  To: Oliver Upton
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On Wed, 12 Nov 2025 00:08:37 +0000,
Oliver Upton <oupton@kernel.org> wrote:
> 
> On Sun, Nov 09, 2025 at 05:15:54PM +0000, Marc Zyngier wrote:
> > +static void summarize_ap_list(struct kvm_vcpu *vcpu,
> > +			      struct ap_list_summary *als)
> >  {
> >  	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> >  	struct vgic_irq *irq;
> > -	int count = 0;
> > -
> > -	*multi_sgi = false;
> >  
> >  	lockdep_assert_held(&vgic_cpu->ap_list_lock);
> >  
> > -	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> > -		int w;
> > +	*als = (typeof(*als)){};
> >  
> > -		raw_spin_lock(&irq->irq_lock);
> > -		/* GICv2 SGIs can count for more than one... */
> > -		w = vgic_irq_get_lr_count(irq);
> > -		raw_spin_unlock(&irq->irq_lock);
> > +	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> > +		scoped_guard(raw_spinlock, &irq->irq_lock) {
> > +			if (vgic_target_oracle(irq) != vcpu)
> > +				continue;
> 
> From our conversation about this sort of thing a few weeks ago, wont
> this 'continue' interact pooly with the for loop that scoped_guard()
> expands to?

Gahhh... I was sure I had killed that everywhere, but obviously failed
to. I wish there was a coccinelle script to detect this sort of broken
constructs (where are the script kiddies when you really need them?).

Thanks for spotting it!

> Consistent with the other checks against the destination oracle you'll
> probably want a branch hint too.

Yup, I'll add that.

Thanks again,

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration
  2025-11-12  8:33     ` Marc Zyngier
@ 2025-11-12  8:45       ` Oliver Upton
  2025-11-12  9:56         ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Oliver Upton @ 2025-11-12  8:45 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On Wed, Nov 12, 2025 at 08:33:54AM +0000, Marc Zyngier wrote:
> On Wed, 12 Nov 2025 00:08:37 +0000,
> Oliver Upton <oupton@kernel.org> wrote:
> > 
> > On Sun, Nov 09, 2025 at 05:15:54PM +0000, Marc Zyngier wrote:
> > > +static void summarize_ap_list(struct kvm_vcpu *vcpu,
> > > +			      struct ap_list_summary *als)
> > >  {
> > >  	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> > >  	struct vgic_irq *irq;
> > > -	int count = 0;
> > > -
> > > -	*multi_sgi = false;
> > >  
> > >  	lockdep_assert_held(&vgic_cpu->ap_list_lock);
> > >  
> > > -	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> > > -		int w;
> > > +	*als = (typeof(*als)){};
> > >  
> > > -		raw_spin_lock(&irq->irq_lock);
> > > -		/* GICv2 SGIs can count for more than one... */
> > > -		w = vgic_irq_get_lr_count(irq);
> > > -		raw_spin_unlock(&irq->irq_lock);
> > > +	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> > > +		scoped_guard(raw_spinlock, &irq->irq_lock) {
> > > +			if (vgic_target_oracle(irq) != vcpu)
> > > +				continue;
> > 
> > From our conversation about this sort of thing a few weeks ago, wont
> > this 'continue' interact pooly with the for loop that scoped_guard()
> > expands to?
> 
> Gahhh... I was sure I had killed that everywhere, but obviously failed
> to. I wish there was a coccinelle script to detect this sort of broken
> constructs (where are the script kiddies when you really need them?).
> 
> Thanks for spotting it!
> 
> > Consistent with the other checks against the destination oracle you'll
> > probably want a branch hint too.
> 
> Yup, I'll add that.

I can take care of it when applying. These patches need to bake :)

Thanks,
Oliver


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure
  2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
                   ` (44 preceding siblings ...)
  2025-11-09 17:16 ` [PATCH v2 45/45] KVM: arm64: selftests: vgic_irq: Add timer deactivation test Marc Zyngier
@ 2025-11-12  9:13 ` Oliver Upton
  45 siblings, 0 replies; 83+ messages in thread
From: Oliver Upton @ 2025-11-12  9:13 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm, Marc Zyngier
  Cc: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On Sun, 09 Nov 2025 17:15:34 +0000, Marc Zyngier wrote:
> This is the 2nd version of the series originally posted at [1]. The
> series has significantly evolved with a bunch of bug fixes, some
> additional optimisations, and a number of test cases.
> 
> This has now been extensively tested on much of what I have access to,
> specially on some of the most broken stuff (Apple, Qualcomm, Cavium,
> ARMv8.0 CPUs without TDIR), but also on some less shitty systems
> (which are the minority, unsurprisingly).
> 
> [...]

Applied to next, thanks!

[01/45] irqchip/gic: Add missing GICH_HCR control bits
        https://git.kernel.org/kvmarm/kvmarm/c/435d529bf5cd
[02/45] irqchip/gic: Expose CPU interface VA to KVM
        https://git.kernel.org/kvmarm/kvmarm/c/5bebf839b2e7
[03/45] irqchip/apple-aic: Spit out ICH_MISR_EL2 value on spurious vGIC MI
        https://git.kernel.org/kvmarm/kvmarm/c/5a9655de7241
[04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
        https://git.kernel.org/kvmarm/kvmarm/c/ca30799f7c2d
[05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
        https://git.kernel.org/kvmarm/kvmarm/c/375e16720b4c
[06/45] KVM: arm64: Repack struct vgic_irq fields
        https://git.kernel.org/kvmarm/kvmarm/c/10576b2d8652
[07/45] KVM: arm64: Add tracking of vgic_irq being present in a LR
        https://git.kernel.org/kvmarm/kvmarm/c/023c81ada994
[08/45] KVM: arm64: Add LR overflow handling documentation
        https://git.kernel.org/kvmarm/kvmarm/c/2a69aca33cac
[09/45] KVM: arm64: GICv3: Drop LPI active state when folding LRs
        https://git.kernel.org/kvmarm/kvmarm/c/9fc8456206e8
[10/45] KVM: arm64: GICv3: Preserve EOIcount on exit
        https://git.kernel.org/kvmarm/kvmarm/c/a3a4ca462fdb
[11/45] KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs
        https://git.kernel.org/kvmarm/kvmarm/c/417714763ec1
[12/45] KVM: arm64: GICv3: Extract LR folding primitive
        https://git.kernel.org/kvmarm/kvmarm/c/7f69d30b7024
[13/45] KVM: arm64: GICv3: Extract LR computing primitive
        https://git.kernel.org/kvmarm/kvmarm/c/4b963cd815c0
[14/45] KVM: arm64: GICv2: Preserve EOIcount on exit
        https://git.kernel.org/kvmarm/kvmarm/c/fc2fda4298ba
[15/45] KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded
        https://git.kernel.org/kvmarm/kvmarm/c/8ff2bf028907
[16/45] KVM: arm64: GICv2: Extract LR folding primitive
        https://git.kernel.org/kvmarm/kvmarm/c/a8306bb15afd
[17/45] KVM: arm64: GICv2: Extract LR computing primitive
        https://git.kernel.org/kvmarm/kvmarm/c/d78124a65a03
[18/45] KVM: arm64: Compute vgic state irrespective of the number of interrupts
        https://git.kernel.org/kvmarm/kvmarm/c/f9bb784186be
[19/45] KVM: arm64: Eagerly save VMCR on exit
        https://git.kernel.org/kvmarm/kvmarm/c/d3c2036861c6
[20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration
        https://git.kernel.org/kvmarm/kvmarm/c/877324a1b541
[21/45] KVM: arm64: Turn kvm_vgic_vcpu_enable() into kvm_vgic_vcpu_reset()
        https://git.kernel.org/kvmarm/kvmarm/c/df5dfcad48ca
[22/45] KVM: arm64: Make vgic_target_oracle() globally available
        https://git.kernel.org/kvmarm/kvmarm/c/0cb4b6d1c73e
[23/45] KVM: arm64: Invert ap_list sorting to push active interrupts out
        https://git.kernel.org/kvmarm/kvmarm/c/c6a5d4815634
[24/45] KVM: arm64: Move undeliverable interrupts to the end of ap_list
        https://git.kernel.org/kvmarm/kvmarm/c/7fb79d1a03e6
[25/45] KVM: arm64: Use MI to detect groups being enabled/disabled
        https://git.kernel.org/kvmarm/kvmarm/c/f259e2c758dc
[26/45] KVM: arm64: GICv3: Handle LR overflow when EOImode==0
        https://git.kernel.org/kvmarm/kvmarm/c/81ef83de9440
[27/45] KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps
        https://git.kernel.org/kvmarm/kvmarm/c/f1d440106c3f
[28/45] KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive
        https://git.kernel.org/kvmarm/kvmarm/c/90d527ac928c
[29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
        https://git.kernel.org/kvmarm/kvmarm/c/9a2292c50d1c
[30/45] KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation
        https://git.kernel.org/kvmarm/kvmarm/c/0f64bed159d2
[31/45] KVM: arm64: GICv3: Handle in-LR deactivation when possible
        https://git.kernel.org/kvmarm/kvmarm/c/164dd4bae47b
[32/45] KVM: arm64: GICv3: Avoid broadcast kick on CPUs lacking TDIR
        https://git.kernel.org/kvmarm/kvmarm/c/2b36853d7e58
[33/45] KVM: arm64: GICv2: Handle LR overflow when EOImode==0
        https://git.kernel.org/kvmarm/kvmarm/c/d4537c8d3116
[34/45] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps
        https://git.kernel.org/kvmarm/kvmarm/c/182853c7680a
[35/45] KVM: arm64: GICv2: Always trap GICV_DIR register
        https://git.kernel.org/kvmarm/kvmarm/c/efa05bae8936
[36/45] KVM: arm64: selftests: gic_v3: Add irq group setting helper
        https://git.kernel.org/kvmarm/kvmarm/c/0f0e2b4108b3
[37/45] KVM: arm64: selftests: gic_v3: Disable Group-0 interrupts by default
        https://git.kernel.org/kvmarm/kvmarm/c/eea5d19518f2
[38/45] KVM: arm64: selftests: vgic_irq: Fix GUEST_ASSERT_IAR_EMPTY() helper
        https://git.kernel.org/kvmarm/kvmarm/c/eecb216b2f74
[39/45] KVM: arm64: selftests: vgic_irq: Change configuration before enabling interrupt
        https://git.kernel.org/kvmarm/kvmarm/c/9f3f3ddbc730
[40/45] KVM: arm64: selftests: vgic_irq: Exclude timer-controlled interrupts
        https://git.kernel.org/kvmarm/kvmarm/c/bbaf6ed67c41
[41/45] KVM: arm64: selftests: vgic_irq: Remove LR-bound limitation
        https://git.kernel.org/kvmarm/kvmarm/c/0b7c2f50f3fd
[42/45] KVM: arm64: selftests: vgic_irq: Perform EOImode==1 deactivation in ack order
        https://git.kernel.org/kvmarm/kvmarm/c/d64e3140ab5f
[43/45] KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test
        https://git.kernel.org/kvmarm/kvmarm/c/170b047e15af
[44/45] KVM: arm64: selftests: vgic_irq: Add Group-0 enable test
        https://git.kernel.org/kvmarm/kvmarm/c/2423a2369fc1
[45/45] KVM: arm64: selftests: vgic_irq: Add timer deactivation test
        https://git.kernel.org/kvmarm/kvmarm/c/eaaaaab4b530

--
Best,
Oliver


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration
  2025-11-12  8:45       ` Oliver Upton
@ 2025-11-12  9:56         ` Marc Zyngier
  0 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-12  9:56 UTC (permalink / raw)
  To: Oliver Upton
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On Wed, 12 Nov 2025 08:45:45 +0000,
Oliver Upton <oupton@kernel.org> wrote:
> 
> On Wed, Nov 12, 2025 at 08:33:54AM +0000, Marc Zyngier wrote:
> > On Wed, 12 Nov 2025 00:08:37 +0000,
> > Oliver Upton <oupton@kernel.org> wrote:
> > > 
> > > On Sun, Nov 09, 2025 at 05:15:54PM +0000, Marc Zyngier wrote:
> > > > +static void summarize_ap_list(struct kvm_vcpu *vcpu,
> > > > +			      struct ap_list_summary *als)
> > > >  {
> > > >  	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> > > >  	struct vgic_irq *irq;
> > > > -	int count = 0;
> > > > -
> > > > -	*multi_sgi = false;
> > > >  
> > > >  	lockdep_assert_held(&vgic_cpu->ap_list_lock);
> > > >  
> > > > -	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> > > > -		int w;
> > > > +	*als = (typeof(*als)){};
> > > >  
> > > > -		raw_spin_lock(&irq->irq_lock);
> > > > -		/* GICv2 SGIs can count for more than one... */
> > > > -		w = vgic_irq_get_lr_count(irq);
> > > > -		raw_spin_unlock(&irq->irq_lock);
> > > > +	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
> > > > +		scoped_guard(raw_spinlock, &irq->irq_lock) {
> > > > +			if (vgic_target_oracle(irq) != vcpu)
> > > > +				continue;
> > > 
> > > From our conversation about this sort of thing a few weeks ago, wont
> > > this 'continue' interact pooly with the for loop that scoped_guard()
> > > expands to?
> > 
> > Gahhh... I was sure I had killed that everywhere, but obviously failed
> > to. I wish there was a coccinelle script to detect this sort of broken
> > constructs (where are the script kiddies when you really need them?).
> > 
> > Thanks for spotting it!
> > 
> > > Consistent with the other checks against the destination oracle you'll
> > > probably want a branch hint too.
> > 
> > Yup, I'll add that.
> 
> I can take care of it when applying. These patches need to bake :)

Yes, they do. Here's the current state of additional changes I have
(compile tested only).

Thanks,

	M.

diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index bd67ad1fcad5e..28184582f23d3 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -851,15 +851,15 @@ static void summarize_ap_list(struct kvm_vcpu *vcpu,
 	*als = (typeof(*als)){};
 
 	list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
-		scoped_guard(raw_spinlock, &irq->irq_lock) {
-			if (vgic_target_oracle(irq) != vcpu)
-				continue;
-
-			if (!irq->active)
-				als->nr_pend++;
-			else
-				als->nr_act++;
-		}
+		guard(raw_spinlock)(&irq->irq_lock);
+
+		if (unlikely(vgic_target_oracle(irq) != vcpu))
+			continue;
+
+		if (!irq->active)
+			als->nr_pend++;
+		else
+			als->nr_act++;
 
 		if (irq->intid < VGIC_NR_SGIS)
 			als->nr_sgi++;
@@ -915,8 +915,8 @@ static void summarize_ap_list(struct kvm_vcpu *vcpu,
  *
  *      - deactivation can happen in any order, and we cannot rely on
  *	  EOImode=0's coupling of priority-drop and deactivation which
- *	  imposes strict reverse Ack order. This means that DIR must be set
- *	  if we have active interrupts outside of the LRs.
+ *	  imposes strict reverse Ack order. This means that DIR must
+ *	  trap if we have active interrupts outside of the LRs.
  *
  *      - deactivation of SPIs can occur on any CPU, while the SPI is only
  *	  present in the ap_list of the CPU that actually ack-ed it. In that

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


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
  2025-11-10 10:40   ` Suzuki K Poulose
  2025-11-11 23:53   ` Oliver Upton
@ 2025-11-13  9:52   ` Marek Szyprowski
  2025-11-13 10:56     ` Marc Zyngier
  2025-11-13 10:59     ` Marc Zyngier
  2025-11-13 18:01   ` Mark Brown
  3 siblings, 2 replies; 83+ messages in thread
From: Marek Szyprowski @ 2025-11-13  9:52 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Christoffer Dall, Volodymyr Babchuk, Yao Yuan

On 09.11.2025 18:15, Marc Zyngier wrote:
> The trap bits are currently only set to manage CPU errata. However,
> we are about to make use of them for purposes beyond beating broken
> CPUs into submission.
>
> For this purpose, turn these errata-driven bits into a patched-in
> constant that is merged with the KVM-driven value at the point of
> programming the ICH_HCR_EL2 register, rather than being directly
> stored with with the shadow value..
>
> This allows the KVM code to distinguish between a trap being handled
> for the purpose of an erratum workaround, or for KVM's own need.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>

This patch landed in today's linux-next as commit ca30799f7c2d ("KVM: 
arm64: Turn vgic-v3 errata traps into a patched-in constant"). In my 
tests I found that it triggers oops and breaks booting on Raspberry Pi5 
and Amlogic SM1 based boards: Odroid-C4 and Khadas VIM3l. Here is the 
failure log:

alternatives: applying system-wide alternatives
Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
Modules linked in:
CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11665 
PREEMPT
Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : vgic_v3_broken_seis+0x14/0x44
lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
...
Call trace:
  vgic_v3_broken_seis+0x14/0x44 (P)
  __apply_alternatives+0x1b4/0x200
  __apply_alternatives_multi_stop+0xac/0xc8
  multi_cpu_stop+0x90/0x178
  cpu_stopper_thread+0x8c/0x11c
  smpboot_thread_fn+0x160/0x32c
  kthread+0x150/0x228
  ret_from_fork+0x10/0x20
Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
---[ end trace 0000000000000000 ]---
note: migration/0[18] exited with irqs disabled
note: migration/0[18] exited with preempt_count 1
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu:     1-...0: (7 ticks this GP) idle=0124/1/0x4000000000000000 
softirq=9/10 fqs=3250
rcu:     2-...0: (7 ticks this GP) idle=0154/1/0x4000000000000000 
softirq=9/10 fqs=3250
rcu:     3-...0: (7 ticks this GP) idle=018c/1/0x4000000000000000 
softirq=9/10 fqs=3250
rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
Sending NMI from CPU 0 to CPUs 1:
Sending NMI from CPU 0 to CPUs 2:
Sending NMI from CPU 0 to CPUs 3:

Let me know how I can help in debugging this issue.


> ---
>   arch/arm64/kernel/image-vars.h       |  1 +
>   arch/arm64/kvm/hyp/vgic-v3-sr.c      | 21 +++++---
>   arch/arm64/kvm/vgic/vgic-v3-nested.c |  9 ----
>   arch/arm64/kvm/vgic/vgic-v3.c        | 81 +++++++++++++++++-----------
>   arch/arm64/kvm/vgic/vgic.h           | 16 ++++++
>   5 files changed, 82 insertions(+), 46 deletions(-)
>
> ...

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland



^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13  9:52   ` Marek Szyprowski
@ 2025-11-13 10:56     ` Marc Zyngier
  2025-11-13 11:04       ` Marek Szyprowski
  2025-11-13 10:59     ` Marc Zyngier
  1 sibling, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-13 10:56 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

Hi Marek,

On Thu, 13 Nov 2025 09:52:23 +0000,
Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> 
> On 09.11.2025 18:15, Marc Zyngier wrote:
> > The trap bits are currently only set to manage CPU errata. However,
> > we are about to make use of them for purposes beyond beating broken
> > CPUs into submission.
> >
> > For this purpose, turn these errata-driven bits into a patched-in
> > constant that is merged with the KVM-driven value at the point of
> > programming the ICH_HCR_EL2 register, rather than being directly
> > stored with with the shadow value..
> >
> > This allows the KVM code to distinguish between a trap being handled
> > for the purpose of an erratum workaround, or for KVM's own need.
> >
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> 
> This patch landed in today's linux-next as commit ca30799f7c2d ("KVM: 
> arm64: Turn vgic-v3 errata traps into a patched-in constant"). In my 
> tests I found that it triggers oops and breaks booting on Raspberry Pi5 
> and Amlogic SM1 based boards: Odroid-C4 and Khadas VIM3l. Here is the 
> failure log:
> 
> alternatives: applying system-wide alternatives
> Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
> Modules linked in:
> CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11665 
> PREEMPT
> Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
> Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
> pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
> pc : vgic_v3_broken_seis+0x14/0x44
> lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
> ...
> Call trace:
>   vgic_v3_broken_seis+0x14/0x44 (P)
>   __apply_alternatives+0x1b4/0x200
>   __apply_alternatives_multi_stop+0xac/0xc8
>   multi_cpu_stop+0x90/0x178
>   cpu_stopper_thread+0x8c/0x11c
>   smpboot_thread_fn+0x160/0x32c
>   kthread+0x150/0x228
>   ret_from_fork+0x10/0x20
> Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
> ---[ end trace 0000000000000000 ]---
> note: migration/0[18] exited with irqs disabled
> note: migration/0[18] exited with preempt_count 1
> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
> rcu:     1-...0: (7 ticks this GP) idle=0124/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     2-...0: (7 ticks this GP) idle=0154/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     3-...0: (7 ticks this GP) idle=018c/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
> Sending NMI from CPU 0 to CPUs 1:
> Sending NMI from CPU 0 to CPUs 2:
> Sending NMI from CPU 0 to CPUs 3:
> 
> Let me know how I can help in debugging this issue.

I think the common thing between these machines is that although they
run VHE, they are stuck with a GICv2, and should never get to this
code path.

Can you dump the kernel log until this point? Something must be
screwed in the detection logic.

Thanks,

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13  9:52   ` Marek Szyprowski
  2025-11-13 10:56     ` Marc Zyngier
@ 2025-11-13 10:59     ` Marc Zyngier
  2025-11-13 11:20       ` Marek Szyprowski
  1 sibling, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-13 10:59 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On Thu, 13 Nov 2025 09:52:23 +0000,
Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> 
> On 09.11.2025 18:15, Marc Zyngier wrote:
> > The trap bits are currently only set to manage CPU errata. However,
> > we are about to make use of them for purposes beyond beating broken
> > CPUs into submission.
> >
> > For this purpose, turn these errata-driven bits into a patched-in
> > constant that is merged with the KVM-driven value at the point of
> > programming the ICH_HCR_EL2 register, rather than being directly
> > stored with with the shadow value..
> >
> > This allows the KVM code to distinguish between a trap being handled
> > for the purpose of an erratum workaround, or for KVM's own need.
> >
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> 
> This patch landed in today's linux-next as commit ca30799f7c2d ("KVM: 
> arm64: Turn vgic-v3 errata traps into a patched-in constant"). In my 
> tests I found that it triggers oops and breaks booting on Raspberry Pi5 
> and Amlogic SM1 based boards: Odroid-C4 and Khadas VIM3l. Here is the 
> failure log:
> 
> alternatives: applying system-wide alternatives
> Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
> Modules linked in:
> CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11665 
> PREEMPT
> Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
> Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
> pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
> pc : vgic_v3_broken_seis+0x14/0x44
> lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
> ...
> Call trace:
>   vgic_v3_broken_seis+0x14/0x44 (P)
>   __apply_alternatives+0x1b4/0x200
>   __apply_alternatives_multi_stop+0xac/0xc8
>   multi_cpu_stop+0x90/0x178
>   cpu_stopper_thread+0x8c/0x11c
>   smpboot_thread_fn+0x160/0x32c
>   kthread+0x150/0x228
>   ret_from_fork+0x10/0x20
> Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
> ---[ end trace 0000000000000000 ]---
> note: migration/0[18] exited with irqs disabled
> note: migration/0[18] exited with preempt_count 1
> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
> rcu:     1-...0: (7 ticks this GP) idle=0124/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     2-...0: (7 ticks this GP) idle=0154/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     3-...0: (7 ticks this GP) idle=018c/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
> Sending NMI from CPU 0 to CPUs 1:
> Sending NMI from CPU 0 to CPUs 2:
> Sending NMI from CPU 0 to CPUs 3:
> 
> Let me know how I can help in debugging this issue.

Wild guess. Can you try this (untested)?

	M.

diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index fc7a4cb8e231d..598621b14a30d 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -829,8 +829,8 @@ static const struct midr_range broken_seis[] = {
 static bool vgic_v3_broken_seis(void)
 {
 	return (is_kernel_in_hyp_mode() &&
-		(read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS) &&
-		is_midr_in_range_list(broken_seis));
+		is_midr_in_range_list(broken_seis) &&
+		(read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS));
 }
 
 void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,

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


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13 10:56     ` Marc Zyngier
@ 2025-11-13 11:04       ` Marek Szyprowski
  2025-11-13 11:23         ` Joey Gouly
  0 siblings, 1 reply; 83+ messages in thread
From: Marek Szyprowski @ 2025-11-13 11:04 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On 13.11.2025 11:56, Marc Zyngier wrote:
> Hi Marek,
>
> On Thu, 13 Nov 2025 09:52:23 +0000,
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> On 09.11.2025 18:15, Marc Zyngier wrote:
>>> The trap bits are currently only set to manage CPU errata. However,
>>> we are about to make use of them for purposes beyond beating broken
>>> CPUs into submission.
>>>
>>> For this purpose, turn these errata-driven bits into a patched-in
>>> constant that is merged with the KVM-driven value at the point of
>>> programming the ICH_HCR_EL2 register, rather than being directly
>>> stored with with the shadow value..
>>>
>>> This allows the KVM code to distinguish between a trap being handled
>>> for the purpose of an erratum workaround, or for KVM's own need.
>>>
>>> Signed-off-by: Marc Zyngier <maz@kernel.org>
>> This patch landed in today's linux-next as commit ca30799f7c2d ("KVM:
>> arm64: Turn vgic-v3 errata traps into a patched-in constant"). In my
>> tests I found that it triggers oops and breaks booting on Raspberry Pi5
>> and Amlogic SM1 based boards: Odroid-C4 and Khadas VIM3l. Here is the
>> failure log:
>>
>> alternatives: applying system-wide alternatives
>> Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
>> Modules linked in:
>> CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11665
>> PREEMPT
>> Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
>> Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
>> pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
>> pc : vgic_v3_broken_seis+0x14/0x44
>> lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
>> ...
>> Call trace:
>>    vgic_v3_broken_seis+0x14/0x44 (P)
>>    __apply_alternatives+0x1b4/0x200
>>    __apply_alternatives_multi_stop+0xac/0xc8
>>    multi_cpu_stop+0x90/0x178
>>    cpu_stopper_thread+0x8c/0x11c
>>    smpboot_thread_fn+0x160/0x32c
>>    kthread+0x150/0x228
>>    ret_from_fork+0x10/0x20
>> Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
>> ---[ end trace 0000000000000000 ]---
>> note: migration/0[18] exited with irqs disabled
>> note: migration/0[18] exited with preempt_count 1
>> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
>> rcu:     1-...0: (7 ticks this GP) idle=0124/1/0x4000000000000000
>> softirq=9/10 fqs=3250
>> rcu:     2-...0: (7 ticks this GP) idle=0154/1/0x4000000000000000
>> softirq=9/10 fqs=3250
>> rcu:     3-...0: (7 ticks this GP) idle=018c/1/0x4000000000000000
>> softirq=9/10 fqs=3250
>> rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
>> Sending NMI from CPU 0 to CPUs 1:
>> Sending NMI from CPU 0 to CPUs 2:
>> Sending NMI from CPU 0 to CPUs 3:
>>
>> Let me know how I can help in debugging this issue.
> I think the common thing between these machines is that although they
> run VHE, they are stuck with a GICv2, and should never get to this
> code path.
>
> Can you dump the kernel log until this point? Something must be
> screwed in the detection logic.

Here is a complete boot log:

Booting Linux on physical CPU 0x0000000000 [0x414fd0b1]
Linux version 6.18.0-rc3+ (m.szyprowski@AMDC4653) (aarch64-linux-gnu-gcc 
(Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 
2.38) #11666 SMP PREEMPT Thu Nov 13 11:58:18 CET 2025
KASLR enabled
Machine model: Raspberry Pi 5 Model B Rev 1.0
earlycon: pl11 at MMIO 0x000000107d001000 (options '115200n8')
printk: legacy bootconsole [pl11] enabled
printk: debug: ignoring loglevel setting.
Reserved memory: created CMA memory pool at 0x000000003bc00000, size 64 MiB
OF: reserved mem: initialized node linux,cma, compatible id shared-dma-pool
OF: reserved mem: 0x000000003bc00000..0x000000003fbfffff (65536 KiB) map 
reusable linux,cma
OF: reserved mem: 0x0000000000000000..0x000000000007ffff (512 KiB) nomap 
non-reusable atf@0
NUMA: Faking a node at [mem 0x0000000000000000-0x00000001ffffffff]
NODE_DATA(0) allocated [mem 0x1fefe4a00-0x1fefe763f]
Zone ranges:
   DMA      [mem 0x0000000000000000-0x00000000ffffffff]
   DMA32    empty
   Normal   [mem 0x0000000100000000-0x00000001ffffffff]
Movable zone start for each node
Early memory node ranges
   node   0: [mem 0x0000000000000000-0x000000000007ffff]
   node   0: [mem 0x0000000000080000-0x000000003fbfffff]
   node   0: [mem 0x0000000040000000-0x00000001ffffffff]
Initmem setup node 0 [mem 0x0000000000000000-0x00000001ffffffff]
On node 0, zone DMA: 1024 pages in unavailable ranges
psci: probing for conduit method from DT.
psci: PSCIv1.1 detected in firmware.
psci: Using standard PSCI v0.2 function IDs
psci: MIGRATE_INFO_TYPE not supported.
psci: SMC Calling Convention v1.2
percpu: Embedded 35 pages/cpu s104336 r8192 d30832 u143360
pcpu-alloc: s104336 r8192 d30832 u143360 alloc=35*4096
pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3
Detected PIPT I-cache on CPU0
CPU features: detected: Virtualization Host Extensions
CPU features: detected: Spectre-v4
CPU features: detected: Spectre-BHB
CPU features: kernel page table isolation forced ON by KASLR
CPU features: detected: Kernel page table isolation (KPTI)
CPU features: detected: SSBS not fully self-synchronizing
alternatives: applying boot alternatives
Kernel command line: console=ttyAMA10,115200n8 earlycon 
root=PARTUUID=11111111-03 rw clk_ignore_unused rootdelay=2 
ignore_loglevel earlycon
printk: log buffer data + meta data: 131072 + 458752 = 589824 bytes
Dentry cache hash table entries: 1048576 (order: 11, 8388608 bytes, linear)
Inode-cache hash table entries: 524288 (order: 10, 4194304 bytes, linear)
software IO TLB: area num 4.
software IO TLB: mapped [mem 0x00000000fbfff000-0x00000000fffff000] (64MB)
Fallback order for Node 0: 0
Built 1 zonelists, mobility grouping on.  Total pages: 2096128
Policy zone: Normal
mem auto-init: stack:off, heap alloc:off, heap free:off
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
Running RCU self tests
Running RCU synchronous self tests
rcu: Preemptible hierarchical RCU implementation.
rcu:     RCU event tracing is enabled.
rcu:     RCU lockdep checking is enabled.
rcu:     RCU restricting CPUs from NR_CPUS=512 to nr_cpu_ids=4.
  Trampoline variant of Tasks RCU enabled.
  Tracing variant of Tasks RCU enabled.
rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
Running RCU synchronous self tests
RCU Tasks: Setting shift to 2 and lim to 1 rcu_task_cb_adjust=1 
rcu_task_cpu_ids=4.
RCU Tasks Trace: Setting shift to 2 and lim to 1 rcu_task_cb_adjust=1 
rcu_task_cpu_ids=4.
NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
Root IRQ handler: gic_handle_irq
GIC: Using split EOI/Deactivate mode
rcu: srcu_init: Setting srcu_struct sizes based on contention.
arch_timer: cp15 timer running at 54.00MHz (phys).
clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 
0xc743ce346, max_idle_ns: 440795203123 ns
sched_clock: 56 bits at 54MHz, resolution 18ns, wraps every 4398046511102ns
Console: colour dummy device 80x25
Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar
... MAX_LOCKDEP_SUBCLASSES:  8
... MAX_LOCK_DEPTH:          48
... MAX_LOCKDEP_KEYS:        8192
... CLASSHASH_SIZE:          4096
... MAX_LOCKDEP_ENTRIES:     32768
... MAX_LOCKDEP_CHAINS:      65536
... CHAINHASH_SIZE:          32768
  memory used by lock dependency info: 6429 kB
  memory used for stack traces: 4224 kB
  per task-struct memory footprint: 1920 bytes
Calibrating delay loop (skipped), value calculated using timer 
frequency.. 108.00 BogoMIPS (lpj=216000)
pid_max: default: 32768 minimum: 301
LSM: initializing lsm=capability
Mount-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
Mountpoint-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
Running RCU synchronous self tests
Running RCU synchronous self tests
rcu: Hierarchical SRCU implementation.
rcu:     Max phase no-delay instances is 1000.
Timer migration: 1 hierarchy levels; 8 children per group; 1 crossnode level
smp: Bringing up secondary CPUs ...
Detected PIPT I-cache on CPU1
CPU1: Booted secondary processor 0x0000000100 [0x414fd0b1]
Detected PIPT I-cache on CPU2
CPU2: Booted secondary processor 0x0000000200 [0x414fd0b1]
Detected PIPT I-cache on CPU3
CPU3: Booted secondary processor 0x0000000300 [0x414fd0b1]
smp: Brought up 1 node, 4 CPUs
SMP: Total of 4 processors activated.
CPU: All CPU(s) started at EL2
CPU features: detected: 32-bit EL0 Support
CPU features: detected: Data cache clean to the PoU not required for I/D 
coherence
CPU features: detected: Common not Private translations
CPU features: detected: CRC32 instructions
CPU features: detected: RCpc load-acquire (LDAPR)
CPU features: detected: LSE atomic instructions
CPU features: detected: Privileged Access Never
CPU features: detected: PMUv3
CPU features: detected: RAS Extension Support
CPU features: detected: Speculative Store Bypassing Safe (SSBS)
alternatives: applying system-wide alternatives
Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
Modules linked in:
CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11666 
PREEMPT
Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : vgic_v3_broken_seis+0x14/0x44
lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
sp : ffff8000800b3c90
x29: ffff8000800b3c90 x28: ffff8000800b3d58 x27: ffffa07c4e48dd28
x26: 0000000000000001 x25: ffffa07c50461000 x24: ffffa07c50460000
x23: ffffa07c51864000 x22: ffffa07c4e48dd28 x21: ffff00000028dd28
x20: ffffa07c51864598 x19: ffffa07c505b2e10 x18: 00000000ffffffff
x17: 6465726975716572 x16: 20746f6e20556f50 x15: 0000000000000001
x14: 0000000000000000 x13: 0000000000000000 x12: ffff000100292520
x11: ffffa07c51f7e620 x10: 0000000000000000 x9 : 0000000000000001
x8 : ffff8000800b3c98 x7 : ffff8000800b3d60 x6 : ffffa07c5060dc28
x5 : 0000000000000001 x4 : ffffa07c4f7645cc x3 : 0000000000000001
x2 : ffff00000028dd28 x1 : 0000000000000008 x0 : 0000000000000000
Call trace:
  vgic_v3_broken_seis+0x14/0x44 (P)
  __apply_alternatives+0x1b4/0x200
  __apply_alternatives_multi_stop+0xac/0xc8
  multi_cpu_stop+0x90/0x178
  cpu_stopper_thread+0x8c/0x11c
  smpboot_thread_fn+0x160/0x32c
  kthread+0x150/0x228
  ret_from_fork+0x10/0x20
Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
---[ end trace 0000000000000000 ]---
note: migration/0[18] exited with irqs disabled
note: migration/0[18] exited with preempt_count 1
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu:     1-...0: (7 ticks this GP) idle=0194/1/0x4000000000000000 
softirq=9/10 fqs=3250
rcu:     2-...0: (7 ticks this GP) idle=00c4/1/0x4000000000000000 
softirq=9/10 fqs=3250
rcu:     3-...0: (7 ticks this GP) idle=01bc/1/0x4000000000000000 
softirq=9/10 fqs=3250
rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
Sending NMI from CPU 0 to CPUs 1:
Sending NMI from CPU 0 to CPUs 2:
Sending NMI from CPU 0 to CPUs 3:

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland



^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13 10:59     ` Marc Zyngier
@ 2025-11-13 11:20       ` Marek Szyprowski
  0 siblings, 0 replies; 83+ messages in thread
From: Marek Szyprowski @ 2025-11-13 11:20 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On 13.11.2025 11:59, Marc Zyngier wrote:
> On Thu, 13 Nov 2025 09:52:23 +0000,
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> On 09.11.2025 18:15, Marc Zyngier wrote:
>>> The trap bits are currently only set to manage CPU errata. However,
>>> we are about to make use of them for purposes beyond beating broken
>>> CPUs into submission.
>>>
>>> For this purpose, turn these errata-driven bits into a patched-in
>>> constant that is merged with the KVM-driven value at the point of
>>> programming the ICH_HCR_EL2 register, rather than being directly
>>> stored with with the shadow value..
>>>
>>> This allows the KVM code to distinguish between a trap being handled
>>> for the purpose of an erratum workaround, or for KVM's own need.
>>>
>>> Signed-off-by: Marc Zyngier <maz@kernel.org>
>> This patch landed in today's linux-next as commit ca30799f7c2d ("KVM:
>> arm64: Turn vgic-v3 errata traps into a patched-in constant"). In my
>> tests I found that it triggers oops and breaks booting on Raspberry Pi5
>> and Amlogic SM1 based boards: Odroid-C4 and Khadas VIM3l. Here is the
>> failure log:
>>
>> alternatives: applying system-wide alternatives
>> Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
>> Modules linked in:
>> CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11665
>> PREEMPT
>> Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
>> Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
>> pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
>> pc : vgic_v3_broken_seis+0x14/0x44
>> lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
>> ...
>> Call trace:
>>    vgic_v3_broken_seis+0x14/0x44 (P)
>>    __apply_alternatives+0x1b4/0x200
>>    __apply_alternatives_multi_stop+0xac/0xc8
>>    multi_cpu_stop+0x90/0x178
>>    cpu_stopper_thread+0x8c/0x11c
>>    smpboot_thread_fn+0x160/0x32c
>>    kthread+0x150/0x228
>>    ret_from_fork+0x10/0x20
>> Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
>> ---[ end trace 0000000000000000 ]---
>> note: migration/0[18] exited with irqs disabled
>> note: migration/0[18] exited with preempt_count 1
>> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
>> rcu:     1-...0: (7 ticks this GP) idle=0124/1/0x4000000000000000
>> softirq=9/10 fqs=3250
>> rcu:     2-...0: (7 ticks this GP) idle=0154/1/0x4000000000000000
>> softirq=9/10 fqs=3250
>> rcu:     3-...0: (7 ticks this GP) idle=018c/1/0x4000000000000000
>> softirq=9/10 fqs=3250
>> rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
>> Sending NMI from CPU 0 to CPUs 1:
>> Sending NMI from CPU 0 to CPUs 2:
>> Sending NMI from CPU 0 to CPUs 3:
>>
>> Let me know how I can help in debugging this issue.
> Wild guess. Can you try this (untested)?
>
> 	M.
>
> diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> index fc7a4cb8e231d..598621b14a30d 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> @@ -829,8 +829,8 @@ static const struct midr_range broken_seis[] = {
>   static bool vgic_v3_broken_seis(void)
>   {
>   	return (is_kernel_in_hyp_mode() &&
> -		(read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS) &&
> -		is_midr_in_range_list(broken_seis));
> +		is_midr_in_range_list(broken_seis) &&
> +		(read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS));
>   }
>   
>   void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,

That's it! Now it boots fine on the mentioned boards. Feel free to add:

Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland



^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13 11:04       ` Marek Szyprowski
@ 2025-11-13 11:23         ` Joey Gouly
  2025-11-13 11:42           ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Joey Gouly @ 2025-11-13 11:23 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Marc Zyngier, kvmarm, linux-arm-kernel, kvm, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On Thu, Nov 13, 2025 at 12:04:46PM +0100, Marek Szyprowski wrote:
> On 13.11.2025 11:56, Marc Zyngier wrote:
> > Hi Marek,
> >
> > On Thu, 13 Nov 2025 09:52:23 +0000,
> > Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> >> On 09.11.2025 18:15, Marc Zyngier wrote:
> >>> The trap bits are currently only set to manage CPU errata. However,
> >>> we are about to make use of them for purposes beyond beating broken
> >>> CPUs into submission.
> >>>
> >>> For this purpose, turn these errata-driven bits into a patched-in
> >>> constant that is merged with the KVM-driven value at the point of
> >>> programming the ICH_HCR_EL2 register, rather than being directly
> >>> stored with with the shadow value..
> >>>
> >>> This allows the KVM code to distinguish between a trap being handled
> >>> for the purpose of an erratum workaround, or for KVM's own need.
> >>>
> >>> Signed-off-by: Marc Zyngier <maz@kernel.org>
> >> This patch landed in today's linux-next as commit ca30799f7c2d ("KVM:
> >> arm64: Turn vgic-v3 errata traps into a patched-in constant"). In my
> >> tests I found that it triggers oops and breaks booting on Raspberry Pi5
> >> and Amlogic SM1 based boards: Odroid-C4 and Khadas VIM3l. Here is the
> >> failure log:
> >>
> >> alternatives: applying system-wide alternatives
> >> Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
> >> Modules linked in:
> >> CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11665
> >> PREEMPT
> >> Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
> >> Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
> >> pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
> >> pc : vgic_v3_broken_seis+0x14/0x44
> >> lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
> >> ...
> >> Call trace:
> >>    vgic_v3_broken_seis+0x14/0x44 (P)
> >>    __apply_alternatives+0x1b4/0x200
> >>    __apply_alternatives_multi_stop+0xac/0xc8
> >>    multi_cpu_stop+0x90/0x178
> >>    cpu_stopper_thread+0x8c/0x11c
> >>    smpboot_thread_fn+0x160/0x32c
> >>    kthread+0x150/0x228
> >>    ret_from_fork+0x10/0x20
> >> Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
> >> ---[ end trace 0000000000000000 ]---
> >> note: migration/0[18] exited with irqs disabled
> >> note: migration/0[18] exited with preempt_count 1
> >> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
> >> rcu:     1-...0: (7 ticks this GP) idle=0124/1/0x4000000000000000
> >> softirq=9/10 fqs=3250
> >> rcu:     2-...0: (7 ticks this GP) idle=0154/1/0x4000000000000000
> >> softirq=9/10 fqs=3250
> >> rcu:     3-...0: (7 ticks this GP) idle=018c/1/0x4000000000000000
> >> softirq=9/10 fqs=3250
> >> rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
> >> Sending NMI from CPU 0 to CPUs 1:
> >> Sending NMI from CPU 0 to CPUs 2:
> >> Sending NMI from CPU 0 to CPUs 3:
> >>
> >> Let me know how I can help in debugging this issue.
> > I think the common thing between these machines is that although they
> > run VHE, they are stuck with a GICv2, and should never get to this
> > code path.
> >
> > Can you dump the kernel log until this point? Something must be
> > screwed in the detection logic.
> 
> Here is a complete boot log:
> 
> Booting Linux on physical CPU 0x0000000000 [0x414fd0b1]
> Linux version 6.18.0-rc3+ (m.szyprowski@AMDC4653) (aarch64-linux-gnu-gcc 
> (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 
> 2.38) #11666 SMP PREEMPT Thu Nov 13 11:58:18 CET 2025
> KASLR enabled
> Machine model: Raspberry Pi 5 Model B Rev 1.0
> earlycon: pl11 at MMIO 0x000000107d001000 (options '115200n8')
> printk: legacy bootconsole [pl11] enabled
> printk: debug: ignoring loglevel setting.
> Reserved memory: created CMA memory pool at 0x000000003bc00000, size 64 MiB
> OF: reserved mem: initialized node linux,cma, compatible id shared-dma-pool
> OF: reserved mem: 0x000000003bc00000..0x000000003fbfffff (65536 KiB) map 
> reusable linux,cma
> OF: reserved mem: 0x0000000000000000..0x000000000007ffff (512 KiB) nomap 
> non-reusable atf@0
> NUMA: Faking a node at [mem 0x0000000000000000-0x00000001ffffffff]
> NODE_DATA(0) allocated [mem 0x1fefe4a00-0x1fefe763f]
> Zone ranges:
>    DMA      [mem 0x0000000000000000-0x00000000ffffffff]
>    DMA32    empty
>    Normal   [mem 0x0000000100000000-0x00000001ffffffff]
> Movable zone start for each node
> Early memory node ranges
>    node   0: [mem 0x0000000000000000-0x000000000007ffff]
>    node   0: [mem 0x0000000000080000-0x000000003fbfffff]
>    node   0: [mem 0x0000000040000000-0x00000001ffffffff]
> Initmem setup node 0 [mem 0x0000000000000000-0x00000001ffffffff]
> On node 0, zone DMA: 1024 pages in unavailable ranges
> psci: probing for conduit method from DT.
> psci: PSCIv1.1 detected in firmware.
> psci: Using standard PSCI v0.2 function IDs
> psci: MIGRATE_INFO_TYPE not supported.
> psci: SMC Calling Convention v1.2
> percpu: Embedded 35 pages/cpu s104336 r8192 d30832 u143360
> pcpu-alloc: s104336 r8192 d30832 u143360 alloc=35*4096
> pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3
> Detected PIPT I-cache on CPU0
> CPU features: detected: Virtualization Host Extensions
> CPU features: detected: Spectre-v4
> CPU features: detected: Spectre-BHB
> CPU features: kernel page table isolation forced ON by KASLR
> CPU features: detected: Kernel page table isolation (KPTI)
> CPU features: detected: SSBS not fully self-synchronizing
> alternatives: applying boot alternatives
> Kernel command line: console=ttyAMA10,115200n8 earlycon 
> root=PARTUUID=11111111-03 rw clk_ignore_unused rootdelay=2 
> ignore_loglevel earlycon
> printk: log buffer data + meta data: 131072 + 458752 = 589824 bytes
> Dentry cache hash table entries: 1048576 (order: 11, 8388608 bytes, linear)
> Inode-cache hash table entries: 524288 (order: 10, 4194304 bytes, linear)
> software IO TLB: area num 4.
> software IO TLB: mapped [mem 0x00000000fbfff000-0x00000000fffff000] (64MB)
> Fallback order for Node 0: 0
> Built 1 zonelists, mobility grouping on.  Total pages: 2096128
> Policy zone: Normal
> mem auto-init: stack:off, heap alloc:off, heap free:off
> SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
> Running RCU self tests
> Running RCU synchronous self tests
> rcu: Preemptible hierarchical RCU implementation.
> rcu:     RCU event tracing is enabled.
> rcu:     RCU lockdep checking is enabled.
> rcu:     RCU restricting CPUs from NR_CPUS=512 to nr_cpu_ids=4.
>   Trampoline variant of Tasks RCU enabled.
>   Tracing variant of Tasks RCU enabled.
> rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
> rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
> Running RCU synchronous self tests
> RCU Tasks: Setting shift to 2 and lim to 1 rcu_task_cb_adjust=1 
> rcu_task_cpu_ids=4.
> RCU Tasks Trace: Setting shift to 2 and lim to 1 rcu_task_cb_adjust=1 
> rcu_task_cpu_ids=4.
> NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
> Root IRQ handler: gic_handle_irq
> GIC: Using split EOI/Deactivate mode
> rcu: srcu_init: Setting srcu_struct sizes based on contention.
> arch_timer: cp15 timer running at 54.00MHz (phys).
> clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 
> 0xc743ce346, max_idle_ns: 440795203123 ns
> sched_clock: 56 bits at 54MHz, resolution 18ns, wraps every 4398046511102ns
> Console: colour dummy device 80x25
> Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar
> ... MAX_LOCKDEP_SUBCLASSES:  8
> ... MAX_LOCK_DEPTH:          48
> ... MAX_LOCKDEP_KEYS:        8192
> ... CLASSHASH_SIZE:          4096
> ... MAX_LOCKDEP_ENTRIES:     32768
> ... MAX_LOCKDEP_CHAINS:      65536
> ... CHAINHASH_SIZE:          32768
>   memory used by lock dependency info: 6429 kB
>   memory used for stack traces: 4224 kB
>   per task-struct memory footprint: 1920 bytes
> Calibrating delay loop (skipped), value calculated using timer 
> frequency.. 108.00 BogoMIPS (lpj=216000)
> pid_max: default: 32768 minimum: 301
> LSM: initializing lsm=capability
> Mount-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
> Mountpoint-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
> Running RCU synchronous self tests
> Running RCU synchronous self tests
> rcu: Hierarchical SRCU implementation.
> rcu:     Max phase no-delay instances is 1000.
> Timer migration: 1 hierarchy levels; 8 children per group; 1 crossnode level
> smp: Bringing up secondary CPUs ...
> Detected PIPT I-cache on CPU1
> CPU1: Booted secondary processor 0x0000000100 [0x414fd0b1]
> Detected PIPT I-cache on CPU2
> CPU2: Booted secondary processor 0x0000000200 [0x414fd0b1]
> Detected PIPT I-cache on CPU3
> CPU3: Booted secondary processor 0x0000000300 [0x414fd0b1]
> smp: Brought up 1 node, 4 CPUs
> SMP: Total of 4 processors activated.
> CPU: All CPU(s) started at EL2
> CPU features: detected: 32-bit EL0 Support
> CPU features: detected: Data cache clean to the PoU not required for I/D 
> coherence
> CPU features: detected: Common not Private translations
> CPU features: detected: CRC32 instructions
> CPU features: detected: RCpc load-acquire (LDAPR)
> CPU features: detected: LSE atomic instructions
> CPU features: detected: Privileged Access Never
> CPU features: detected: PMUv3
> CPU features: detected: RAS Extension Support
> CPU features: detected: Speculative Store Bypassing Safe (SSBS)
> alternatives: applying system-wide alternatives
> Internal error: Oops - Undefined instruction: 0000000002000000 [#1]  SMP
> Modules linked in:
> CPU: 0 UID: 0 PID: 18 Comm: migration/0 Not tainted 6.18.0-rc3+ #11666 
> PREEMPT
> Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
> Stopper: multi_cpu_stop+0x0/0x178 <- __stop_cpus.constprop.0+0x7c/0xc8
> pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
> pc : vgic_v3_broken_seis+0x14/0x44
> lr : kvm_compute_ich_hcr_trap_bits+0x48/0xd8
> sp : ffff8000800b3c90
> x29: ffff8000800b3c90 x28: ffff8000800b3d58 x27: ffffa07c4e48dd28
> x26: 0000000000000001 x25: ffffa07c50461000 x24: ffffa07c50460000
> x23: ffffa07c51864000 x22: ffffa07c4e48dd28 x21: ffff00000028dd28
> x20: ffffa07c51864598 x19: ffffa07c505b2e10 x18: 00000000ffffffff
> x17: 6465726975716572 x16: 20746f6e20556f50 x15: 0000000000000001
> x14: 0000000000000000 x13: 0000000000000000 x12: ffff000100292520
> x11: ffffa07c51f7e620 x10: 0000000000000000 x9 : 0000000000000001
> x8 : ffff8000800b3c98 x7 : ffff8000800b3d60 x6 : ffffa07c5060dc28
> x5 : 0000000000000001 x4 : ffffa07c4f7645cc x3 : 0000000000000001
> x2 : ffff00000028dd28 x1 : 0000000000000008 x0 : 0000000000000000
> Call trace:
>   vgic_v3_broken_seis+0x14/0x44 (P)
>   __apply_alternatives+0x1b4/0x200
>   __apply_alternatives_multi_stop+0xac/0xc8
>   multi_cpu_stop+0x90/0x178
>   cpu_stopper_thread+0x8c/0x11c
>   smpboot_thread_fn+0x160/0x32c
>   kthread+0x150/0x228
>   ret_from_fork+0x10/0x20
> Code: 52800000 f100203f 54000040 d65f03c0 (d53ccb21)
> ---[ end trace 0000000000000000 ]---
> note: migration/0[18] exited with irqs disabled
> note: migration/0[18] exited with preempt_count 1
> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
> rcu:     1-...0: (7 ticks this GP) idle=0194/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     2-...0: (7 ticks this GP) idle=00c4/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     3-...0: (7 ticks this GP) idle=01bc/1/0x4000000000000000 
> softirq=9/10 fqs=3250
> rcu:     (detected by 0, t=6502 jiffies, g=-1179, q=2 ncpus=4)
> Sending NMI from CPU 0 to CPUs 1:
> Sending NMI from CPU 0 to CPUs 2:
> Sending NMI from CPU 0 to CPUs 3:

I suppose the issue is that the alternatives in vgic_ich_hcr_trap_bits() are
always calculated, even if that function is only used from the gic-v3 code.
Calculating the trap bits reads ICH_VTR_EL2, which needs FEAT_GICv3.

Thanks,
Joey

> 
> Best regards
> -- 
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
> 
> 


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13 11:23         ` Joey Gouly
@ 2025-11-13 11:42           ` Marc Zyngier
  0 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-13 11:42 UTC (permalink / raw)
  To: Joey Gouly
  Cc: Marek Szyprowski, kvmarm, linux-arm-kernel, kvm, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On Thu, 13 Nov 2025 11:23:33 +0000,
Joey Gouly <joey.gouly@arm.com> wrote:

> I suppose the issue is that the alternatives in vgic_ich_hcr_trap_bits() are
> always calculated, even if that function is only used from the gic-v3 code.
> Calculating the trap bits reads ICH_VTR_EL2, which needs FEAT_GICv3.

Except that we have a bunch of rotten fruits that only have half a
GICv3, so relying on FEAT_GICv3 isn't useful if we want to continue
supporting these machines in the foreseeable future.

The fix for this is, I expect, what I posted at [1].

Thanks,

	M.

[1] https://lore.kernel.org/r/86seeitd3f.wl-maz@kernel.org

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
  2025-11-09 17:15 ` [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping Marc Zyngier
@ 2025-11-13 14:33   ` Mark Brown
  2025-11-13 18:15     ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Mark Brown @ 2025-11-13 14:33 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Aishwarya.TCV

[-- Attachment #1: Type: text/plain, Size: 30448 bytes --]

On Sun, Nov 09, 2025 at 05:15:39PM +0000, Marc Zyngier wrote:
> A long time ago, an unsuspecting architect forgot to add a trap
> bit for ICV_DIR_EL1 in ICH_HCR_EL2. Which was unfortunate, but
> what's a bit of spec between friends? Thankfully, this was fixed
> in a later revision, and ARM "deprecates" the lack of trapping
> ability.

I'm seeing a regression on i.MX8MP-EVK and Toradax AM625+Mallow boards
(both 4xA53+GICv3) in protected mode only with a bunch of the KVM
selftests, including the arch_timer one:

# selftests: kvm: arch_timer
# Random seed: 0x6b8b4567
# ==== Test Assertion Failure ====
#   lib/arm64/processor.c:487: false
#   pid=4469 tid=4473 errno=4 - Interrupted system call
# ==== Test Assertion Failure ====
#   lib/arm64/processor.c:487: false
#   pid=4469 tid=4471 errno=4 - Interrupted system call
# ==== Test Assertion Failure ====
#   lib/arm64/processor.c:487: false
#   pid=4469 tid=4472 errno=4 - Interrupted system call
# ==== Test Assertion Failure ====
#   lib/arm64/processor.c:487: false
#   pid=4469 tid=4470 errno=4 - Interrupted system call
#      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
#      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
#      3	 (inlined by) vcpu_run at kvm_util.c:1710
#      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
#      5	0x0000ffffb12f2f9b: ?? ??:0
#      6	0x0000ffffb135e58b: ?? ??:0
#      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
#      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
#      3	 (inlined by) vcpu_run at kvm_util.c:1710
#      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
#      5	0x0000ffffb12f2f9b: ?? ??:0
#      6	0x0000ffffb135e58b: ?? ??:0
#   Unexpected exception (vector:0x4, ec:0x0)
#      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
#      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
#      3	 (inlined by) vcpu_run at kvm_util.c:1710
#      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
#      5	0x0000ffffb12f2f9b: ?? ??:0
#      6	0x0000ffffb135e58b: ?? ??:0
#      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
#      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
#      3	 (inlined by) vcpu_run at kvm_util.c:1710
#      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
#      5	0x0000ffffb12f2f9b: ?? ??:0
#      6	0x0000ffffb135e58b: ?? ??:0
not ok 28 selftests: kvm: arch_timer # exit=254

The arch_timer case bisects to this patch in -next, regular nVHE mode
runs this test happily.  Full log (on the i.MX8MP-EVK):

   https://lava.sirena.org.uk/scheduler/job/2079917#L3993

bisect log:

# bad: [6d7e7251d03f98f26f2ee0dfd21bb0a0480a2178] Add linux-next specific files for 20251113
# good: [aeee991e3a4cb03497cda0f879619c2e71143b49] Merge branch 'for-linux-next-fixes' of https://gitlab.freedesktop.org/drm/misc/kernel.git
# good: [4d6e2211aeb932e096f673c88475016b1cc0f8ab] ASoC: Intel: boards: fix HDMI playback lookup when HDMI-In capture used
# good: [1d562ba0aa7df81335bf96c02be77efe8d5bab87] spi: dt-bindings: nuvoton,npcm-pspi: Convert to DT schema
# good: [b3a5302484033331af37569f7277d00131694b57] ASoC: Intel: sof_rt5682: Add quirk override support
# good: [32172cf3cb543a04c41a1677c97a38e60cad05b6] ASoC: cs35l56: Allow restoring factory calibration through ALSA control
# good: [873bc94689d832878befbcadc10b6ad5bb4e0027] ASoC: Intel: sof_sdw: add codec speaker support for the SKU
# good: [772ada50282b0c80343c8989147db816961f571d] ASoC: cs35l56: Alter error codes for calibration routine
# good: [6985defd1d832f1dd9d1977a6a2cc2cef7632704] regmap: sdw-mbq: Reorder regmap_mbq_context struct for better packing
# good: [fb1ebb10468da414d57153ddebaab29c38ef1a78] regulator: core: disable supply if enabling main regulator fails
# good: [2089f086303b773e181567fd8d5df3038bd85937] regulator: mt6363: Remove unneeded semicolon
# good: [4e92abd0a11b91af3742197a9ca962c3c00d0948] spi: imx: add i.MX51 ECSPI target mode support
# good: [6951be397ca8b8b167c9f99b5a11c541148c38cb] ASoC: codecs: pm4125: remove duplicate code
# good: [abc9a349b87ac0fd3ba8787ca00971b59c2e1257] spi: fsl-qspi: support the SpacemiT K1 SoC
# good: [1b0f3f9ee41ee2bdd206667f85ea2aa36dfe6e69] ASoC: SDCA: support Q7.8 volume format
# good: [6bd1ad97eb790570c167d4de4ca59fbc9c33722a] regulator: pf9453: Fix kernel doc for mux_poll()
# good: [55d03b5b5bdd04daf9a35ce49db18d8bb488dffb] spi: imx: remove CLK calculation and check for target mode
# good: [655079ac8a7721ac215a0596e3f33b740e01144a] ASoC: qcom: q6asm: Use guard() for spin locks
# good: [3c36965df80801344850388592e95033eceea05b] regulator: Add support for MediaTek MT6363 SPMI PMIC Regulators
# good: [2f538ef9f6f7c3d700c68536f21447dfc598f8c8] spi: aspeed: Use devm_iounmap() to unmap devm_ioremap() memory
# good: [aa897ffc396b48cc39eee133b6b43175d0df9eb5] ASoC: dt-bindings: ti,pcm1862: convert to dtschema
# good: [c4e68959af66df525d71db619ffe44af9178bb22] ASoC: dt-bindings: ti,tas2781: Add TAS5822 support
# good: [af9c8092d84244ca54ffb590435735f788e7a170] regmap: i3c: Use ARRAY_SIZE()
# good: [380fd29d57abe6679d87ec56babe65ddc5873a37] spi: tegra210-quad: Check hardware status on timeout
# good: [84194c66aaf78fed150edb217b9f341518b1cba2] ASoC: codecs: aw88261: pass pointer directly instead of passing the address
# good: [252abf2d07d33b1c70a59ba1c9395ba42bbd793e] regulator: Small cleanup in of_get_regulation_constraints()
# good: [2ecc8c089802e033d2e5204d21a9f467e2517df9] regulator: pf9453: remove unused I2C_LT register
# good: [ed5d499b5c9cc11dd3edae1a7a55db7dfa4f1bdc] regcache: maple: Split ->populate() from ->init()
# good: [e73b743bfe8a6ff4e05b5657d3f7586a17ac3ba0] ASoC: soc-core: check ops & auto_selectable_formats in snd_soc_dai_get_fmt() to prevent dereference error
# good: [f1dfbc1b5cf8650ae9a0d543e5f5335fc0f478ce] ASoC: max98090/91: fixing the stream index
# good: [6ef8e042cdcaabe3e3c68592ba8bfbaee2fa10a3] ASoC: codec: wm8400: replace printk() calls with dev_*() device aware logging
# good: [ecd0de438c1f0ee86cf8f6d5047965a2a181444b] spi: tle62x0: Add newline to sysfs attribute output
# good: [20bcda681f8597e86070a4b3b12d1e4f541865d3] ASoC: codecs: va-macro: fix revision checking
# good: [8fdb030fe283c84fd8d378c97ad0f32d6cdec6ce] ASoC: qcom: sc7280: make use of common helpers
# good: [e062bdfdd6adbb2dee7751d054c1d8df63ddb8b8] regmap: warn users about uninitialized flat cache
# good: [28039efa4d8e8bbf98b066133a906bd4e307d496] MAINTAINERS: remove obsolete file entry in DIALOG SEMICONDUCTOR DRIVERS
# good: [cf6bf51b53252284bafc7377a4d8dbf10f048b4d] ASoC: cs4271: Add support for the external mclk
# good: [66fecfa91deb536a12ddf3d878a99590d7900277] ASoC: spacemit: use `depends on` instead of `select`
# good: [f034c16a4663eaf3198dc18b201ba50533fb5b81] ASoC: spacemit: add failure check for spacemit_i2s_init_dai()
# good: [4a5ac6cd05a7e54f1585d7779464d6ed6272c134] ASoC: sun4i-spdif: Support SPDIF output on A523 family
# good: [4c33cef58965eb655a0ac8e243aa323581ec025f] regulator: pca9450: link regulator inputs to supply groups
# good: [4795375d8aa072e9aacb0b278e6203c6ca41816a] ASoC: cs-amp-lib-test: Add test cases for cs_amp_set_efi_calibration_data()
# good: [ef042df96d0e1089764f39ede61bc8f140a4be00] ASoC: SDCA: Add HID button IRQ
# good: [77a58ba7c64ccca20616aa03599766ccb0d1a330] spi: spi-mem: Trace exec_op
# good: [01313661b248c5ba586acae09bff57077dbec0a5] regulator: Let raspberrypi drivers depend on ARM
# good: [e973dfe9259095fb509ab12658c68d46f0e439d7] ASoC: qcom: sm8250: add qrb2210-sndcard compatible string
# good: [e7434adf0c53a84d548226304cdb41c8818da1cb] ASoC: cs530x: Add SPI bus support for cs530x parts
# good: [d29479abaded34b2b1dab2e17efe96a65eba3d61] ASoC: renesas: fsi: Constify struct fsi_stream_handler
# good: [c17fa4cbc546c431ccf13e9354d5d9c1cd247b7c] ASoC: sdw_utils: add name_prefix for rt1321 part id
# good: [2528c15f314ece50218d1273654f630d74109583] ASoC: max98090/91: adding DAPM routing for digital output for max98091
# good: [fd5ef3d69f8975bad16c437a337b5cb04c8217a2] spi: spi-qpic-snand: make qcom_spi_ecc_engine_ops_pipelined const
# good: [310bf433c01f78e0756fd5056a43118a2f77318c] ASoC: max98090/91: fixing a space
# good: [d054cc3a2ccfb19484f3b54d69b6e416832dc8f4] regulator: rpmh-regulator: Add RPMH regulator support for PMR735D
# good: [638bae3fb225a708dc67db613af62f6d14c4eff4] ASoC: max98090/91: added DAPM widget for digital output for max98091
# good: [ecba655bf54a661ffe078856cd8dbc898270e4b5] ASoC: fsl_aud2htx: add IEC958_SUBFRAME_LE format in supported list
# good: [7e1906643a7374529af74b013bba35e4fa4e6ffc] ASoC: codecs: va-macro: Clean up on error path in probe()
# good: [d742ebcfe524dc54023f7c520d2ed2e4b7203c19] ASoC: soc.h: remove snd_soc_kcontrol_component()
# good: [fce217449075d59b29052b8cdac567f0f3e22641] ASoC: spacemit: add i2s support for K1 SoC
# good: [6658472a3e2de08197acfe099ba71ee0e2505ecf] ASoC: amd: amd_sdw: Propagate the PCI subsystem Vendor and Device IDs
# good: [0cc08c8130ac8f74419f99fe707dc193b7f79d86] spi: aspeed: Fix an IS_ERR() vs NULL bug in probe()
# good: [0743acf746a81e0460a56fd5ff847d97fa7eb370] spi: airoha: buffer must be 0xff-ed before writing
# good: [79eaabc61dfbf5a4b680f42d3a113d05333c3960] irqchip/riscv-imsic: Embed the vector array in lpriv
# good: [00a155c691befdb10bea52c91d4c8c930bdaf73a] Merge branch 'objtool/core' of https://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux
# good: [1e570e77392f43a3cdab2849d1f81535f8a033e2] ASoC: mxs-saif: support usage with simple-audio-card
# good: [d77daa49085b067137d0adbe3263f75a7ee13a1b] spi: aspeed: fix spelling mistake "triming" -> "trimming"
# good: [15afe57a874eaf104bfbb61ec598fa31627f7b19] ASoC: dt-bindings: qcom: Add Kaanapali LPASS macro codecs
# good: [65efe5404d151767653c7b7dd39bd2e7ad532c2d] regulator: rpmh-regulator: Add RPMH regulator support for Glymur
# good: [fb25114cd760c13cf177d9ac37837fafcc9657b5] regulator: sy7636a: add gpios and input regulator
# good: [6621b0f118d500092f5f3d72ddddb22aeeb3c3a0] ASoC: codecs: rt5670: use SOC_VALUE_ENUM_SINGLE_DECL for DAC2 L/R MX-1B
# good: [50062baa536bcac03804cf04579c71b9351e829c] perf print-events: Remove print_symbol_events
# good: [433e294c3c5b5d2020085a0e36c1cb47b694690a] regulator: core: forward undervoltage events downstream by default
# good: [0b0eb7702a9fa410755e86124b4b7cd36e7d1cb4] ASoC: replace use of system_wq with system_dfl_wq
# good: [c2d420796a427dda71a2400909864e7f8e037fd4] elfnote: Change ELFNOTE() to use __UNIQUE_ID()
# good: [3049fc4b5f1d2320a84e2902b3ac5a735f60ca04] x86/alternative: Refactor INT3 call emulation selftest
# good: [07e1c3fd86d7a2ddce3ebc6b7390590c8524a484] objtool: Make find_symbol_containing() less arbitrary
# good: [9f14f1f91883aa2bfd6663161d2002c8ce937c43] compiler.h: Make addressable symbols less of an eyesore
# good: [6717e8f91db71641cb52855ed14c7900972ed0bc] kbuild: Remove 'kmod_' prefix from __KBUILD_MODNAME
# good: [4109043bff31f95d3da9ace33eb3c1925fd62cbd] modpost: Ignore unresolved section bounds symbols
# good: [a1526bcfcb6cb7cb601b9ff8e24d08881ef9afb8] objtool: Mark prefix functions
# good: [122679ebf90eeff97c5f793ed9a289197e0fbb2c] x86/kprobes: Remove STACK_FRAME_NON_STANDARD annotation
# good: [7e7e2c6e2a1cb250f8d03bb99eed01f6d982d5dd] ASoC: sof-function-topology-lib: escalate the log when missing function topoplogy
# good: [4d410ba9aa275e7990a270f63ce436990ace1bea] dt-bindings: sound: Update ADMAIF bindings for tegra264
# good: [fe8cc44dd173cde5788ab4e3730ac61f3d316d9c] spi: dw: add target mode support
# good: [9797329220a2c6622411eb9ecf6a35b24ce09d04] ASoC: sof-function-topology-lib: escalate the log when missing function topoplogy
# good: [5e537031f322d55315cd384398b726a9a0748d47] ASoC: codecs: Fix the error of excessive semicolons
# good: [4412ab501677606436e5c49e41151a1e6eac7ac0] spi: dt-bindings: spi-qpic-snand: Add IPQ5332 compatible
# good: [64d87ccfae3326a9561fe41dc6073064a083e0df] spi: aspeed: Only map necessary address window region
# good: [b83fb1b14c06bdd765903ac852ba20a14e24f227] spi: offload: Add offset parameter
# good: [6277a486a7faaa6c87f4bf1d59a2de233a093248] regulator: dt-bindings: Convert Dialog DA9211 Regulators to DT schema
# good: [a3a8c9c18f6904a777ff21f300d3da8c2b214c80] usb: vhci-hcd: Replace pr_*() with dev_*() logging
# good: [561f0ed96a626c53fbd9a06ce2de6349fd0c31d2] staging: rtl8723bs: sdio: clarify OQT free page comment
# good: [fe3a615dadd398f73cde00a5ba32389958242dec] drm/xe/vf: Kickstart after resfix in VF post migration recovery
# good: [59fedf46f782c024b74ceab7868e13f0e0f10c45] drm/ast: Split ast_detect_tx_chip() per chip generation
git bisect start '6d7e7251d03f98f26f2ee0dfd21bb0a0480a2178' 'aeee991e3a4cb03497cda0f879619c2e71143b49' '4d6e2211aeb932e096f673c88475016b1cc0f8ab' '1d562ba0aa7df81335bf96c02be77efe8d5bab87' 'b3a5302484033331af37569f7277d00131694b57' '32172cf3cb543a04c41a1677c97a38e60cad05b6' '873bc94689d832878befbcadc10b6ad5bb4e0027' '772ada50282b0c80343c8989147db816961f571d' '6985defd1d832f1dd9d1977a6a2cc2cef7632704' 'fb1ebb10468da414d57153ddebaab29c38ef1a78' '2089f086303b773e181567fd8d5df3038bd85937' '4e92abd0a11b91af3742197a9ca962c3c00d0948' '6951be397ca8b8b167c9f99b5a11c541148c38cb' 'abc9a349b87ac0fd3ba8787ca00971b59c2e1257' '1b0f3f9ee41ee2bdd206667f85ea2aa36dfe6e69' '6bd1ad97eb790570c167d4de4ca59fbc9c33722a' '55d03b5b5bdd04daf9a35ce49db18d8bb488dffb' '655079ac8a7721ac215a0596e3f33b740e01144a' '3c36965df80801344850388592e95033eceea05b' '2f538ef9f6f7c3d700c68536f21447dfc598f8c8' 'aa897ffc396b48cc39eee133b6b43175d0df9eb5' 'c4e68959af66df525d71db619ffe44af9178bb22' 'af9c8092d84244ca54ffb590435735f788e7a170' '380fd29d57abe6679d87ec56babe65ddc5873a37' '84194c66aaf78fed150edb217b9f341518b1cba2' '252abf2d07d33b1c70a59ba1c9395ba42bbd793e' '2ecc8c089802e033d2e5204d21a9f467e2517df9' 'ed5d499b5c9cc11dd3edae1a7a55db7dfa4f1bdc' 'e73b743bfe8a6ff4e05b5657d3f7586a17ac3ba0' 'f1dfbc1b5cf8650ae9a0d543e5f5335fc0f478ce' '6ef8e042cdcaabe3e3c68592ba8bfbaee2fa10a3' 'ecd0de438c1f0ee86cf8f6d5047965a2a181444b' '20bcda681f8597e86070a4b3b12d1e4f541865d3' '8fdb030fe283c84fd8d378c97ad0f32d6cdec6ce' 'e062bdfdd6adbb2dee7751d054c1d8df63ddb8b8' '28039efa4d8e8bbf98b066133a906bd4e307d496' 'cf6bf51b53252284bafc7377a4d8dbf10f048b4d' '66fecfa91deb536a12ddf3d878a99590d7900277' 'f034c16a4663eaf3198dc18b201ba50533fb5b81' '4a5ac6cd05a7e54f1585d7779464d6ed6272c134' '4c33cef58965eb655a0ac8e243aa323581ec025f' '4795375d8aa072e9aacb0b278e6203c6ca41816a' 'ef042df96d0e1089764f39ede61bc8f140a4be00' '77a58ba7c64ccca20616aa03599766ccb0d1a330' '01313661b248c5ba586acae09bff57077dbec0a5' 'e973dfe9259095fb509ab12658c68d46f0e439d7' 'e7434adf0c53a84d548226304cdb41c8818da1cb' 'd29479abaded34b2b1dab2e17efe96a65eba3d61' 'c17fa4cbc546c431ccf13e9354d5d9c1cd247b7c' '2528c15f314ece50218d1273654f630d74109583' 'fd5ef3d69f8975bad16c437a337b5cb04c8217a2' '310bf433c01f78e0756fd5056a43118a2f77318c' 'd054cc3a2ccfb19484f3b54d69b6e416832dc8f4' '638bae3fb225a708dc67db613af62f6d14c4eff4' 'ecba655bf54a661ffe078856cd8dbc898270e4b5' '7e1906643a7374529af74b013bba35e4fa4e6ffc' 'd742ebcfe524dc54023f7c520d2ed2e4b7203c19' 'fce217449075d59b29052b8cdac567f0f3e22641' '6658472a3e2de08197acfe099ba71ee0e2505ecf' '0cc08c8130ac8f74419f99fe707dc193b7f79d86' '0743acf746a81e0460a56fd5ff847d97fa7eb370' '79eaabc61dfbf5a4b680f42d3a113d05333c3960' '00a155c691befdb10bea52c91d4c8c930bdaf73a' '1e570e77392f43a3cdab2849d1f81535f8a033e2' 'd77daa49085b067137d0adbe3263f75a7ee13a1b' '15afe57a874eaf104bfbb61ec598fa31627f7b19' '65efe5404d151767653c7b7dd39bd2e7ad532c2d' 'fb25114cd760c13cf177d9ac37837fafcc9657b5' '6621b0f118d500092f5f3d72ddddb22aeeb3c3a0' '50062baa536bcac03804cf04579c71b9351e829c' '433e294c3c5b5d2020085a0e36c1cb47b694690a' '0b0eb7702a9fa410755e86124b4b7cd36e7d1cb4' 'c2d420796a427dda71a2400909864e7f8e037fd4' '3049fc4b5f1d2320a84e2902b3ac5a735f60ca04' '07e1c3fd86d7a2ddce3ebc6b7390590c8524a484' '9f14f1f91883aa2bfd6663161d2002c8ce937c43' '6717e8f91db71641cb52855ed14c7900972ed0bc' '4109043bff31f95d3da9ace33eb3c1925fd62cbd' 'a1526bcfcb6cb7cb601b9ff8e24d08881ef9afb8' '122679ebf90eeff97c5f793ed9a289197e0fbb2c' '7e7e2c6e2a1cb250f8d03bb99eed01f6d982d5dd' '4d410ba9aa275e7990a270f63ce436990ace1bea' 'fe8cc44dd173cde5788ab4e3730ac61f3d316d9c' '9797329220a2c6622411eb9ecf6a35b24ce09d04' '5e537031f322d55315cd384398b726a9a0748d47' '4412ab501677606436e5c49e41151a1e6eac7ac0' '64d87ccfae3326a9561fe41dc6073064a083e0df' 'b83fb1b14c06bdd765903ac852ba20a14e24f227' '6277a486a7faaa6c87f4bf1d59a2de233a093248' 'a3a8c9c18f6904a777ff21f300d3da8c2b214c80' '561f0ed96a626c53fbd9a06ce2de6349fd0c31d2' 'fe3a615dadd398f73cde00a5ba32389958242dec' '59fedf46f782c024b74ceab7868e13f0e0f10c45'
# test job: [4d6e2211aeb932e096f673c88475016b1cc0f8ab] https://lava.sirena.org.uk/scheduler/job/2078045
# test job: [1d562ba0aa7df81335bf96c02be77efe8d5bab87] https://lava.sirena.org.uk/scheduler/job/2078452
# test job: [b3a5302484033331af37569f7277d00131694b57] https://lava.sirena.org.uk/scheduler/job/2074506
# test job: [32172cf3cb543a04c41a1677c97a38e60cad05b6] https://lava.sirena.org.uk/scheduler/job/2075063
# test job: [873bc94689d832878befbcadc10b6ad5bb4e0027] https://lava.sirena.org.uk/scheduler/job/2074989
# test job: [772ada50282b0c80343c8989147db816961f571d] https://lava.sirena.org.uk/scheduler/job/2069151
# test job: [6985defd1d832f1dd9d1977a6a2cc2cef7632704] https://lava.sirena.org.uk/scheduler/job/2059206
# test job: [fb1ebb10468da414d57153ddebaab29c38ef1a78] https://lava.sirena.org.uk/scheduler/job/2059735
# test job: [2089f086303b773e181567fd8d5df3038bd85937] https://lava.sirena.org.uk/scheduler/job/2058045
# test job: [4e92abd0a11b91af3742197a9ca962c3c00d0948] https://lava.sirena.org.uk/scheduler/job/2055814
# test job: [6951be397ca8b8b167c9f99b5a11c541148c38cb] https://lava.sirena.org.uk/scheduler/job/2056393
# test job: [abc9a349b87ac0fd3ba8787ca00971b59c2e1257] https://lava.sirena.org.uk/scheduler/job/2054629
# test job: [1b0f3f9ee41ee2bdd206667f85ea2aa36dfe6e69] https://lava.sirena.org.uk/scheduler/job/2053476
# test job: [6bd1ad97eb790570c167d4de4ca59fbc9c33722a] https://lava.sirena.org.uk/scheduler/job/2053930
# test job: [55d03b5b5bdd04daf9a35ce49db18d8bb488dffb] https://lava.sirena.org.uk/scheduler/job/2054195
# test job: [655079ac8a7721ac215a0596e3f33b740e01144a] https://lava.sirena.org.uk/scheduler/job/2049656
# test job: [3c36965df80801344850388592e95033eceea05b] https://lava.sirena.org.uk/scheduler/job/2050587
# test job: [2f538ef9f6f7c3d700c68536f21447dfc598f8c8] https://lava.sirena.org.uk/scheduler/job/2048586
# test job: [aa897ffc396b48cc39eee133b6b43175d0df9eb5] https://lava.sirena.org.uk/scheduler/job/2048671
# test job: [c4e68959af66df525d71db619ffe44af9178bb22] https://lava.sirena.org.uk/scheduler/job/2044470
# test job: [af9c8092d84244ca54ffb590435735f788e7a170] https://lava.sirena.org.uk/scheduler/job/2043681
# test job: [380fd29d57abe6679d87ec56babe65ddc5873a37] https://lava.sirena.org.uk/scheduler/job/2044549
# test job: [84194c66aaf78fed150edb217b9f341518b1cba2] https://lava.sirena.org.uk/scheduler/job/2038332
# test job: [252abf2d07d33b1c70a59ba1c9395ba42bbd793e] https://lava.sirena.org.uk/scheduler/job/2038504
# test job: [2ecc8c089802e033d2e5204d21a9f467e2517df9] https://lava.sirena.org.uk/scheduler/job/2038909
# test job: [ed5d499b5c9cc11dd3edae1a7a55db7dfa4f1bdc] https://lava.sirena.org.uk/scheduler/job/2029006
# test job: [e73b743bfe8a6ff4e05b5657d3f7586a17ac3ba0] https://lava.sirena.org.uk/scheduler/job/2026401
# test job: [f1dfbc1b5cf8650ae9a0d543e5f5335fc0f478ce] https://lava.sirena.org.uk/scheduler/job/2025480
# test job: [6ef8e042cdcaabe3e3c68592ba8bfbaee2fa10a3] https://lava.sirena.org.uk/scheduler/job/2025879
# test job: [ecd0de438c1f0ee86cf8f6d5047965a2a181444b] https://lava.sirena.org.uk/scheduler/job/2026088
# test job: [20bcda681f8597e86070a4b3b12d1e4f541865d3] https://lava.sirena.org.uk/scheduler/job/2022933
# test job: [8fdb030fe283c84fd8d378c97ad0f32d6cdec6ce] https://lava.sirena.org.uk/scheduler/job/2021446
# test job: [e062bdfdd6adbb2dee7751d054c1d8df63ddb8b8] https://lava.sirena.org.uk/scheduler/job/2020179
# test job: [28039efa4d8e8bbf98b066133a906bd4e307d496] https://lava.sirena.org.uk/scheduler/job/2020283
# test job: [cf6bf51b53252284bafc7377a4d8dbf10f048b4d] https://lava.sirena.org.uk/scheduler/job/2022983
# test job: [66fecfa91deb536a12ddf3d878a99590d7900277] https://lava.sirena.org.uk/scheduler/job/2015329
# test job: [f034c16a4663eaf3198dc18b201ba50533fb5b81] https://lava.sirena.org.uk/scheduler/job/2015473
# test job: [4a5ac6cd05a7e54f1585d7779464d6ed6272c134] https://lava.sirena.org.uk/scheduler/job/2011241
# test job: [4c33cef58965eb655a0ac8e243aa323581ec025f] https://lava.sirena.org.uk/scheduler/job/2010807
# test job: [4795375d8aa072e9aacb0b278e6203c6ca41816a] https://lava.sirena.org.uk/scheduler/job/2011005
# test job: [ef042df96d0e1089764f39ede61bc8f140a4be00] https://lava.sirena.org.uk/scheduler/job/2011039
# test job: [77a58ba7c64ccca20616aa03599766ccb0d1a330] https://lava.sirena.org.uk/scheduler/job/2007290
# test job: [01313661b248c5ba586acae09bff57077dbec0a5] https://lava.sirena.org.uk/scheduler/job/2008742
# test job: [e973dfe9259095fb509ab12658c68d46f0e439d7] https://lava.sirena.org.uk/scheduler/job/2012324
# test job: [e7434adf0c53a84d548226304cdb41c8818da1cb] https://lava.sirena.org.uk/scheduler/job/2007744
# test job: [d29479abaded34b2b1dab2e17efe96a65eba3d61] https://lava.sirena.org.uk/scheduler/job/2008412
# test job: [c17fa4cbc546c431ccf13e9354d5d9c1cd247b7c] https://lava.sirena.org.uk/scheduler/job/2000004
# test job: [2528c15f314ece50218d1273654f630d74109583] https://lava.sirena.org.uk/scheduler/job/1997593
# test job: [fd5ef3d69f8975bad16c437a337b5cb04c8217a2] https://lava.sirena.org.uk/scheduler/job/1996100
# test job: [310bf433c01f78e0756fd5056a43118a2f77318c] https://lava.sirena.org.uk/scheduler/job/1996021
# test job: [d054cc3a2ccfb19484f3b54d69b6e416832dc8f4] https://lava.sirena.org.uk/scheduler/job/1995684
# test job: [638bae3fb225a708dc67db613af62f6d14c4eff4] https://lava.sirena.org.uk/scheduler/job/1991853
# test job: [ecba655bf54a661ffe078856cd8dbc898270e4b5] https://lava.sirena.org.uk/scheduler/job/1985116
# test job: [7e1906643a7374529af74b013bba35e4fa4e6ffc] https://lava.sirena.org.uk/scheduler/job/1978575
# test job: [d742ebcfe524dc54023f7c520d2ed2e4b7203c19] https://lava.sirena.org.uk/scheduler/job/1976775
# test job: [fce217449075d59b29052b8cdac567f0f3e22641] https://lava.sirena.org.uk/scheduler/job/1975621
# test job: [6658472a3e2de08197acfe099ba71ee0e2505ecf] https://lava.sirena.org.uk/scheduler/job/1976196
# test job: [0cc08c8130ac8f74419f99fe707dc193b7f79d86] https://lava.sirena.org.uk/scheduler/job/1965692
# test job: [0743acf746a81e0460a56fd5ff847d97fa7eb370] https://lava.sirena.org.uk/scheduler/job/1964813
# test job: [79eaabc61dfbf5a4b680f42d3a113d05333c3960] https://lava.sirena.org.uk/scheduler/job/1979980
# test job: [00a155c691befdb10bea52c91d4c8c930bdaf73a] https://lava.sirena.org.uk/scheduler/job/1980696
# test job: [1e570e77392f43a3cdab2849d1f81535f8a033e2] https://lava.sirena.org.uk/scheduler/job/1963814
# test job: [d77daa49085b067137d0adbe3263f75a7ee13a1b] https://lava.sirena.org.uk/scheduler/job/1964718
# test job: [15afe57a874eaf104bfbb61ec598fa31627f7b19] https://lava.sirena.org.uk/scheduler/job/1963938
# test job: [65efe5404d151767653c7b7dd39bd2e7ad532c2d] https://lava.sirena.org.uk/scheduler/job/1962057
# test job: [fb25114cd760c13cf177d9ac37837fafcc9657b5] https://lava.sirena.org.uk/scheduler/job/1963349
# test job: [6621b0f118d500092f5f3d72ddddb22aeeb3c3a0] https://lava.sirena.org.uk/scheduler/job/1962085
# test job: [50062baa536bcac03804cf04579c71b9351e829c] https://lava.sirena.org.uk/scheduler/job/1978269
# test job: [433e294c3c5b5d2020085a0e36c1cb47b694690a] https://lava.sirena.org.uk/scheduler/job/1957339
# test job: [0b0eb7702a9fa410755e86124b4b7cd36e7d1cb4] https://lava.sirena.org.uk/scheduler/job/1957395
# test job: [c2d420796a427dda71a2400909864e7f8e037fd4] https://lava.sirena.org.uk/scheduler/job/1981809
# test job: [3049fc4b5f1d2320a84e2902b3ac5a735f60ca04] https://lava.sirena.org.uk/scheduler/job/1989503
# test job: [07e1c3fd86d7a2ddce3ebc6b7390590c8524a484] https://lava.sirena.org.uk/scheduler/job/1980837
# test job: [9f14f1f91883aa2bfd6663161d2002c8ce937c43] https://lava.sirena.org.uk/scheduler/job/1981518
# test job: [6717e8f91db71641cb52855ed14c7900972ed0bc] https://lava.sirena.org.uk/scheduler/job/1981667
# test job: [4109043bff31f95d3da9ace33eb3c1925fd62cbd] https://lava.sirena.org.uk/scheduler/job/1981316
# test job: [a1526bcfcb6cb7cb601b9ff8e24d08881ef9afb8] https://lava.sirena.org.uk/scheduler/job/1980768
# test job: [122679ebf90eeff97c5f793ed9a289197e0fbb2c] https://lava.sirena.org.uk/scheduler/job/1981229
# test job: [7e7e2c6e2a1cb250f8d03bb99eed01f6d982d5dd] https://lava.sirena.org.uk/scheduler/job/1954246
# test job: [4d410ba9aa275e7990a270f63ce436990ace1bea] https://lava.sirena.org.uk/scheduler/job/1947847
# test job: [fe8cc44dd173cde5788ab4e3730ac61f3d316d9c] https://lava.sirena.org.uk/scheduler/job/1947074
# test job: [9797329220a2c6622411eb9ecf6a35b24ce09d04] https://lava.sirena.org.uk/scheduler/job/1947363
# test job: [5e537031f322d55315cd384398b726a9a0748d47] https://lava.sirena.org.uk/scheduler/job/1946632
# test job: [4412ab501677606436e5c49e41151a1e6eac7ac0] https://lava.sirena.org.uk/scheduler/job/1946228
# test job: [64d87ccfae3326a9561fe41dc6073064a083e0df] https://lava.sirena.org.uk/scheduler/job/1953764
# test job: [b83fb1b14c06bdd765903ac852ba20a14e24f227] https://lava.sirena.org.uk/scheduler/job/1946821
# test job: [6277a486a7faaa6c87f4bf1d59a2de233a093248] https://lava.sirena.org.uk/scheduler/job/1953762
# test job: [a3a8c9c18f6904a777ff21f300d3da8c2b214c80] https://lava.sirena.org.uk/scheduler/job/1979592
# test job: [561f0ed96a626c53fbd9a06ce2de6349fd0c31d2] https://lava.sirena.org.uk/scheduler/job/1978456
# test job: [fe3a615dadd398f73cde00a5ba32389958242dec] https://lava.sirena.org.uk/scheduler/job/1978703
# test job: [59fedf46f782c024b74ceab7868e13f0e0f10c45] https://lava.sirena.org.uk/scheduler/job/1979218
# test job: [6d7e7251d03f98f26f2ee0dfd21bb0a0480a2178] https://lava.sirena.org.uk/scheduler/job/2079917
# bad: [6d7e7251d03f98f26f2ee0dfd21bb0a0480a2178] Add linux-next specific files for 20251113
git bisect bad 6d7e7251d03f98f26f2ee0dfd21bb0a0480a2178
# test job: [3d08639208ff9f52d988c95781bc45d41d94ffd0] https://lava.sirena.org.uk/scheduler/job/2080233
# good: [3d08639208ff9f52d988c95781bc45d41d94ffd0] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
git bisect good 3d08639208ff9f52d988c95781bc45d41d94ffd0
# test job: [f7bef466d6ea35a35e9c852a7b5315ce969c7877] https://lava.sirena.org.uk/scheduler/job/2080382
# good: [f7bef466d6ea35a35e9c852a7b5315ce969c7877] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/libata/linux
git bisect good f7bef466d6ea35a35e9c852a7b5315ce969c7877
# test job: [362f39fb330b5578824bd5330403d6c438582483] https://lava.sirena.org.uk/scheduler/job/2080519
# bad: [362f39fb330b5578824bd5330403d6c438582483] Merge branch 'driver-core-next' of https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
git bisect bad 362f39fb330b5578824bd5330403d6c438582483
# test job: [f6e1d0d9e619027062eed9217d4804c11044a27a] https://lava.sirena.org.uk/scheduler/job/2080640
# good: [f6e1d0d9e619027062eed9217d4804c11044a27a] Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git
git bisect good f6e1d0d9e619027062eed9217d4804c11044a27a
# test job: [44d048590effc717acf997ff9b7545193289fbf0] https://lava.sirena.org.uk/scheduler/job/2080756
# bad: [44d048590effc717acf997ff9b7545193289fbf0] Merge branch 'next' of https://github.com/kvm-x86/linux.git
git bisect bad 44d048590effc717acf997ff9b7545193289fbf0
# test job: [b50267d5e8901940dfb948ad8d36628e8392c5dc] https://lava.sirena.org.uk/scheduler/job/2080827
# good: [b50267d5e8901940dfb948ad8d36628e8392c5dc] Merge branch 'non-rcu/next' of https://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
git bisect good b50267d5e8901940dfb948ad8d36628e8392c5dc
# test job: [5acf839dcad8bdfc6d646c34680c008bd1167cde] https://lava.sirena.org.uk/scheduler/job/2080878
# good: [5acf839dcad8bdfc6d646c34680c008bd1167cde] Merge branch 'tdx'
git bisect good 5acf839dcad8bdfc6d646c34680c008bd1167cde
# test job: [182853c7680aaaf00fffd7cd347ae4aa50805fdf] https://lava.sirena.org.uk/scheduler/job/2080989
# bad: [182853c7680aaaf00fffd7cd347ae4aa50805fdf] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps
git bisect bad 182853c7680aaaf00fffd7cd347ae4aa50805fdf
# test job: [d78124a65a03f1483c62d8612594d5d833b8e4a5] https://lava.sirena.org.uk/scheduler/job/2081054
# bad: [d78124a65a03f1483c62d8612594d5d833b8e4a5] KVM: arm64: GICv2: Extract LR computing primitive
git bisect bad d78124a65a03f1483c62d8612594d5d833b8e4a5
# test job: [2a69aca33cac6b191fd13c7ea16c33df680a1b0e] https://lava.sirena.org.uk/scheduler/job/2081151
# bad: [2a69aca33cac6b191fd13c7ea16c33df680a1b0e] KVM: arm64: Add LR overflow handling documentation
git bisect bad 2a69aca33cac6b191fd13c7ea16c33df680a1b0e
# test job: [ca30799f7c2d04400a428fbc82aa49dc2493cc1a] https://lava.sirena.org.uk/scheduler/job/2081229
# good: [ca30799f7c2d04400a428fbc82aa49dc2493cc1a] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
git bisect good ca30799f7c2d04400a428fbc82aa49dc2493cc1a
# test job: [10576b2d86522cc70d8e1d8e121a2cb9c44c2ff3] https://lava.sirena.org.uk/scheduler/job/2081295
# bad: [10576b2d86522cc70d8e1d8e121a2cb9c44c2ff3] KVM: arm64: Repack struct vgic_irq fields
git bisect bad 10576b2d86522cc70d8e1d8e121a2cb9c44c2ff3
# test job: [375e16720b4c8ee04a01de03fb7103ac0a7a4856] https://lava.sirena.org.uk/scheduler/job/2081327
# bad: [375e16720b4c8ee04a01de03fb7103ac0a7a4856] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
git bisect bad 375e16720b4c8ee04a01de03fb7103ac0a7a4856
# first bad commit: [375e16720b4c8ee04a01de03fb7103ac0a7a4856] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
                     ` (2 preceding siblings ...)
  2025-11-13  9:52   ` Marek Szyprowski
@ 2025-11-13 18:01   ` Mark Brown
  2025-11-14  9:37     ` Marc Zyngier
  3 siblings, 1 reply; 83+ messages in thread
From: Mark Brown @ 2025-11-13 18:01 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Sascha Bischoff, Stephen Rothwell, Aishwarya.TCV

[-- Attachment #1: Type: text/plain, Size: 1984 bytes --]

On Sun, Nov 09, 2025 at 05:15:38PM +0000, Marc Zyngier wrote:
> The trap bits are currently only set to manage CPU errata. However,
> we are about to make use of them for purposes beyond beating broken
> CPUs into submission.
> 
> For this purpose, turn these errata-driven bits into a patched-in
> constant that is merged with the KVM-driven value at the point of
> programming the ICH_HCR_EL2 register, rather than being directly
> stored with with the shadow value..

We're seeing the no-vgic-v3 failures in -next with slightly different
symptoms to those that were seen in mainline and fixed with Sacha's
change da888524c393 ("KVM: arm64: vgic-v3: Trap all if no in-kernel
irqchip").  That change generated a conflict with this patch which
Stephen resolved in what looks like a reasonable fashion tactically but
I suspect needs some wider changes.  The test now fails with:

# selftests: kvm: no-vgic-v3
# Random seed: 0x6b8b4567
# ==== Test Assertion Failure ====
#   lib/arm64/processor.c:487: false
#   pid=2080 tid=2080 errno=4 - Interrupted system call
#      1	0x0000000000413d27: assert_on_unhandled_exception at processor.c:487
#      2	0x0000000000406c1f: _vcpu_run at kvm_util.c:1699
#      3	 (inlined by) vcpu_run at kvm_util.c:1710
#      4	0x000000000040308b: test_run_vcpu at no-vgic-v3.c:124
#      5	0x0000000000402253: test_guest_no_gicv3 at no-vgic-v3.c:155
#      6	 (inlined by) main at no-vgic-v3.c:174
#      7	0x0000ffff9dc17543: ?? ??:0
#      8	0x0000ffff9dc17617: ?? ??:0
#      9	0x000000000040242f: _start at ??:?
#   Unexpected exception (vector:0x4, ec:0x18)
not ok 25 selftests: kvm: no-vgic-v3 # exit=254

Log showing the failure:

   https://lava.sirena.org.uk/scheduler/job/2079953#L2994

This is on i.MX8MP-EVK, 4xA53 and a GICv3.

No bisect or anything, between unrelated DRM breakage in -next and the
same test failing in Linus' tree in -rc3 which the kvm-arm tree is based
on it's a bit of a mess.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
  2025-11-13 14:33   ` Mark Brown
@ 2025-11-13 18:15     ` Marc Zyngier
  2025-11-13 19:06       ` Mark Brown
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-13 18:15 UTC (permalink / raw)
  To: Mark Brown
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Aishwarya.TCV

On Thu, 13 Nov 2025 14:33:02 +0000,
Mark Brown <broonie@kernel.org> wrote:
> 
> [1  <text/plain; us-ascii (quoted-printable)>]
> On Sun, Nov 09, 2025 at 05:15:39PM +0000, Marc Zyngier wrote:
> > A long time ago, an unsuspecting architect forgot to add a trap
> > bit for ICV_DIR_EL1 in ICH_HCR_EL2. Which was unfortunate, but
> > what's a bit of spec between friends? Thankfully, this was fixed
> > in a later revision, and ARM "deprecates" the lack of trapping
> > ability.
> 
> I'm seeing a regression on i.MX8MP-EVK and Toradax AM625+Mallow boards
> (both 4xA53+GICv3) in protected mode only with a bunch of the KVM
> selftests, including the arch_timer one:
> 
> # selftests: kvm: arch_timer
> # Random seed: 0x6b8b4567
> # ==== Test Assertion Failure ====
> #   lib/arm64/processor.c:487: false
> #   pid=4469 tid=4473 errno=4 - Interrupted system call
> # ==== Test Assertion Failure ====
> #   lib/arm64/processor.c:487: false
> #   pid=4469 tid=4471 errno=4 - Interrupted system call
> # ==== Test Assertion Failure ====
> #   lib/arm64/processor.c:487: false
> #   pid=4469 tid=4472 errno=4 - Interrupted system call
> # ==== Test Assertion Failure ====
> #   lib/arm64/processor.c:487: false
> #   pid=4469 tid=4470 errno=4 - Interrupted system call
> #      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
> #      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
> #      3	 (inlined by) vcpu_run at kvm_util.c:1710
> #      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
> #      5	0x0000ffffb12f2f9b: ?? ??:0
> #      6	0x0000ffffb135e58b: ?? ??:0
> #      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
> #      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
> #      3	 (inlined by) vcpu_run at kvm_util.c:1710
> #      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
> #      5	0x0000ffffb12f2f9b: ?? ??:0
> #      6	0x0000ffffb135e58b: ?? ??:0
> #   Unexpected exception (vector:0x4, ec:0x0)
> #      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
> #      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
> #      3	 (inlined by) vcpu_run at kvm_util.c:1710
> #      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
> #      5	0x0000ffffb12f2f9b: ?? ??:0
> #      6	0x0000ffffb135e58b: ?? ??:0
> #      1	0x0000000000414387: assert_on_unhandled_exception at processor.c:487
> #      2	0x000000000040727f: _vcpu_run at kvm_util.c:1699
> #      3	 (inlined by) vcpu_run at kvm_util.c:1710
> #      4	0x0000000000402b07: test_vcpu_run at arch_timer.c:55
> #      5	0x0000ffffb12f2f9b: ?? ??:0
> #      6	0x0000ffffb135e58b: ?? ??:0
> not ok 28 selftests: kvm: arch_timer # exit=254
> 
> The arch_timer case bisects to this patch in -next, regular nVHE mode
> runs this test happily.

My hunch is that we're missing something like the hack below, but I
haven't tried it yet.

I'll probably get to it tomorrow.

	M.

diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
index 82da9b03692d4..3108b5185c204 100644
--- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c
+++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
@@ -444,6 +444,8 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {
 
 	/* Scalable Vector Registers are restricted. */
 
+	HOST_HANDLED(SYS_ICC_PMR_EL1),
+
 	RAZ_WI(SYS_ERRIDR_EL1),
 	RAZ_WI(SYS_ERRSELR_EL1),
 	RAZ_WI(SYS_ERXFR_EL1),
@@ -457,9 +459,12 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {
 
 	/* Limited Ordering Regions Registers are restricted. */
 
+	HOST_HANDLED(SYS_ICC_DIR_EL1),
+	HOST_HANDLED(SYS_ICC_RPR_EL1),
 	HOST_HANDLED(SYS_ICC_SGI1R_EL1),
 	HOST_HANDLED(SYS_ICC_ASGI1R_EL1),
 	HOST_HANDLED(SYS_ICC_SGI0R_EL1),
+	HOST_HANDLED(SYS_ICC_CTLR_EL1),
 	{ SYS_DESC(SYS_ICC_SRE_EL1), .access = pvm_gic_read_sre, },
 
 	HOST_HANDLED(SYS_CCSIDR_EL1),

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


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
  2025-11-13 18:15     ` Marc Zyngier
@ 2025-11-13 19:06       ` Mark Brown
  2025-11-13 20:10         ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Mark Brown @ 2025-11-13 19:06 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Aishwarya.TCV

[-- Attachment #1: Type: text/plain, Size: 362 bytes --]

On Thu, Nov 13, 2025 at 06:15:29PM +0000, Marc Zyngier wrote:
> Mark Brown <broonie@kernel.org> wrote:

> > The arch_timer case bisects to this patch in -next, regular nVHE mode
> > runs this test happily.

> My hunch is that we're missing something like the hack below, but I
> haven't tried it yet.

> I'll probably get to it tomorrow.

That still fails FWIW.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
  2025-11-13 19:06       ` Mark Brown
@ 2025-11-13 20:10         ` Marc Zyngier
  2025-11-13 21:59           ` Oliver Upton
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-13 20:10 UTC (permalink / raw)
  To: Mark Brown
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Aishwarya.TCV, Fuad Tabba

+Fuad

On Thu, 13 Nov 2025 19:06:31 +0000,
Mark Brown <broonie@kernel.org> wrote:
> 
> [1  <text/plain; us-ascii (7bit)>]
> On Thu, Nov 13, 2025 at 06:15:29PM +0000, Marc Zyngier wrote:
> > Mark Brown <broonie@kernel.org> wrote:
> 
> > > The arch_timer case bisects to this patch in -next, regular nVHE mode
> > > runs this test happily.
> 
> > My hunch is that we're missing something like the hack below, but I
> > haven't tried it yet.
> 
> > I'll probably get to it tomorrow.
> 
> That still fails FWIW.

Yup, this has uncovered yet another pKVM bug, which doesn't preserve
the vgic_model in its private kvm structure. I'm able to make it work
with this:

diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 43bde061b65de..8911338961c5b 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -337,6 +337,9 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
 	/* CTR_EL0 is always under host control, even for protected VMs. */
 	hyp_vm->kvm.arch.ctr_el0 = host_kvm->arch.ctr_el0;
 
+	/* Preserve the vgic model so that GICv3 emulation works */
+	hyp_vm->kvm.arch.vgic.vgic_model = host_kvm->arch.vgic.vgic_model;
+
 	if (test_bit(KVM_ARCH_FLAG_MTE_ENABLED, &host_kvm->arch.flags))
 		set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags);
 
diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
index 82da9b03692d4..3108b5185c204 100644
--- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c
+++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
@@ -444,6 +444,8 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {
 
 	/* Scalable Vector Registers are restricted. */
 
+	HOST_HANDLED(SYS_ICC_PMR_EL1),
+
 	RAZ_WI(SYS_ERRIDR_EL1),
 	RAZ_WI(SYS_ERRSELR_EL1),
 	RAZ_WI(SYS_ERXFR_EL1),
@@ -457,9 +459,12 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {
 
 	/* Limited Ordering Regions Registers are restricted. */
 
+	HOST_HANDLED(SYS_ICC_DIR_EL1),
+	HOST_HANDLED(SYS_ICC_RPR_EL1),
 	HOST_HANDLED(SYS_ICC_SGI1R_EL1),
 	HOST_HANDLED(SYS_ICC_ASGI1R_EL1),
 	HOST_HANDLED(SYS_ICC_SGI0R_EL1),
+	HOST_HANDLED(SYS_ICC_CTLR_EL1),
 	{ SYS_DESC(SYS_ICC_SRE_EL1), .access = pvm_gic_read_sre, },
 
 	HOST_HANDLED(SYS_CCSIDR_EL1),

Thanks,

	M.

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


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping
  2025-11-13 20:10         ` Marc Zyngier
@ 2025-11-13 21:59           ` Oliver Upton
  0 siblings, 0 replies; 83+ messages in thread
From: Oliver Upton @ 2025-11-13 21:59 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Mark Brown, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Aishwarya.TCV, Fuad Tabba

On Thu, Nov 13, 2025 at 08:10:18PM +0000, Marc Zyngier wrote:
> +Fuad
> 
> On Thu, 13 Nov 2025 19:06:31 +0000,
> Mark Brown <broonie@kernel.org> wrote:
> > 
> > [1  <text/plain; us-ascii (7bit)>]
> > On Thu, Nov 13, 2025 at 06:15:29PM +0000, Marc Zyngier wrote:
> > > Mark Brown <broonie@kernel.org> wrote:
> > 
> > > > The arch_timer case bisects to this patch in -next, regular nVHE mode
> > > > runs this test happily.
> > 
> > > My hunch is that we're missing something like the hack below, but I
> > > haven't tried it yet.
> > 
> > > I'll probably get to it tomorrow.
> > 
> > That still fails FWIW.
> 
> Yup, this has uncovered yet another pKVM bug, which doesn't preserve
> the vgic_model in its private kvm structure. I'm able to make it work
> with this:

Thanks for debugging this Marc. I've added a patch on top of kvmarm/next
with this. I don't have any A53 machines around but I was able to repro
using kvm-arm.vgic_v3_common_trap=1 on QEMU.

Thanks,
Oliver


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant
  2025-11-13 18:01   ` Mark Brown
@ 2025-11-14  9:37     ` Marc Zyngier
  0 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-14  9:37 UTC (permalink / raw)
  To: Mark Brown
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan, Sascha Bischoff, Stephen Rothwell, Aishwarya.TCV

On Thu, 13 Nov 2025 18:01:57 +0000,
Mark Brown <broonie@kernel.org> wrote:
> 
> [1  <text/plain; us-ascii (quoted-printable)>]
> On Sun, Nov 09, 2025 at 05:15:38PM +0000, Marc Zyngier wrote:
> > The trap bits are currently only set to manage CPU errata. However,
> > we are about to make use of them for purposes beyond beating broken
> > CPUs into submission.
> > 
> > For this purpose, turn these errata-driven bits into a patched-in
> > constant that is merged with the KVM-driven value at the point of
> > programming the ICH_HCR_EL2 register, rather than being directly
> > stored with with the shadow value..
> 
> We're seeing the no-vgic-v3 failures in -next with slightly different
> symptoms to those that were seen in mainline and fixed with Sacha's
> change da888524c393 ("KVM: arm64: vgic-v3: Trap all if no in-kernel
> irqchip").  That change generated a conflict with this patch which
> Stephen resolved in what looks like a reasonable fashion tactically but
> I suspect needs some wider changes.  The test now fails with:
> 
> # selftests: kvm: no-vgic-v3
> # Random seed: 0x6b8b4567
> # ==== Test Assertion Failure ====
> #   lib/arm64/processor.c:487: false
> #   pid=2080 tid=2080 errno=4 - Interrupted system call
> #      1	0x0000000000413d27: assert_on_unhandled_exception at processor.c:487
> #      2	0x0000000000406c1f: _vcpu_run at kvm_util.c:1699
> #      3	 (inlined by) vcpu_run at kvm_util.c:1710
> #      4	0x000000000040308b: test_run_vcpu at no-vgic-v3.c:124
> #      5	0x0000000000402253: test_guest_no_gicv3 at no-vgic-v3.c:155
> #      6	 (inlined by) main at no-vgic-v3.c:174
> #      7	0x0000ffff9dc17543: ?? ??:0
> #      8	0x0000ffff9dc17617: ?? ??:0
> #      9	0x000000000040242f: _start at ??:?
> #   Unexpected exception (vector:0x4, ec:0x18)
> not ok 25 selftests: kvm: no-vgic-v3 # exit=254

Fix at 20251114093541.3216162-1-maz@kernel.org based on kvmarm/next. I
haven't tried -next.

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-09 17:16 ` [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity Marc Zyngier
@ 2025-11-14 14:20   ` Fuad Tabba
  2025-11-14 15:02     ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Fuad Tabba @ 2025-11-14 14:20 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

Hi Marc,

On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
>
> Now that we are ready to handle deactivation through ICV_DIR_EL1,
> set the trap bit if we have active interrupts outside of the LRs.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
>  arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> index 1026031f22ff9..26e17ed057f00 100644
> --- a/arch/arm64/kvm/vgic/vgic-v3.c
> +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> @@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
>                 ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
>         cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
>                 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
> +
> +       /*
> +        * Note that we set the trap irrespective of EOIMode, as that
> +        * can change behind our back without any warning...
> +        */
> +       if (irqs_active_outside_lrs(als))
> +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
>  }

I just tested these patches as they are on kvmarm/next
2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
additional pKVM patches. I tried running it with pKVM (non-protected)
and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
when booting a non-protected guest, which triggers a bug in
handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706

This trap is happening because of setting this particular trap (TDIR).
Just removing this trap from vgic_v3_configure_hcr() from the ToT on
kvmarm/next boots fine.

I'm running this on QEMU with '-machine virt,gic-version=3 -cpu max'
and the kernel with 'kvm-arm.mode=protected' and with
'kvm-arm.mode=nvhe'.

Let me know if you need any more info or help testing.

Cheers,
/fuad


>  static bool lr_signals_eoi_mi(u64 lr_val)
> --
> 2.47.3
>
>


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-14 14:20   ` Fuad Tabba
@ 2025-11-14 15:02     ` Marc Zyngier
  2025-11-14 15:53       ` Fuad Tabba
  2025-11-24 11:52       ` Mark Brown
  0 siblings, 2 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-14 15:02 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On Fri, 14 Nov 2025 14:20:46 +0000,
Fuad Tabba <tabba@google.com> wrote:
> 
> Hi Marc,
> 
> On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
> >
> > Now that we are ready to handle deactivation through ICV_DIR_EL1,
> > set the trap bit if we have active interrupts outside of the LRs.
> >
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > ---
> >  arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
> >  1 file changed, 7 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > index 1026031f22ff9..26e17ed057f00 100644
> > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > @@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
> >                 ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
> >         cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
> >                 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
> > +
> > +       /*
> > +        * Note that we set the trap irrespective of EOIMode, as that
> > +        * can change behind our back without any warning...
> > +        */
> > +       if (irqs_active_outside_lrs(als))
> > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> >  }
> 
> I just tested these patches as they are on kvmarm/next
> 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> additional pKVM patches. I tried running it with pKVM (non-protected)
> and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> when booting a non-protected guest, which triggers a bug in
> handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706
> 
> This trap is happening because of setting this particular trap (TDIR).
> Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> kvmarm/next boots fine.

This is surprising, as I'm not hitting this on actual HW. Are you
getting a 0x18 trap? If so, is it coming from the host? Can you
correlate the PC with what the host is doing?

It would indicate that we are leaking trap bits on exit, and that QEMU
is trapping ICC_DIR_EL1 on top of ICV_DIR_EL1 (which the HW I have
access to doesn't seem to do).

> I'm running this on QEMU with '-machine virt,gic-version=3 -cpu max'
> and the kernel with 'kvm-arm.mode=protected' and with
> 'kvm-arm.mode=nvhe'.
> 
> Let me know if you need any more info or help testing.

On top of the above, could you give the hack below a go? I haven't
tested it at all (I'm in the middle of a bisect from hell...)

Thanks,

	M.

diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
index e950efa22547..71199e1a9294 100644
--- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -243,7 +243,7 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
                cpu_if->vgic_hcr |= val & ICH_HCR_EL2_EOIcount;
        }
 
-       write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
+       write_gicreg(0, ICH_HCR_EL2);
 }
 
 void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)

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


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-14 15:02     ` Marc Zyngier
@ 2025-11-14 15:53       ` Fuad Tabba
  2025-11-14 17:41         ` Marc Zyngier
  2025-11-24 11:52       ` Mark Brown
  1 sibling, 1 reply; 83+ messages in thread
From: Fuad Tabba @ 2025-11-14 15:53 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

Hi Marc,

On Fri, 14 Nov 2025 at 15:02, Marc Zyngier <maz@kernel.org> wrote:
>
> On Fri, 14 Nov 2025 14:20:46 +0000,
> Fuad Tabba <tabba@google.com> wrote:
> >
> > Hi Marc,
> >
> > On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
> > >
> > > Now that we are ready to handle deactivation through ICV_DIR_EL1,
> > > set the trap bit if we have active interrupts outside of the LRs.
> > >
> > > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > > ---
> > >  arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
> > >  1 file changed, 7 insertions(+)
> > >
> > > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > > index 1026031f22ff9..26e17ed057f00 100644
> > > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > > @@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
> > >                 ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
> > >         cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
> > >                 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
> > > +
> > > +       /*
> > > +        * Note that we set the trap irrespective of EOIMode, as that
> > > +        * can change behind our back without any warning...
> > > +        */
> > > +       if (irqs_active_outside_lrs(als))
> > > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> > >  }
> >
> > I just tested these patches as they are on kvmarm/next
> > 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> > 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> > additional pKVM patches. I tried running it with pKVM (non-protected)
> > and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> > when booting a non-protected guest, which triggers a bug in
> > handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706
> >
> > This trap is happening because of setting this particular trap (TDIR).
> > Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> > kvmarm/next boots fine.
>
> This is surprising, as I'm not hitting this on actual HW. Are you
> getting a 0x18 trap? If so, is it coming from the host? Can you
> correlate the PC with what the host is doing?

I should have given you that earlier, sorry.

Yes, it's an 0x18 trap from the host (although it happens when I boot
a guest). Here is the relevant part of the backtrace addr2lined and
the full one below.

handle_percpu_devid_irq+0x90/0x120 (kernel/irq/chip.c:930)
generic_handle_domain_irq+0x40/0x64 (include/linux/irqdesc.h:?)
gic_handle_irq+0x4c/0x110 (include/linux/irqdesc.h:?)
call_on_irq_stack+0x30/0x48 (arch/arm64/kernel/entry.S:893)

[   28.454804] Code: d65f03c0 92800008 f9000008 17fffffa (d4210000)
[   28.454873] kvm [266]: Hyp Offset: 0xfff1205c3fe00000
[   28.455157] Kernel panic - not syncing: HYP panic:
[   28.455157] PS:204023c9 PC:000e5fa4413e39bc ESR:00000000f2000800
[   28.455157] FAR:ffff800082733d3c HPFAR:0000000000500000 PAR:0000000000000000
[   28.455157] VCPU:0000000000000000
[   28.459703] CPU: 5 UID: 0 PID: 266 Comm: kvm-vcpu-0 Not tainted
6.18.0-rc3-g2ea7215187c5 #8 PREEMPT
[   28.460247] Hardware name: linux,dummy-virt (DT)
[   28.460615] Call trace:
[   28.460900]  show_stack+0x18/0x24 (C)
[   28.461234]  dump_stack_lvl+0x40/0x84
[   28.461421]  dump_stack+0x18/0x24
[   28.461566]  vpanic+0x11c/0x364
[   28.461698]  vpanic+0x0/0x364
[   28.461838]  nvhe_hyp_panic_handler+0x118/0x190
[   28.462056]  handle_percpu_devid_irq+0x90/0x120
[   28.462248]  handle_percpu_devid_irq+0x90/0x120
[   28.462439]  generic_handle_domain_irq+0x40/0x64
[   28.462643]  gic_handle_irq+0x4c/0x110
[   28.462814]  call_on_irq_stack+0x30/0x48
[   28.463003]  do_interrupt_handler+0x4c/0x6c
[   28.463184]  el1_interrupt+0x3c/0x60
[   28.463348]  el1h_64_irq_handler+0x18/0x24
[   28.463525]  el1h_64_irq+0x6c/0x70
[   28.463799]  local_daif_restore+0x8/0xc (P)
[   28.463980]  el0t_64_sync_handler+0x84/0x12c
[   28.464164]  el0t_64_sync+0x198/0x19c

> It would indicate that we are leaking trap bits on exit, and that QEMU
> is trapping ICC_DIR_EL1 on top of ICV_DIR_EL1 (which the HW I have
> access to doesn't seem to do).
>
> > I'm running this on QEMU with '-machine virt,gic-version=3 -cpu max'
> > and the kernel with 'kvm-arm.mode=protected' and with
> > 'kvm-arm.mode=nvhe'.
> >
> > Let me know if you need any more info or help testing.
>
> On top of the above, could you give the hack below a go? I haven't
> tested it at all (I'm in the middle of a bisect from hell...)

With the hack it boots, both nvhe and protected mode.

Cheers,
/fuad

> Thanks,
>
>         M.
>
> diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
> index e950efa22547..71199e1a9294 100644
> --- a/arch/arm64/kvm/hyp/vgic-v3-sr.c
> +++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
> @@ -243,7 +243,7 @@ void __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if)
>                 cpu_if->vgic_hcr |= val & ICH_HCR_EL2_EOIcount;
>         }
>
> -       write_gicreg(compute_ich_hcr(cpu_if) & ~ICH_HCR_EL2_En, ICH_HCR_EL2);
> +       write_gicreg(0, ICH_HCR_EL2);
>  }
>
>  void __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if)
>
> --
> Without deviation from the norm, progress is not possible.


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-14 15:53       ` Fuad Tabba
@ 2025-11-14 17:41         ` Marc Zyngier
  2025-11-17  8:22           ` Fuad Tabba
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-14 17:41 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On Fri, 14 Nov 2025 15:53:33 +0000,
Fuad Tabba <tabba@google.com> wrote:
> 
> Hi Marc,
> 
> On Fri, 14 Nov 2025 at 15:02, Marc Zyngier <maz@kernel.org> wrote:
> >
> > On Fri, 14 Nov 2025 14:20:46 +0000,
> > Fuad Tabba <tabba@google.com> wrote:
> > >
> > > Hi Marc,
> > >
> > > On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
> > > >
> > > > Now that we are ready to handle deactivation through ICV_DIR_EL1,
> > > > set the trap bit if we have active interrupts outside of the LRs.
> > > >
> > > > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > > > ---
> > > >  arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
> > > >  1 file changed, 7 insertions(+)
> > > >
> > > > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > > > index 1026031f22ff9..26e17ed057f00 100644
> > > > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > > > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > > > @@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
> > > >                 ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
> > > >         cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
> > > >                 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
> > > > +
> > > > +       /*
> > > > +        * Note that we set the trap irrespective of EOIMode, as that
> > > > +        * can change behind our back without any warning...
> > > > +        */
> > > > +       if (irqs_active_outside_lrs(als))
> > > > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> > > >  }
> > >
> > > I just tested these patches as they are on kvmarm/next
> > > 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> > > 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> > > additional pKVM patches. I tried running it with pKVM (non-protected)
> > > and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> > > when booting a non-protected guest, which triggers a bug in
> > > handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706
> > >
> > > This trap is happening because of setting this particular trap (TDIR).
> > > Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> > > kvmarm/next boots fine.
> >
> > This is surprising, as I'm not hitting this on actual HW. Are you
> > getting a 0x18 trap? If so, is it coming from the host? Can you
> > correlate the PC with what the host is doing?
> 
> I should have given you that earlier, sorry.
> 
> Yes, it's an 0x18 trap from the host (although it happens when I boot
> a guest). Here is the relevant part of the backtrace addr2lined and
> the full one below.
>
> handle_percpu_devid_irq+0x90/0x120 (kernel/irq/chip.c:930)
> generic_handle_domain_irq+0x40/0x64 (include/linux/irqdesc.h:?)
> gic_handle_irq+0x4c/0x110 (include/linux/irqdesc.h:?)
> call_on_irq_stack+0x30/0x48 (arch/arm64/kernel/entry.S:893)
> 
> [   28.454804] Code: d65f03c0 92800008 f9000008 17fffffa (d4210000)
> [   28.454873] kvm [266]: Hyp Offset: 0xfff1205c3fe00000
> [   28.455157] Kernel panic - not syncing: HYP panic:
> [   28.455157] PS:204023c9 PC:000e5fa4413e39bc ESR:00000000f2000800
> [   28.455157] FAR:ffff800082733d3c HPFAR:0000000000500000 PAR:0000000000000000

I expect you have a write to ICC_DIR_EL1 at this address?

> [   28.455157] VCPU:0000000000000000
> [   28.459703] CPU: 5 UID: 0 PID: 266 Comm: kvm-vcpu-0 Not tainted
> 6.18.0-rc3-g2ea7215187c5 #8 PREEMPT
> [   28.460247] Hardware name: linux,dummy-virt (DT)
> [   28.460615] Call trace:
> [   28.460900]  show_stack+0x18/0x24 (C)
> [   28.461234]  dump_stack_lvl+0x40/0x84
> [   28.461421]  dump_stack+0x18/0x24
> [   28.461566]  vpanic+0x11c/0x364
> [   28.461698]  vpanic+0x0/0x364
> [   28.461838]  nvhe_hyp_panic_handler+0x118/0x190
> [   28.462056]  handle_percpu_devid_irq+0x90/0x120
> [   28.462248]  handle_percpu_devid_irq+0x90/0x120
> [   28.462439]  generic_handle_domain_irq+0x40/0x64
> [   28.462643]  gic_handle_irq+0x4c/0x110
> [   28.462814]  call_on_irq_stack+0x30/0x48
> [   28.463003]  do_interrupt_handler+0x4c/0x6c
> [   28.463184]  el1_interrupt+0x3c/0x60
> [   28.463348]  el1h_64_irq_handler+0x18/0x24
> [   28.463525]  el1h_64_irq+0x6c/0x70
> [   28.463799]  local_daif_restore+0x8/0xc (P)
> [   28.463980]  el0t_64_sync_handler+0x84/0x12c
> [   28.464164]  el0t_64_sync+0x198/0x19c
> 
> > It would indicate that we are leaking trap bits on exit, and that QEMU
> > is trapping ICC_DIR_EL1 on top of ICV_DIR_EL1 (which the HW I have
> > access to doesn't seem to do).
> >
> > > I'm running this on QEMU with '-machine virt,gic-version=3 -cpu max'
> > > and the kernel with 'kvm-arm.mode=protected' and with
> > > 'kvm-arm.mode=nvhe'.
> > >
> > > Let me know if you need any more info or help testing.
> >
> > On top of the above, could you give the hack below a go? I haven't
> > tested it at all (I'm in the middle of a bisect from hell...)
> 
> With the hack it boots, both nvhe and protected mode.

OK. At least we know what the issue is, and it shouldn't be too hard
to fix. I guess there is an opportunity for cleanup here, and I'll
look into it shortly (probably not before Monday though).

Thanks again,

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-14 17:41         ` Marc Zyngier
@ 2025-11-17  8:22           ` Fuad Tabba
  2025-11-17 11:56             ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Fuad Tabba @ 2025-11-17  8:22 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

Hi Marc,

On Fri, 14 Nov 2025 at 17:41, Marc Zyngier <maz@kernel.org> wrote:
>
> On Fri, 14 Nov 2025 15:53:33 +0000,
> Fuad Tabba <tabba@google.com> wrote:
> >
> > Hi Marc,
> >
> > On Fri, 14 Nov 2025 at 15:02, Marc Zyngier <maz@kernel.org> wrote:
> > >
> > > On Fri, 14 Nov 2025 14:20:46 +0000,
> > > Fuad Tabba <tabba@google.com> wrote:
> > > >
> > > > Hi Marc,
> > > >
> > > > On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
> > > > >
> > > > > Now that we are ready to handle deactivation through ICV_DIR_EL1,
> > > > > set the trap bit if we have active interrupts outside of the LRs.
> > > > >
> > > > > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > > > > ---
> > > > >  arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
> > > > >  1 file changed, 7 insertions(+)
> > > > >
> > > > > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > > > > index 1026031f22ff9..26e17ed057f00 100644
> > > > > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > > > > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > > > > @@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
> > > > >                 ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
> > > > >         cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
> > > > >                 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
> > > > > +
> > > > > +       /*
> > > > > +        * Note that we set the trap irrespective of EOIMode, as that
> > > > > +        * can change behind our back without any warning...
> > > > > +        */
> > > > > +       if (irqs_active_outside_lrs(als))
> > > > > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> > > > >  }
> > > >
> > > > I just tested these patches as they are on kvmarm/next
> > > > 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> > > > 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> > > > additional pKVM patches. I tried running it with pKVM (non-protected)
> > > > and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> > > > when booting a non-protected guest, which triggers a bug in
> > > > handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706
> > > >
> > > > This trap is happening because of setting this particular trap (TDIR).
> > > > Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> > > > kvmarm/next boots fine.
> > >
> > > This is surprising, as I'm not hitting this on actual HW. Are you
> > > getting a 0x18 trap? If so, is it coming from the host? Can you
> > > correlate the PC with what the host is doing?
> >
> > I should have given you that earlier, sorry.
> >
> > Yes, it's an 0x18 trap from the host (although it happens when I boot
> > a guest). Here is the relevant part of the backtrace addr2lined and
> > the full one below.
> >
> > handle_percpu_devid_irq+0x90/0x120 (kernel/irq/chip.c:930)
> > generic_handle_domain_irq+0x40/0x64 (include/linux/irqdesc.h:?)
> > gic_handle_irq+0x4c/0x110 (include/linux/irqdesc.h:?)
> > call_on_irq_stack+0x30/0x48 (arch/arm64/kernel/entry.S:893)
> >
> > [   28.454804] Code: d65f03c0 92800008 f9000008 17fffffa (d4210000)
> > [   28.454873] kvm [266]: Hyp Offset: 0xfff1205c3fe00000
> > [   28.455157] Kernel panic - not syncing: HYP panic:
> > [   28.455157] PS:204023c9 PC:000e5fa4413e39bc ESR:00000000f2000800
> > [   28.455157] FAR:ffff800082733d3c HPFAR:0000000000500000 PAR:0000000000000000
>
> I expect you have a write to ICC_DIR_EL1 at this address?

It almost surely must be, but tracking it down hasn't been that easy.
That said, I think it's ending up in gic_eoimode1_eoi_irq(), which
calls gic_write_dir() if !gic_arm64_erratum_2941627_needed(d).

I wonder if your hardware needs that erratum.

> > [   28.455157] VCPU:0000000000000000
> > [   28.459703] CPU: 5 UID: 0 PID: 266 Comm: kvm-vcpu-0 Not tainted
> > 6.18.0-rc3-g2ea7215187c5 #8 PREEMPT
> > [   28.460247] Hardware name: linux,dummy-virt (DT)
> > [   28.460615] Call trace:
> > [   28.460900]  show_stack+0x18/0x24 (C)
> > [   28.461234]  dump_stack_lvl+0x40/0x84
> > [   28.461421]  dump_stack+0x18/0x24
> > [   28.461566]  vpanic+0x11c/0x364
> > [   28.461698]  vpanic+0x0/0x364
> > [   28.461838]  nvhe_hyp_panic_handler+0x118/0x190
> > [   28.462056]  handle_percpu_devid_irq+0x90/0x120
> > [   28.462248]  handle_percpu_devid_irq+0x90/0x120
> > [   28.462439]  generic_handle_domain_irq+0x40/0x64
> > [   28.462643]  gic_handle_irq+0x4c/0x110
> > [   28.462814]  call_on_irq_stack+0x30/0x48
> > [   28.463003]  do_interrupt_handler+0x4c/0x6c
> > [   28.463184]  el1_interrupt+0x3c/0x60
> > [   28.463348]  el1h_64_irq_handler+0x18/0x24
> > [   28.463525]  el1h_64_irq+0x6c/0x70
> > [   28.463799]  local_daif_restore+0x8/0xc (P)
> > [   28.463980]  el0t_64_sync_handler+0x84/0x12c
> > [   28.464164]  el0t_64_sync+0x198/0x19c
> >
> > > It would indicate that we are leaking trap bits on exit, and that QEMU
> > > is trapping ICC_DIR_EL1 on top of ICV_DIR_EL1 (which the HW I have
> > > access to doesn't seem to do).
> > >
> > > > I'm running this on QEMU with '-machine virt,gic-version=3 -cpu max'
> > > > and the kernel with 'kvm-arm.mode=protected' and with
> > > > 'kvm-arm.mode=nvhe'.
> > > >
> > > > Let me know if you need any more info or help testing.
> > >
> > > On top of the above, could you give the hack below a go? I haven't
> > > tested it at all (I'm in the middle of a bisect from hell...)
> >
> > With the hack it boots, both nvhe and protected mode.
>
> OK. At least we know what the issue is, and it shouldn't be too hard
> to fix. I guess there is an opportunity for cleanup here, and I'll
> look into it shortly (probably not before Monday though).

No hurry on my part. I'm just here for the reviews :)

Cheers,
/fuad

> Thanks again,
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-17  8:22           ` Fuad Tabba
@ 2025-11-17 11:56             ` Marc Zyngier
  0 siblings, 0 replies; 83+ messages in thread
From: Marc Zyngier @ 2025-11-17 11:56 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Christoffer Dall, Volodymyr Babchuk,
	Yao Yuan

On Mon, 17 Nov 2025 08:22:05 +0000,
Fuad Tabba <tabba@google.com> wrote:
> 
> Hi Marc,
> 
> On Fri, 14 Nov 2025 at 17:41, Marc Zyngier <maz@kernel.org> wrote:
> >
> > On Fri, 14 Nov 2025 15:53:33 +0000,
> > Fuad Tabba <tabba@google.com> wrote:
> > >
> > > Hi Marc,
> > >
> > > On Fri, 14 Nov 2025 at 15:02, Marc Zyngier <maz@kernel.org> wrote:
> > > >
> > > > On Fri, 14 Nov 2025 14:20:46 +0000,
> > > > Fuad Tabba <tabba@google.com> wrote:
> > > > >
> > > > > Hi Marc,
> > > > >
> > > > > On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
> > > > > >
> > > > > > Now that we are ready to handle deactivation through ICV_DIR_EL1,
> > > > > > set the trap bit if we have active interrupts outside of the LRs.
> > > > > >
> > > > > > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > > > > > ---
> > > > > >  arch/arm64/kvm/vgic/vgic-v3.c | 7 +++++++
> > > > > >  1 file changed, 7 insertions(+)
> > > > > >
> > > > > > diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
> > > > > > index 1026031f22ff9..26e17ed057f00 100644
> > > > > > --- a/arch/arm64/kvm/vgic/vgic-v3.c
> > > > > > +++ b/arch/arm64/kvm/vgic/vgic-v3.c
> > > > > > @@ -42,6 +42,13 @@ void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu,
> > > > > >                 ICH_HCR_EL2_VGrp0DIE : ICH_HCR_EL2_VGrp0EIE;
> > > > > >         cpuif->vgic_hcr |= (cpuif->vgic_vmcr & ICH_VMCR_ENG1_MASK) ?
> > > > > >                 ICH_HCR_EL2_VGrp1DIE : ICH_HCR_EL2_VGrp1EIE;
> > > > > > +
> > > > > > +       /*
> > > > > > +        * Note that we set the trap irrespective of EOIMode, as that
> > > > > > +        * can change behind our back without any warning...
> > > > > > +        */
> > > > > > +       if (irqs_active_outside_lrs(als))
> > > > > > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> > > > > >  }
> > > > >
> > > > > I just tested these patches as they are on kvmarm/next
> > > > > 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> > > > > 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> > > > > additional pKVM patches. I tried running it with pKVM (non-protected)
> > > > > and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> > > > > when booting a non-protected guest, which triggers a bug in
> > > > > handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706
> > > > >
> > > > > This trap is happening because of setting this particular trap (TDIR).
> > > > > Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> > > > > kvmarm/next boots fine.
> > > >
> > > > This is surprising, as I'm not hitting this on actual HW. Are you
> > > > getting a 0x18 trap? If so, is it coming from the host? Can you
> > > > correlate the PC with what the host is doing?
> > >
> > > I should have given you that earlier, sorry.
> > >
> > > Yes, it's an 0x18 trap from the host (although it happens when I boot
> > > a guest). Here is the relevant part of the backtrace addr2lined and
> > > the full one below.
> > >
> > > handle_percpu_devid_irq+0x90/0x120 (kernel/irq/chip.c:930)
> > > generic_handle_domain_irq+0x40/0x64 (include/linux/irqdesc.h:?)
> > > gic_handle_irq+0x4c/0x110 (include/linux/irqdesc.h:?)
> > > call_on_irq_stack+0x30/0x48 (arch/arm64/kernel/entry.S:893)
> > >
> > > [   28.454804] Code: d65f03c0 92800008 f9000008 17fffffa (d4210000)
> > > [   28.454873] kvm [266]: Hyp Offset: 0xfff1205c3fe00000
> > > [   28.455157] Kernel panic - not syncing: HYP panic:
> > > [   28.455157] PS:204023c9 PC:000e5fa4413e39bc ESR:00000000f2000800
> > > [   28.455157] FAR:ffff800082733d3c HPFAR:0000000000500000 PAR:0000000000000000
> >
> > I expect you have a write to ICC_DIR_EL1 at this address?
> 
> It almost surely must be, but tracking it down hasn't been that easy.
> That said, I think it's ending up in gic_eoimode1_eoi_irq(), which
> calls gic_write_dir() if !gic_arm64_erratum_2941627_needed(d).
> 
> I wonder if your hardware needs that erratum.

No, it doesn't. And this erratum only kicks in if the deactivated
interrupt is an SPI that has been moved to another CPU while being
handled (i.e. never).

It really isn't related to the GIC itself (which the erratum above is
about), but to the CPU interface, and whether TDIR applies to
ICC_DIR_EL1 on top of the virtual variant.

The architecture "deprecates" not trapping, and QEMU abides by this
deprecation. HW that predates the deprecation didn't get the message,
oddly enough... Neither did I, until you reported the crash!

Thanks,

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-14 15:02     ` Marc Zyngier
  2025-11-14 15:53       ` Fuad Tabba
@ 2025-11-24 11:52       ` Mark Brown
  2025-11-24 13:06         ` Marc Zyngier
  1 sibling, 1 reply; 83+ messages in thread
From: Mark Brown @ 2025-11-24 11:52 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Fuad Tabba, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

[-- Attachment #1: Type: text/plain, Size: 1672 bytes --]

On Fri, Nov 14, 2025 at 03:02:32PM +0000, Marc Zyngier wrote:
> Fuad Tabba <tabba@google.com> wrote:
> > On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:

> > > +       /*
> > > +        * Note that we set the trap irrespective of EOIMode, as that
> > > +        * can change behind our back without any warning...
> > > +        */
> > > +       if (irqs_active_outside_lrs(als))
> > > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> > >  }

> > I just tested these patches as they are on kvmarm/next
> > 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> > 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> > additional pKVM patches. I tried running it with pKVM (non-protected)
> > and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> > when booting a non-protected guest, which triggers a bug in
> > handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706

> > This trap is happening because of setting this particular trap (TDIR).
> > Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> > kvmarm/next boots fine.

> This is surprising, as I'm not hitting this on actual HW. Are you
> getting a 0x18 trap? If so, is it coming from the host? Can you
> correlate the PC with what the host is doing?

FWIW I am seeing this on i.MX8MP (4xA53+GICv3):

  https://lava.sirena.org.uk/scheduler/job/2118713#L1044
 
I'm also seeing boot failures on AM625 in pKVM mode but much earlier,
before any console output, so there's some confounding issue in that
case (non-pKVM boots on the platform seemed fine, I'll investigate if
it's still a problem in today's -next).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-24 11:52       ` Mark Brown
@ 2025-11-24 13:06         ` Marc Zyngier
  2025-11-24 13:23           ` Mark Brown
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-24 13:06 UTC (permalink / raw)
  To: Mark Brown
  Cc: Fuad Tabba, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

On Mon, 24 Nov 2025 11:52:07 +0000,
Mark Brown <broonie@kernel.org> wrote:
> 
> [1  <text/plain; us-ascii (quoted-printable)>]
> On Fri, Nov 14, 2025 at 03:02:32PM +0000, Marc Zyngier wrote:
> > Fuad Tabba <tabba@google.com> wrote:
> > > On Sun, 9 Nov 2025 at 17:17, Marc Zyngier <maz@kernel.org> wrote:
> 
> > > > +       /*
> > > > +        * Note that we set the trap irrespective of EOIMode, as that
> > > > +        * can change behind our back without any warning...
> > > > +        */
> > > > +       if (irqs_active_outside_lrs(als))
> > > > +               cpuif->vgic_hcr |= ICH_HCR_EL2_TDIR;
> > > >  }
> 
> > > I just tested these patches as they are on kvmarm/next
> > > 2ea7215187c5759fc5d277280e3095b350ca6a50 ("Merge branch
> > > 'kvm-arm64/vgic-lr-overflow' into kvmarm/next"), without any
> > > additional pKVM patches. I tried running it with pKVM (non-protected)
> > > and with just plain nVHE. In both cases, I get a trap to EL2 (0x18)
> > > when booting a non-protected guest, which triggers a bug in
> > > handle_trap() arch/arm64/kvm/hyp/nvhe/hyp-main.c:706
> 
> > > This trap is happening because of setting this particular trap (TDIR).
> > > Just removing this trap from vgic_v3_configure_hcr() from the ToT on
> > > kvmarm/next boots fine.
> 
> > This is surprising, as I'm not hitting this on actual HW. Are you
> > getting a 0x18 trap? If so, is it coming from the host? Can you
> > correlate the PC with what the host is doing?
> 
> FWIW I am seeing this on i.MX8MP (4xA53+GICv3):
> 
>   https://lava.sirena.org.uk/scheduler/job/2118713#L1044

There are worrying errors way before that, in the VMID allocator init,
and I can't see what the GIC has to do with it. The issue Fuad
reported was at run time, not boot time. so this really doesn't align
with what you are seeing.

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-24 13:06         ` Marc Zyngier
@ 2025-11-24 13:23           ` Mark Brown
  2025-11-24 13:40             ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Mark Brown @ 2025-11-24 13:23 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Fuad Tabba, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

[-- Attachment #1: Type: text/plain, Size: 753 bytes --]

On Mon, Nov 24, 2025 at 01:06:29PM +0000, Marc Zyngier wrote:
> Mark Brown <broonie@kernel.org> wrote:

> > FWIW I am seeing this on i.MX8MP (4xA53+GICv3):

> >   https://lava.sirena.org.uk/scheduler/job/2118713#L1044

> There are worrying errors way before that, in the VMID allocator init,
> and I can't see what the GIC has to do with it. The issue Fuad
> reported was at run time, not boot time. so this really doesn't align
> with what you are seeing.

Yeah, I was just looking further and realising it was probably
different - sorry about that.  I was checking what else was failing
after seeing the qemu issue he was, all the platforms aren't booting one
way or another.  FWIW with earlycon on the AM625 is showing similar
issues to the i.MX8MP.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-24 13:23           ` Mark Brown
@ 2025-11-24 13:40             ` Marc Zyngier
  2025-11-24 14:12               ` Marc Zyngier
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-24 13:40 UTC (permalink / raw)
  To: Mark Brown
  Cc: Fuad Tabba, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

On Mon, 24 Nov 2025 13:23:08 +0000,
Mark Brown <broonie@kernel.org> wrote:
> 
> [1  <text/plain; us-ascii (7bit)>]
> On Mon, Nov 24, 2025 at 01:06:29PM +0000, Marc Zyngier wrote:
> > Mark Brown <broonie@kernel.org> wrote:
> 
> > > FWIW I am seeing this on i.MX8MP (4xA53+GICv3):
> 
> > >   https://lava.sirena.org.uk/scheduler/job/2118713#L1044
> 
> > There are worrying errors way before that, in the VMID allocator init,
> > and I can't see what the GIC has to do with it. The issue Fuad
> > reported was at run time, not boot time. so this really doesn't align
> > with what you are seeing.
> 
> Yeah, I was just looking further and realising it was probably
> different - sorry about that.  I was checking what else was failing
> after seeing the qemu issue he was, all the platforms aren't booting one
> way or another.  FWIW with earlycon on the AM625 is showing similar
> issues to the i.MX8MP.

That's the initial warning:

	WARN_ON(NUM_USER_VMIDS - 1 <= num_possible_cpus());

The register state:

[  224.378174] pc : kvm_arm_vmid_alloc_init+0xa0/0xc0
[  224.382954] lr : kvm_arm_vmid_alloc_init+0x24/0xc0
[  224.387734] sp : ffff80008009bd40
[  224.391035] x29: ffff80008009bd40 x28: ffff0020209bd3c0 x27: ffffce5349159068
[  224.398162] x26: ffffce5349070118 x25: ffffce5348fb8eb8 x24: ffffce5349059128
[  224.405287] x23: 0000000000000109 x22: ffff0020208ea6c0 x21: 0000000000000004
[  224.412413] x20: ffffce5349c20b78 x19: 0000000000000000 x18: 00000000ffffffff
[  224.419538] x17: 00000000e9a61a0d x16: 00000000b1c06f2c x15: 00000000ffffffff
[  224.426663] x14: 0000000000000000 x13: 7374696220343420 x12: 3a74696d694c2065
[  224.433789] x11: ffffffffffe00000 x10: ffff00275c260000 x9 : ffffce5348048be0
[  224.440914] x8 : 00000000fffeffff x7 : ffff00275c260000 x6 : 80000000ffff0000
[  224.448039] x5 : 0000000000000048 x4 : 0000000000000110 x3 : ffffce5348fc1000
[  224.455164] x2 : 0000000000000100 x1 : 0000000000000100 x0 : 00000000000000ff

The disassembly:

ffff8000816ff220 <kvm_arm_vmid_alloc_init>:
ffff8000816ff220:       d503201f        nop
ffff8000816ff224:       d503201f        nop
ffff8000816ff228:       d503233f        paciasp
ffff8000816ff22c:       a9be7bfd        stp     x29, x30, [sp, #-32]!
ffff8000816ff230:       5280e400        mov     w0, #0x720                      // #1824
ffff8000816ff234:       910003fd        mov     x29, sp
ffff8000816ff238:       72a00300        movk    w0, #0x18, lsl #16
ffff8000816ff23c:       f9000bf3        str     x19, [sp, #16]
ffff8000816ff240:       97a4a61c        bl      ffff800080028ab0 <read_sanitised_ftr_reg>
ffff8000816ff244:       d3441c00        ubfx    x0, x0, #4, #4
ffff8000816ff248:       d0fffa02        adrp    x2, ffff800081641000 <rodata_full>
ffff8000816ff24c:       d0fffa03        adrp    x3, ffff800081641000 <rodata_full>
ffff8000816ff250:       f100081f        cmp     x0, #0x2
ffff8000816ff254:       52800201        mov     w1, #0x10                       // #16
ffff8000816ff258:       b940f044        ldr     w4, [x2, #240]
ffff8000816ff25c:       52800102        mov     w2, #0x8                        // #8
ffff8000816ff260:       d29fffe0        mov     x0, #0xffff                     // #65535
ffff8000816ff264:       1a820021        csel    w1, w1, w2, eq  // eq = none
ffff8000816ff268:       d2801fe2        mov     x2, #0xff                       // #255
ffff8000816ff26c:       b9005061        str     w1, [x3, #80]
ffff8000816ff270:       9a820000        csel    x0, x0, x2, eq  // eq = none
ffff8000816ff274:       d2802001        mov     x1, #0x100                      // #256
ffff8000816ff278:       d2a00022        mov     x2, #0x10000                    // #65536
ffff8000816ff27c:       9a810042        csel    x2, x2, x1, eq  // eq = none
ffff8000816ff280:       eb00009f        cmp     x4, x0
ffff8000816ff284:       540001e2        b.cs    ffff8000816ff2c0 <kvm_arm_vmid_alloc_init+0xa0>  // b.hs, b.nlast

That's the branch to the...

[...]

ffff8000816ff2c0:       d4210000        brk     #0x800

... BRK instruction.

So x0=255 and x4=272. 272 possible CPUs on a machine with only 16?
Bollocks.

Something is badly screwed in -next, and I'm not convinced it is KVM.

	d0f23ccf6ba9e cpumask: Cache num_possible_cpus()

is my current suspect.

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-24 13:40             ` Marc Zyngier
@ 2025-11-24 14:12               ` Marc Zyngier
  2025-11-24 15:06                 ` Mark Brown
  0 siblings, 1 reply; 83+ messages in thread
From: Marc Zyngier @ 2025-11-24 14:12 UTC (permalink / raw)
  To: Mark Brown
  Cc: Fuad Tabba, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

On Mon, 24 Nov 2025 13:40:35 +0000,
Marc Zyngier <maz@kernel.org> wrote:
> 
> Something is badly screwed in -next, and I'm not convinced it is KVM.
> 
> 	d0f23ccf6ba9e cpumask: Cache num_possible_cpus()
> 
> is my current suspect.

Confirmed, and the fix is at 21782b3a5cd40892cb2995aa1ec3e74dd1112f1d.

	M.

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


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity
  2025-11-24 14:12               ` Marc Zyngier
@ 2025-11-24 15:06                 ` Mark Brown
  0 siblings, 0 replies; 83+ messages in thread
From: Mark Brown @ 2025-11-24 15:06 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Fuad Tabba, kvmarm, linux-arm-kernel, kvm, Joey Gouly,
	Suzuki K Poulose, Oliver Upton, Zenghui Yu, Christoffer Dall,
	Volodymyr Babchuk, Yao Yuan

[-- Attachment #1: Type: text/plain, Size: 455 bytes --]

On Mon, Nov 24, 2025 at 02:12:18PM +0000, Marc Zyngier wrote:
> Marc Zyngier <maz@kernel.org> wrote:

> > Something is badly screwed in -next, and I'm not convinced it is KVM.

> > 	d0f23ccf6ba9e cpumask: Cache num_possible_cpus()

> > is my current suspect.

> Confirmed, and the fix is at 21782b3a5cd40892cb2995aa1ec3e74dd1112f1d.

Great, thanks for checking.  Unfortunately it looks like that missed
today's -next but hopefully it'll show up tomorrow.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 83+ messages in thread

end of thread, other threads:[~2025-11-24 15:07 UTC | newest]

Thread overview: 83+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-09 17:15 [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 01/45] irqchip/gic: Add missing GICH_HCR control bits Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 02/45] irqchip/gic: Expose CPU interface VA to KVM Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 03/45] irqchip/apple-aic: Spit out ICH_MISR_EL2 value on spurious vGIC MI Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 04/45] KVM: arm64: Turn vgic-v3 errata traps into a patched-in constant Marc Zyngier
2025-11-10 10:40   ` Suzuki K Poulose
2025-11-10 11:47     ` Marc Zyngier
2025-11-11 23:53   ` Oliver Upton
2025-11-13  9:52   ` Marek Szyprowski
2025-11-13 10:56     ` Marc Zyngier
2025-11-13 11:04       ` Marek Szyprowski
2025-11-13 11:23         ` Joey Gouly
2025-11-13 11:42           ` Marc Zyngier
2025-11-13 10:59     ` Marc Zyngier
2025-11-13 11:20       ` Marek Szyprowski
2025-11-13 18:01   ` Mark Brown
2025-11-14  9:37     ` Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 05/45] KVM: arm64: GICv3: Detect and work around the lack of ICV_DIR_EL1 trapping Marc Zyngier
2025-11-13 14:33   ` Mark Brown
2025-11-13 18:15     ` Marc Zyngier
2025-11-13 19:06       ` Mark Brown
2025-11-13 20:10         ` Marc Zyngier
2025-11-13 21:59           ` Oliver Upton
2025-11-09 17:15 ` [PATCH v2 06/45] KVM: arm64: Repack struct vgic_irq fields Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 07/45] KVM: arm64: Add tracking of vgic_irq being present in a LR Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 08/45] KVM: arm64: Add LR overflow handling documentation Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 09/45] KVM: arm64: GICv3: Drop LPI active state when folding LRs Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 10/45] KVM: arm64: GICv3: Preserve EOIcount on exit Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 11/45] KVM: arm64: GICv3: Decouple ICH_HCR_EL2 programming from LRs Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 12/45] KVM: arm64: GICv3: Extract LR folding primitive Marc Zyngier
2025-11-10  9:01   ` Yao Yuan
2025-11-10  9:18     ` Marc Zyngier
2025-11-10  9:48       ` Yao Yuan
2025-11-09 17:15 ` [PATCH v2 13/45] KVM: arm64: GICv3: Extract LR computing primitive Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 14/45] KVM: arm64: GICv2: Preserve EOIcount on exit Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 15/45] KVM: arm64: GICv2: Decouple GICH_HCR programming from LRs being loaded Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 16/45] KVM: arm64: GICv2: Extract LR folding primitive Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 17/45] KVM: arm64: GICv2: Extract LR computing primitive Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 18/45] KVM: arm64: Compute vgic state irrespective of the number of interrupts Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 19/45] KVM: arm64: Eagerly save VMCR on exit Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 20/45] KVM: arm64: Revamp vgic maintenance interrupt configuration Marc Zyngier
2025-11-12  0:08   ` Oliver Upton
2025-11-12  8:33     ` Marc Zyngier
2025-11-12  8:45       ` Oliver Upton
2025-11-12  9:56         ` Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 21/45] KVM: arm64: Turn kvm_vgic_vcpu_enable() into kvm_vgic_vcpu_reset() Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 22/45] KVM: arm64: Make vgic_target_oracle() globally available Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 23/45] KVM: arm64: Invert ap_list sorting to push active interrupts out Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 24/45] KVM: arm64: Move undeliverable interrupts to the end of ap_list Marc Zyngier
2025-11-09 17:15 ` [PATCH v2 25/45] KVM: arm64: Use MI to detect groups being enabled/disabled Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 26/45] KVM: arm64: GICv3: Handle LR overflow when EOImode==0 Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 27/45] KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 28/45] KVM: arm64: GICv3: Add GICv2 SGI handling to deactivation primitive Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 29/45] KVM: arm64: GICv3: Set ICH_HCR_EL2.TDIR when interrupts overflow LR capacity Marc Zyngier
2025-11-14 14:20   ` Fuad Tabba
2025-11-14 15:02     ` Marc Zyngier
2025-11-14 15:53       ` Fuad Tabba
2025-11-14 17:41         ` Marc Zyngier
2025-11-17  8:22           ` Fuad Tabba
2025-11-17 11:56             ` Marc Zyngier
2025-11-24 11:52       ` Mark Brown
2025-11-24 13:06         ` Marc Zyngier
2025-11-24 13:23           ` Mark Brown
2025-11-24 13:40             ` Marc Zyngier
2025-11-24 14:12               ` Marc Zyngier
2025-11-24 15:06                 ` Mark Brown
2025-11-09 17:16 ` [PATCH v2 30/45] KVM: arm64: GICv3: Add SPI tracking to handle asymmetric deactivation Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 31/45] KVM: arm64: GICv3: Handle in-LR deactivation when possible Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 32/45] KVM: arm64: GICv3: Avoid broadcast kick on CPUs lacking TDIR Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 33/45] KVM: arm64: GICv2: Handle LR overflow when EOImode==0 Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 34/45] KVM: arm64: GICv2: Handle deactivation via GICV_DIR traps Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 35/45] KVM: arm64: GICv2: Always trap GICV_DIR register Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 36/45] KVM: arm64: selftests: gic_v3: Add irq group setting helper Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 37/45] KVM: arm64: selftests: gic_v3: Disable Group-0 interrupts by default Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 38/45] KVM: arm64: selftests: vgic_irq: Fix GUEST_ASSERT_IAR_EMPTY() helper Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 39/45] KVM: arm64: selftests: vgic_irq: Change configuration before enabling interrupt Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 40/45] KVM: arm64: selftests: vgic_irq: Exclude timer-controlled interrupts Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 41/45] KVM: arm64: selftests: vgic_irq: Remove LR-bound limitation Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 42/45] KVM: arm64: selftests: vgic_irq: Perform EOImode==1 deactivation in ack order Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 43/45] KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 44/45] KVM: arm64: selftests: vgic_irq: Add Group-0 enable test Marc Zyngier
2025-11-09 17:16 ` [PATCH v2 45/45] KVM: arm64: selftests: vgic_irq: Add timer deactivation test Marc Zyngier
2025-11-12  9:13 ` [PATCH v2 00/45] KVM: arm64: Add LR overflow infrastructure Oliver Upton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).