Re: [PATCH 10/32] KVM: arm64: gic-v5: Add emulation for ICC_IAFFID_EL1 accesses
From: Sascha Bischoff <hidden>
Date: 2025-12-16 10:58:55
Also in:
kvm, kvmarm
On Mon, 2025-12-15 at 17:31 +0000, Marc Zyngier wrote:
On Fri, 12 Dec 2025 15:22:38 +0000, Sascha Bischoff [off-list ref] wrote:quoted
GICv5 doesn't include an ICV_IAFFIDR_EL1 or ICH_IAFFIDR_EL2 for providing the IAFFID to the guest. A guest access to the ICH_IAFFIDR_EL1 must therefore be trapped and emulated to avoid thenit: ICC_IAFFIDR_EL1. There is no ICH_*_EL1 register.quoted
guest accessing the host's ICC_IAFFIDR_EL1. For GICv5, the VPE ID corresponds to the virtual IAFFID for the purposes of specifying the affinity of virtual interrupts. The VPE ID is the index into the VPE Table, which will be the same as the vcpu->vcpu_id once the various GICv5 VM tables are introduced. At this stage, said VM tables have yet to be introduced as they are not required for PPI support. Moreover, the IAFFID should go largely unused by any guest using just PPIs as they are not routable to a different PE. That said, we still need to trap and emulate the guest's accesses to avoid leaking host state into the guest.I think you can trim some of this. Just state that KVM makes the IAFFIDR, VPEID and vcpu_id the same thing, and that'll be good enough.quoted
The virtual IAFFID is provided to the guest when it reads ICC_IAFFID_EL1 (which always traps back to the hypervisor). Writes are rightly ignored. The trapping for the ICC_IAFFIDR_EL2 is always enabled when in a guest context.This register doesn't exist either.
Yikes. I really wasn't having a good day with my register names. I've addressed these now and have compacted down that paragraph.
quoted
Co-authored-by: Timothy Hayes [off-list ref] Signed-off-by: Timothy Hayes <redacted> Signed-off-by: Sascha Bischoff <redacted> --- arch/arm64/kvm/config.c | 10 +++++++++- arch/arm64/kvm/sys_regs.c | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-)diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c index 57ef67f718113..cbdd8ac90f4d0 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c@@ -1582,6 +1582,14 @@ static void __compute_hdfgwtr(structkvm_vcpu *vcpu) *vcpu_fgt(vcpu, HDFGWTR_EL2) |= HDFGWTR_EL2_MDSCR_EL1; } +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 */ + *vcpu_fgt(vcpu, ICH_HFGRTR_EL2) &= ~ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1;Slightly redundant when !GICv5 in the guest, but that's not really a problem.quoted
+} + void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu) { if (!cpus_have_final_cap(ARM64_HAS_FGT))@@ -1607,7 +1615,7 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) return; - __compute_fgt(vcpu, ICH_HFGRTR_EL2); + __compute_ich_hfgrtr(vcpu); __compute_fgt(vcpu, ICH_HFGWTR_EL2); __compute_fgt(vcpu, ICH_HFGITR_EL2); }diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index fbbd7b6ff6507..31c08fd591d08 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c@@ -681,6 +681,24 @@ static bool access_gic_dir(struct kvm_vcpu*vcpu, return true; } +static bool access_gicv5_iaffid(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);Shouldn't this be readily handled by the FGU configuration in the absence of GICv5 in the guest?
Yeah, it should be. This was written before GICv5 was plumbed into the FGUs. I've just tested it, and it undefs correctly without the explicit check, so I've dropped this. Thanks, Sascha
quoted
+ + 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. + */ + p->regval = FIELD_PREP(ICC_IAFFIDR_EL1_IAFFID, vcpu-quoted
vcpu_id);+ + return true; +} + static bool trap_raz_wi(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r)@@ -3411,6 +3429,7 @@ static const struct sys_reg_descsys_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_IAFFIDR_EL1), access_gicv5_iaffid }, { 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 },Thanks, M.