[PATCH 11/32] KVM: arm64: gic-v5: Trap and emulate ICH_PPI_HMRx_EL1 accesses

Sascha Bischoff Sascha.Bischoff at arm.com
Fri Dec 12 07:22:39 PST 2025


The ICC_PPI_HMRx_EL1 register is used to determine which PPIs use
Level-sensitive semantics, and which use Edge. For a GICv5 guest, the
correct view of the virtual PPIs must be provided to the guest.

The GICv5 architecture doesn't provide an ICV_PPI_HMRx_EL1 or
ICH_PPI_HMRx_EL2 register, and therefore all guest accesses must be
trapped to avoid the guest directly accessing the host's
ICC_PPI_HMRx_EL1 state. This change hence configures the FGTs to
always trap and emulate guest accesses to the HMR running a
GICv5-based guest.

This change also introduces the struct vgic_v5_cpu_if, which includes
the vgic_hmr. This is not yet populated as it can only be correctly
populated at vcpu reset time. This will be introduced in a subsquent
change.

Signed-off-by: Sascha Bischoff <sascha.bischoff at arm.com>
---
 arch/arm64/kvm/config.c   |  6 +++++-
 arch/arm64/kvm/sys_regs.c | 26 ++++++++++++++++++++++++++
 include/kvm/arm_vgic.h    |  5 +++++
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c
index cbdd8ac90f4d0..7683407ce052a 100644
--- a/arch/arm64/kvm/config.c
+++ b/arch/arm64/kvm/config.c
@@ -1586,8 +1586,12 @@ static void __compute_ich_hfgrtr(struct kvm_vcpu *vcpu)
 {
 	__compute_fgt(vcpu, ICH_HFGRTR_EL2);
 
-	/* ICC_IAFFIDR_EL1 *always* needs to be trapped when running a guest */
+	/*
+	 * ICC_IAFFIDR_EL1 and ICH_PPI_HMRx_EL1 *always* needs to be
+	 * trapped when running a guest.
+	 **/
 	*vcpu_fgt(vcpu, ICH_HFGRTR_EL2) &= ~ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1;
+	*vcpu_fgt(vcpu, ICH_HFGRTR_EL2) &= ~ICH_HFGRTR_EL2_ICC_PPI_HMRn_EL1;
 }
 
 void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 31c08fd591d08..a4ae034340040 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -699,6 +699,30 @@ static bool access_gicv5_iaffid(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
 	return true;
 }
 
+static bool access_gicv5_ppi_hmr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
+				 const struct sys_reg_desc *r)
+{
+	if (!vgic_is_v5(vcpu->kvm))
+		return undef_access(vcpu, p, r);
+
+	if (p->is_write)
+		return ignore_write(vcpu, p);
+
+	/*
+	 * For GICv5 VMs, the IAFFID value is the same as the VPE ID. The VPE ID
+	 * is the same as the VCPU's ID.
+	 */
+
+	if (p->Op2 == 0) {	/* ICC_PPI_HMR0_EL1 */
+		p->regval = vcpu->arch.vgic_cpu.vgic_v5.vgic_ppi_hmr[0];
+	} else {		/* ICC_PPI_HMR1_EL1 */
+		p->regval = vcpu->arch.vgic_cpu.vgic_v5.vgic_ppi_hmr[1];
+	}
+
+	return true;
+}
+
+
 static bool trap_raz_wi(struct kvm_vcpu *vcpu,
 			struct sys_reg_params *p,
 			const struct sys_reg_desc *r)
@@ -3429,6 +3453,8 @@ 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_PPI_HMR0_EL1), access_gicv5_ppi_hmr },
+	{ SYS_DESC(SYS_ICC_PPI_HMR1_EL1), access_gicv5_ppi_hmr },
 	{ SYS_DESC(SYS_ICC_IAFFIDR_EL1), access_gicv5_iaffid },
 	{ SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir },
 	{ SYS_DESC(SYS_ICC_RPR_EL1), undef_access },
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index be1f45a494f78..fbbaef4ad2114 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -358,11 +358,16 @@ struct vgic_v3_cpu_if {
 	unsigned int used_lrs;
 };
 
+struct vgic_v5_cpu_if {
+	u64	vgic_ppi_hmr[2];
+};
+
 struct vgic_cpu {
 	/* CPU vif control registers for world switch */
 	union {
 		struct vgic_v2_cpu_if	vgic_v2;
 		struct vgic_v3_cpu_if	vgic_v3;
+		struct vgic_v5_cpu_if	vgic_v5;
 	};
 
 	struct vgic_irq *private_irqs;
-- 
2.34.1



More information about the linux-arm-kernel mailing list