On Tue, May 06, 2025 at 05:43:38PM +0100, Marc Zyngier wrote: > Another benefit of mapping bits to features is that it becomes trivial > to define which bits should be handled as RES0. > > Let's apply this principle to the guest's view of the FGT registers. > > Signed-off-by: Marc Zyngier <maz@xxxxxxxxxx> > --- > arch/arm64/include/asm/kvm_host.h | 1 + > arch/arm64/kvm/config.c | 46 +++++++++++ > arch/arm64/kvm/nested.c | 129 +++--------------------------- > 3 files changed, 57 insertions(+), 119 deletions(-) > > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h > index 59bfb049ce987..0eff513167868 100644 > --- a/arch/arm64/include/asm/kvm_host.h > +++ b/arch/arm64/include/asm/kvm_host.h > @@ -1611,6 +1611,7 @@ void kvm_set_vm_id_reg(struct kvm *kvm, u32 reg, u64 val); > (kvm_has_feat((k), ID_AA64MMFR3_EL1, S1POE, IMP)) > > void compute_fgu(struct kvm *kvm, enum fgt_group_id fgt); > +void get_reg_fixed_bits(struct kvm *kvm, enum vcpu_sysreg reg, u64 *res0, u64*res1); nit: u64* res1 > void check_feature_map(void); > > #endif /* __ARM64_KVM_HOST_H__ */ > diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c > index 242c82eefd5e4..388f207bf64f6 100644 > --- a/arch/arm64/kvm/config.c > +++ b/arch/arm64/kvm/config.c > @@ -616,3 +616,49 @@ void compute_fgu(struct kvm *kvm, enum fgt_group_id fgt) > > kvm->arch.fgu[fgt] = val; > } > + > +void get_reg_fixed_bits(struct kvm *kvm, enum vcpu_sysreg reg, u64 *res0, u64 *res1) > +{ > + switch (reg) { > + case HFGRTR_EL2: > + *res0 = compute_res0_bits(kvm, hfgrtr_feat_map, > + ARRAY_SIZE(hfgrtr_feat_map), 0, 0); > + *res0 |= hfgrtr_masks.res0; > + *res1 = HFGRTR_EL2_RES1; > + break; > + case HFGWTR_EL2: > + *res0 = compute_res0_bits(kvm, hfgwtr_feat_map, > + ARRAY_SIZE(hfgwtr_feat_map), 0, 0); > + *res0 |= hfgwtr_masks.res0; > + *res1 = HFGWTR_EL2_RES1; > + break; > + case HFGITR_EL2: > + *res0 = compute_res0_bits(kvm, hfgitr_feat_map, > + ARRAY_SIZE(hfgitr_feat_map), 0, 0); > + *res0 |= hfgitr_masks.res0; > + *res1 = HFGITR_EL2_RES1; > + break; > + case HDFGRTR_EL2: > + *res0 = compute_res0_bits(kvm, hdfgrtr_feat_map, > + ARRAY_SIZE(hdfgrtr_feat_map), 0, 0); > + *res0 |= hdfgrtr_masks.res0; > + *res1 = HDFGRTR_EL2_RES1; > + break; > + case HDFGWTR_EL2: > + *res0 = compute_res0_bits(kvm, hdfgwtr_feat_map, > + ARRAY_SIZE(hdfgwtr_feat_map), 0, 0); > + *res0 |= hdfgwtr_masks.res0; > + *res1 = HDFGWTR_EL2_RES1; > + break; > + case HAFGRTR_EL2: > + *res0 = compute_res0_bits(kvm, hafgrtr_feat_map, > + ARRAY_SIZE(hafgrtr_feat_map), 0, 0); > + *res0 |= hafgrtr_masks.res0; > + *res1 = HAFGRTR_EL2_RES1; > + break; > + default: > + WARN_ON_ONCE(1); > + *res0 = *res1 = 0; > + break; > + } > +} > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c > index 666df85230c9b..3d91a0233652b 100644 > --- a/arch/arm64/kvm/nested.c > +++ b/arch/arm64/kvm/nested.c > @@ -1100,132 +1100,23 @@ int kvm_init_nv_sysregs(struct kvm_vcpu *vcpu) > set_sysreg_masks(kvm, HCRX_EL2, res0, res1); > > /* HFG[RW]TR_EL2 */ > - res0 = res1 = 0; > - if (!(kvm_vcpu_has_feature(kvm, KVM_ARM_VCPU_PTRAUTH_ADDRESS) && > - kvm_vcpu_has_feature(kvm, KVM_ARM_VCPU_PTRAUTH_GENERIC))) The change from checking kvm_vcpu_has_feature() to using feat_pauth() (from "KVM: arm64: Switch to table-driven FGU configuration" patch 31) is fine because the ID regs (ID_AA64ISAR1_EL, ID_AA64ISAR2_EL) are sanitised by __kvm_read_sanitised_id_reg(), which checks vcpu_has_feature() via vcpu_has_ptrauth(). > - res0 |= (HFGRTR_EL2_APDAKey | HFGRTR_EL2_APDBKey | > - HFGRTR_EL2_APGAKey | HFGRTR_EL2_APIAKey | > - HFGRTR_EL2_APIBKey); > - if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, LO, IMP)) > - res0 |= (HFGRTR_EL2_LORC_EL1 | HFGRTR_EL2_LOREA_EL1 | > - HFGRTR_EL2_LORID_EL1 | HFGRTR_EL2_LORN_EL1 | > - HFGRTR_EL2_LORSA_EL1); > - if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, CSV2, CSV2_2) && > - !kvm_has_feat(kvm, ID_AA64PFR1_EL1, CSV2_frac, CSV2_1p2)) > - res0 |= (HFGRTR_EL2_SCXTNUM_EL1 | HFGRTR_EL2_SCXTNUM_EL0); > - if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, GIC, IMP)) > - res0 |= HFGRTR_EL2_ICC_IGRPENn_EL1; > - if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, RAS, IMP)) > - res0 |= (HFGRTR_EL2_ERRIDR_EL1 | HFGRTR_EL2_ERRSELR_EL1 | > - HFGRTR_EL2_ERXFR_EL1 | HFGRTR_EL2_ERXCTLR_EL1 | > - HFGRTR_EL2_ERXSTATUS_EL1 | HFGRTR_EL2_ERXMISCn_EL1 | > - HFGRTR_EL2_ERXPFGF_EL1 | HFGRTR_EL2_ERXPFGCTL_EL1 | > - HFGRTR_EL2_ERXPFGCDN_EL1 | HFGRTR_EL2_ERXADDR_EL1); > - if (!kvm_has_feat(kvm, ID_AA64ISAR1_EL1, LS64, LS64_ACCDATA)) > - res0 |= HFGRTR_EL2_nACCDATA_EL1; > - if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, GCS, IMP)) > - res0 |= (HFGRTR_EL2_nGCS_EL0 | HFGRTR_EL2_nGCS_EL1); > - if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, SME, IMP)) > - res0 |= (HFGRTR_EL2_nSMPRI_EL1 | HFGRTR_EL2_nTPIDR2_EL0); > - if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, THE, IMP)) > - res0 |= HFGRTR_EL2_nRCWMASK_EL1; > - if (!kvm_has_s1pie(kvm)) > - res0 |= (HFGRTR_EL2_nPIRE0_EL1 | HFGRTR_EL2_nPIR_EL1); > - if (!kvm_has_s1poe(kvm)) > - res0 |= (HFGRTR_EL2_nPOR_EL0 | HFGRTR_EL2_nPOR_EL1); > - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S2POE, IMP)) > - res0 |= HFGRTR_EL2_nS2POR_EL1; > - if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, AIE, IMP)) > - res0 |= (HFGRTR_EL2_nMAIR2_EL1 | HFGRTR_EL2_nAMAIR2_EL1); > - set_sysreg_masks(kvm, HFGRTR_EL2, res0 | hfgrtr_masks.res0, res1); > - set_sysreg_masks(kvm, HFGWTR_EL2, res0 | hfgwtr_masks.res0, res1); > + get_reg_fixed_bits(kvm, HFGRTR_EL2, &res0, &res1); > + set_sysreg_masks(kvm, HFGRTR_EL2, res0, res1); > + get_reg_fixed_bits(kvm, HFGWTR_EL2, &res0, &res1); > + set_sysreg_masks(kvm, HFGWTR_EL2, res0, res1); > > /* HDFG[RW]TR_EL2 */ > - res0 = res1 = 0; > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, DoubleLock, IMP)) > - res0 |= HDFGRTR_EL2_OSDLR_EL1; > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, IMP)) > - res0 |= (HDFGRTR_EL2_PMEVCNTRn_EL0 | HDFGRTR_EL2_PMEVTYPERn_EL0 | > - HDFGRTR_EL2_PMCCFILTR_EL0 | HDFGRTR_EL2_PMCCNTR_EL0 | > - HDFGRTR_EL2_PMCNTEN | HDFGRTR_EL2_PMINTEN | > - HDFGRTR_EL2_PMOVS | HDFGRTR_EL2_PMSELR_EL0 | > - HDFGRTR_EL2_PMMIR_EL1 | HDFGRTR_EL2_PMUSERENR_EL0 | > - HDFGRTR_EL2_PMCEIDn_EL0); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, IMP)) > - res0 |= (HDFGRTR_EL2_PMBLIMITR_EL1 | HDFGRTR_EL2_PMBPTR_EL1 | > - HDFGRTR_EL2_PMBSR_EL1 | HDFGRTR_EL2_PMSCR_EL1 | > - HDFGRTR_EL2_PMSEVFR_EL1 | HDFGRTR_EL2_PMSFCR_EL1 | > - HDFGRTR_EL2_PMSICR_EL1 | HDFGRTR_EL2_PMSIDR_EL1 | > - HDFGRTR_EL2_PMSIRR_EL1 | HDFGRTR_EL2_PMSLATFR_EL1 | > - HDFGRTR_EL2_PMBIDR_EL1); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceVer, IMP)) > - res0 |= (HDFGRTR_EL2_TRC | HDFGRTR_EL2_TRCAUTHSTATUS | > - HDFGRTR_EL2_TRCAUXCTLR | HDFGRTR_EL2_TRCCLAIM | > - HDFGRTR_EL2_TRCCNTVRn | HDFGRTR_EL2_TRCID | > - HDFGRTR_EL2_TRCIMSPECn | HDFGRTR_EL2_TRCOSLSR | > - HDFGRTR_EL2_TRCPRGCTLR | HDFGRTR_EL2_TRCSEQSTR | > - HDFGRTR_EL2_TRCSSCSRn | HDFGRTR_EL2_TRCSTATR | > - HDFGRTR_EL2_TRCVICTLR); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceBuffer, IMP)) > - res0 |= (HDFGRTR_EL2_TRBBASER_EL1 | HDFGRTR_EL2_TRBIDR_EL1 | > - HDFGRTR_EL2_TRBLIMITR_EL1 | HDFGRTR_EL2_TRBMAR_EL1 | > - HDFGRTR_EL2_TRBPTR_EL1 | HDFGRTR_EL2_TRBSR_EL1 | > - HDFGRTR_EL2_TRBTRG_EL1); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP)) > - res0 |= (HDFGRTR_EL2_nBRBIDR | HDFGRTR_EL2_nBRBCTL | > - HDFGRTR_EL2_nBRBDATA); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMSVer, V1P2)) > - res0 |= HDFGRTR_EL2_nPMSNEVFR_EL1; > - set_sysreg_masks(kvm, HDFGRTR_EL2, res0 | hdfgrtr_masks.res0, res1); > - > - /* Reuse the bits from the read-side and add the write-specific stuff */ > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, IMP)) > - res0 |= (HDFGWTR_EL2_PMCR_EL0 | HDFGWTR_EL2_PMSWINC_EL0); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceVer, IMP)) > - res0 |= HDFGWTR_EL2_TRCOSLAR; > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, TraceFilt, IMP)) > - res0 |= HDFGWTR_EL2_TRFCR_EL1; > - set_sysreg_masks(kvm, HFGWTR_EL2, res0 | hdfgwtr_masks.res0, res1); > + get_reg_fixed_bits(kvm, HDFGRTR_EL2, &res0, &res1); > + set_sysreg_masks(kvm, HDFGRTR_EL2, res0, res1); > + get_reg_fixed_bits(kvm, HDFGWTR_EL2, &res0, &res1); > + set_sysreg_masks(kvm, HDFGWTR_EL2, res0, res1); > > /* HFGITR_EL2 */ > - res0 = hfgitr_masks.res0; > - res1 = HFGITR_EL2_RES1; > - if (!kvm_has_feat(kvm, ID_AA64ISAR1_EL1, DPB, DPB2)) > - res0 |= HFGITR_EL2_DCCVADP; > - if (!kvm_has_feat(kvm, ID_AA64MMFR1_EL1, PAN, PAN2)) > - res0 |= (HFGITR_EL2_ATS1E1RP | HFGITR_EL2_ATS1E1WP); > - if (!kvm_has_feat(kvm, ID_AA64ISAR0_EL1, TLB, OS)) > - res0 |= (HFGITR_EL2_TLBIRVAALE1OS | HFGITR_EL2_TLBIRVALE1OS | > - HFGITR_EL2_TLBIRVAAE1OS | HFGITR_EL2_TLBIRVAE1OS | > - HFGITR_EL2_TLBIVAALE1OS | HFGITR_EL2_TLBIVALE1OS | > - HFGITR_EL2_TLBIVAAE1OS | HFGITR_EL2_TLBIASIDE1OS | > - HFGITR_EL2_TLBIVAE1OS | HFGITR_EL2_TLBIVMALLE1OS); > - if (!kvm_has_feat(kvm, ID_AA64ISAR0_EL1, TLB, RANGE)) > - res0 |= (HFGITR_EL2_TLBIRVAALE1 | HFGITR_EL2_TLBIRVALE1 | > - HFGITR_EL2_TLBIRVAAE1 | HFGITR_EL2_TLBIRVAE1 | > - HFGITR_EL2_TLBIRVAALE1IS | HFGITR_EL2_TLBIRVALE1IS | > - HFGITR_EL2_TLBIRVAAE1IS | HFGITR_EL2_TLBIRVAE1IS | > - HFGITR_EL2_TLBIRVAALE1OS | HFGITR_EL2_TLBIRVALE1OS | > - HFGITR_EL2_TLBIRVAAE1OS | HFGITR_EL2_TLBIRVAE1OS); > - if (!kvm_has_feat(kvm, ID_AA64ISAR1_EL1, SPECRES, IMP)) > - res0 |= (HFGITR_EL2_CFPRCTX | HFGITR_EL2_DVPRCTX | > - HFGITR_EL2_CPPRCTX); > - if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP)) > - res0 |= (HFGITR_EL2_nBRBINJ | HFGITR_EL2_nBRBIALL); > - if (!kvm_has_feat(kvm, ID_AA64PFR1_EL1, GCS, IMP)) > - res0 |= (HFGITR_EL2_nGCSPUSHM_EL1 | HFGITR_EL2_nGCSSTR_EL1 | > - HFGITR_EL2_nGCSEPP); > - if (!kvm_has_feat(kvm, ID_AA64ISAR1_EL1, SPECRES, COSP_RCTX)) > - res0 |= HFGITR_EL2_COSPRCTX; > - if (!kvm_has_feat(kvm, ID_AA64ISAR2_EL1, ATS1A, IMP)) > - res0 |= HFGITR_EL2_ATS1E1A; > + get_reg_fixed_bits(kvm, HFGITR_EL2, &res0, &res1); > set_sysreg_masks(kvm, HFGITR_EL2, res0, res1); > > /* HAFGRTR_EL2 - not a lot to see here */ > - res0 = hafgrtr_masks.res0; > - res1 = HAFGRTR_EL2_RES1; > - if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, AMU, V1P1)) > - res0 |= ~(res0 | res1); > + get_reg_fixed_bits(kvm, HAFGRTR_EL2, &res0, &res1); > set_sysreg_masks(kvm, HAFGRTR_EL2, res0, res1); > > /* TCR2_EL2 */ Reviewed-by: Joey Gouly <joey.gouly@xxxxxxx>