KVM: arm64: Add support for FEAT_IDST
Marc Zyngier
maintainercover KVM: arm64: Add support for FEAT_IDST
FEAT_IDST appeared in ARMv8.4, and allows ID registers to be trapped if they are not implemented. This only concerns 3 registers (GMID_EL1, CCSIDR2_EL1 and SMIDR_EL1), which are part of features that may not be exposed to the guest even if present on the host.
For these registers, the HW should report them with EC=0x18, even if the feature isn't implemented.
Add support for this feature by handling these registers in a specific way and implementing GMID_EL1 support in the process. A very basic selftest checks that these registers behave as expected.
Marc Zyngier (5):
KVM: arm64: Add routing/handling for GMID_EL1
KVM: arm64: Force trap of GMID_EL1 when the guest doesn't have MTE
KVM: arm64: Add a generic synchronous exception injection primitive
KVM: arm64: Report optional ID register traps with a 0x18 syndrome
KVM: arm64: selftests: Add a test for FEAT_IDST
arch/arm64/include/asm/kvm_emulate.h | 1 + arch/arm64/kvm/emulate-nested.c | 8 ++ arch/arm64/kvm/inject_fault.c | 10 +- arch/arm64/kvm/sys_regs.c | 17 ++- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/arm64/idreg-idst.c | 117 ++++++++++++++++++ 6 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 tools/testing/selftests/kvm/arm64/idreg-idst.c
Signature · 1 line
2.47.3
Marc Zyngier
maintainerPATCH 1/5 KVM: arm64: Add routing/handling for GMID_EL1
HCR_EL2.TID5 is currently ignored by the trap routing infrastructure, and we currently don't handle GMID_EL1 either (the only register trapped by TID5).
Wire both the trap bit and a default UNDEF handler.
--- arch/arm64/kvm/emulate-nested.c | 8 ++++++++ arch/arm64/kvm/sys_regs.c | 1 + 2 files changed, 9 insertions(+)
arch/arm64/kvm/emulate-nested.c +8 -0
@@ -70,6 +70,7 @@ enum cgt_group_id {CGT_HCR_ENSCXT ,CGT_HCR_TTLBIS ,CGT_HCR_TTLBOS , +CGT_HCR_TID5 ,CGT_MDCR_TPMCR , CGT_MDCR_TPM,
@@ -308,6 +309,12 @@ static const struct trap_bits coarse_trap_bits[] = { .mask = HCR_TTLBOS, .behaviour = BEHAVE_FORWARD_RW, }, + [CGT_HCR_TID5] = { + .index = HCR_EL2, + .value = HCR_TID5, + .mask = HCR_TID5, + .behaviour = BEHAVE_FORWARD_RW, + }, [CGT_MDCR_TPMCR] = { .index = MDCR_EL2, .value = MDCR_EL2_TPMCR,
@@ -665,6 +672,7 @@ static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = {SR_TRAP (SYS_CCSIDR2_EL1 ,CGT_HCR_TID2_TID4 ),SR_TRAP (SYS_CLIDR_EL1 ,CGT_HCR_TID2_TID4 ),SR_TRAP (SYS_CSSELR_EL1 ,CGT_HCR_TID2_TID4 ), +SR_TRAP (SYS_GMID_EL1 ,CGT_HCR_TID5 ),SR_RANGE_TRAP (SYS_ID_PFR0_EL1 ,sys_reg (3 ,0 ,0 ,7 ,7 ),CGT_HCR_TID3 ), SR_TRAP(SYS_ICC_SGI0R_EL1, CGT_HCR_IMO_FMO_ICH_HCR_TC),
arch/arm64/kvm/sys_regs.c +1 -0
@@ -3397,6 +3397,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { {SYS_DESC (SYS_CLIDR_EL1 ), access_clidr, reset_clidr,CLIDR_EL1 , .set_user = set_clidr, .val = ~CLIDR_EL1_RES0 }, { SYS_DESC(SYS_CCSIDR2_EL1), undef_access }, + { SYS_DESC(SYS_GMID_EL1), undef_access }, { SYS_DESC(SYS_SMIDR_EL1), undef_access },IMPLEMENTATION_ID (AIDR_EL1 ,GENMASK_ULL (63 ,0 )), { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 },
Signature · 1 line
2.47.3
Marc Zyngier
maintainerPATCH 2/5 KVM: arm64: Force trap of GMID_EL1 when the guest doesn't have MTE
In reply to Marc ZyngierIf our host has MTE, but the guest doesn't, make sure we set HCR_EL2.TID5 to force GMID_EL1 being trapped.
--- arch/arm64/kvm/sys_regs.c | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/kvm/sys_regs.c +2 -0
@@ -5558,6 +5558,8 @@ static void vcpu_set_hcr(struct kvm_vcpu *vcpu)if (kvm_has_mte (vcpu->kvm)) vcpu->arch.hcr_el2 |=HCR_ATA ; +else if (id_aa64pfr1_mte (read_sanitised_ftr_reg (SYS_ID_AA64PFR1_EL1 ))) + vcpu->arch.hcr_el2 |=HCR_TID5 ;/* * In the absence of FGT, we cannot independently trap TLBI
Signature · 1 line
2.47.3
Re: [PATCH 2/5] KVM: arm64: Force trap of GMID_EL1 when the guest doesn't have MTE
Quoted 13 lines · Marc Zyngier
If our host has MTE, but the guest doesn't, make sure we set HCR_EL2.TID5 to force
GMID_EL1being trapped.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kvm/sys_regs.c | 2 ++ 1 file changed, 2 insertions(+)arch/arm64/kvm/sys_regs.c +0
-0
arch/arm64/kvm/sys_regs.c static void vcpu_set_hcr(struct kvm_vcpu *vcpu) · line 5562
Quoted 5 lines
arch/arm64/kvm/sys_regs.c +2
-0@@ -5558,6 +5558,8 @@ static void vcpu_set_hcr(struct kvm_vcpu *vcpu)if (kvm_has_mte (vcpu->kvm)) vcpu->arch.hcr_el2 |=HCR_ATA ; +else if (id_aa64pfr1_mte (read_sanitised_ftr_reg (SYS_ID_AA64PFR1_EL1 ))) + vcpu->arch.hcr_el2 |=HCR_TID5 ;
This is because we want to enable the trapping regardless of CONFIG_ARM64_MTE (so we can't use system_supports_mte()).
Quoted 6 lines
arch/arm64/kvm/sys_regs.c +0
-0/* * In the absence of FGT, we cannot independently trap TLBI Signature · 1 line
2.47.3
Marc Zyngier
maintainerRe: [PATCH 2/5] KVM: arm64: Force trap of GMID_EL1 when the guest doesn't have MTE
Quoted 16 lines · Joey Gouly
Quoted 14 lines · Marc Zyngier
If our host has MTE, but the guest doesn't, make sure we set HCR_EL2.TID5 to force
GMID_EL1being trapped.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kvm/sys_regs.c | 2 ++ 1 file changed, 2 insertions(+)arch/arm64/kvm/sys_regs.c +0
-0@@ -5558,6 +5558,8 @@ static void vcpu_set_hcr(struct kvm_vcpu *vcpu)
Quoted 7 lines
arch/arm64/kvm/sys_regs.c static void vcpu_set_hcr(struct kvm_vcpu *vcpu) · line 5562
Quoted 4 lines
arch/arm64/kvm/sys_regs.c +2
-0if (kvm_has_mte (vcpu->kvm)) vcpu->arch.hcr_el2 |=HCR_ATA ; +else if (id_aa64pfr1_mte (read_sanitised_ftr_reg (SYS_ID_AA64PFR1_EL1 ))) + vcpu->arch.hcr_el2 |=HCR_TID5 ;This is because we want to enable the trapping regardless of
CONFIG_ARM64_MTE(so we can't usesystem_supports_mte()).
Indeed. In general, we cannot rely on the kernel's own configuration, since the guest sees most of the actual HW.
Quoted 2 lines
Reviewed-by Joey Gouly <[email protected]>
Thanks!
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Marc Zyngier
maintainerPATCH 3/5 KVM: arm64: Add a generic synchronous exception injection primitive
In reply to Marc ZyngierMaybe in a surprising way, we don't currently have a generic way to inject a synchronous exception at the EL the vcpu is currently running at.
Extract such primitive from the UNDEF injection code.
--- arch/arm64/include/asm/kvm_emulate.h | 1 + arch/arm64/kvm/inject_fault.c | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +1 -0
@@ -45,6 +45,7 @@ bool kvm_condition_valid32(const struct kvm_vcpu *vcpu);void kvm_skip_instr32 (struct kvm_vcpu *vcpu);void kvm_inject_undefined (struct kvm_vcpu *vcpu); +void kvm_inject_sync (struct kvm_vcpu *vcpu,u64 esr);int kvm_inject_serror_esr (struct kvm_vcpu *vcpu,u64 esr);int kvm_inject_sea (struct kvm_vcpu *vcpu,bool iabt,u64 addr);void kvm_inject_size_fault (struct kvm_vcpu *vcpu);
arch/arm64/kvm/inject_fault.c +7 -3
@@ -162,12 +162,16 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addrvcpu_write_sys_reg (vcpu, esr,exception_esr_elx (vcpu)); } +void kvm_inject_sync (struct kvm_vcpu *vcpu,u64 esr) +{ +pend_sync_exception (vcpu); +vcpu_write_sys_reg (vcpu, esr,exception_esr_elx (vcpu)); +} +static void inject_undef64 (struct kvm_vcpu *vcpu) {u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); -pend_sync_exception (vcpu); -/* * Build an unknown exception, depending on the instruction * set.
@@ -175,7 +179,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu)if (kvm_vcpu_trap_il_is32bit (vcpu)) esr |= ESR_ELx_IL; -vcpu_write_sys_reg (vcpu, esr,exception_esr_elx (vcpu)); +kvm_inject_sync (vcpu, esr); }#define DFSR_FSC_EXTABT_LPAE 0x10
Signature · 1 line
2.47.3
Marc Zyngier
maintainerPATCH 4/5 KVM: arm64: Report optional ID register traps with a 0x18 syndrome
In reply to Marc ZyngierWith FEAT_IDST, unimplemented system registers must be reported using EC=0x18 at the closest handling EL, rather than with an UNDEF.
Most system registers are always implemented thanks to their dependency on FEAT_AA64, except for a set of (currently) three registers:GMID_EL1 (depending on MTE2), CCSIDR2_EL1 (depending on FEAT_CCIDX), and SMIDR_EL1 (depending on SME).
For these three registers, report their trap as EC=0x18 if they end-up trapping into KVM and that FEAT_IDST is not implemented in the guest. Otherwise, just make them UNDEF.
--- arch/arm64/kvm/sys_regs.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
arch/arm64/kvm/sys_regs.c +13 -3
@@ -82,6 +82,16 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu, "sys_reg write to read-only register"); } +static bool idst_access (struct kvm_vcpu *vcpu,struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (kvm_has_feat_enum (vcpu->kvm,ID_AA64MMFR2_EL1 ,IDS ,0x0 )) +return undef_access (vcpu, p, r); + +kvm_inject_sync (vcpu,kvm_vcpu_get_esr (vcpu)); +return false; +} +enum sr_loc_attr {SR_LOC_MEMORY =0 ,/* Register definitely in memory */ SR_LOC_LOADED =BIT (0 ),/* Register on CPU, unless it cannot */
@@ -3396,9 +3406,9 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CCSIDR_EL1), access_ccsidr }, { SYS_DESC(SYS_CLIDR_EL1), access_clidr, reset_clidr, CLIDR_EL1, .set_user = set_clidr, .val = ~CLIDR_EL1_RES0 }, - { SYS_DESC(SYS_CCSIDR2_EL1), undef_access }, - { SYS_DESC(SYS_GMID_EL1), undef_access }, - { SYS_DESC(SYS_SMIDR_EL1), undef_access }, + { SYS_DESC(SYS_CCSIDR2_EL1), idst_access }, + { SYS_DESC(SYS_GMID_EL1), idst_access }, + { SYS_DESC(SYS_SMIDR_EL1), idst_access },IMPLEMENTATION_ID (AIDR_EL1 ,GENMASK_ULL (63 ,0 )), { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 }, ID_FILTERED(CTR_EL0, ctr_el0,
Signature · 1 line
2.47.3
Re: [PATCH 4/5] KVM: arm64: Report optional ID register traps with a 0x18 syndrome
Hi Marc,
Quoted 2 lines · Marc Zyngier
With
FEAT_IDST, unimplemented system registers must be reported using EC=0x18 at the closest handling EL, rather than with an UNDEF.
I think this needs 'in the feature ID space' adding. Something like:
With FEAT_IDST, unimplemented system registers in the feature ID space must be reported using EC=0x18 at the closest handling EL, rather than with an UNDEF.
Quoted 2 lines
Most system registers are always implemented thanks to their dependency
'Most of these system registers...'
Quoted 3 lines
on
FEAT_AA64, except for a set of (currently) three registers:GMID_EL1(depending on MTE2),CCSIDR2_EL1(depending onFEAT_CCIDX), andSMIDR_EL1(depending on SME).
I agree that these 3 are currently the only optional system registers in the feature ID space.
Quoted 34 lines
For these three registers, report their trap as EC=0x18 if they end-up trapping into KVM and that
FEAT_IDSTis not implemented in the guest. Otherwise, just make them UNDEF.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kvm/sys_regs.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)arch/arm64/kvm/sys_regs.c +10
-0@@ -82,6 +82,16 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu, "sys_reg write to read-only register"); } +static bool idst_access (struct kvm_vcpu *vcpu,struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (kvm_has_feat_enum (vcpu->kvm,ID_AA64MMFR2_EL1 ,IDS ,0x0 )) +return undef_access (vcpu, p, r); + +kvm_inject_sync (vcpu,kvm_vcpu_get_esr (vcpu)); +return false; +} +enum sr_loc_attr {SR_LOC_MEMORY =0 ,/* Register definitely in memory */ SR_LOC_LOADED =BIT (0 ),/* Register on CPU, unless it cannot */ @@ -3396,9 +3406,9 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CCSIDR_EL1), access_ccsidr }, { SYS_DESC(SYS_CLIDR_EL1), access_clidr, reset_clidr, CLIDR_EL1,
arch/arm64/kvm/sys_regs.c static const struct sys_reg_desc sys_reg_descs[] = { · line 3414
Quoted 10 lines
arch/arm64/kvm/sys_regs.c +3
-3.set_user = set_clidr, .val = ~CLIDR_EL1_RES0 }, - { SYS_DESC(SYS_CCSIDR2_EL1), undef_access }, - { SYS_DESC(SYS_GMID_EL1), undef_access }, - { SYS_DESC(SYS_SMIDR_EL1), undef_access }, + { SYS_DESC(SYS_CCSIDR2_EL1), idst_access }, + { SYS_DESC(SYS_GMID_EL1), idst_access }, + { SYS_DESC(SYS_SMIDR_EL1), idst_access },IMPLEMENTATION_ID (AIDR_EL1 ,GENMASK_ULL (63 ,0 )), { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 }, ID_FILTERED(CTR_EL0, ctr_el0,
Thanks,
Ben
Marc Zyngier
maintainerRe: [PATCH 4/5] KVM: arm64: Report optional ID register traps with a 0x18 syndrome
Quoted 4 lines · Ben Horgan
Hi Marc,
On 11/20/25 13:32, Marc Zyngier wrote:
Quoted 8 lines
Quoted 2 lines
With
FEAT_IDST, unimplemented system registers must be reported using EC=0x18 at the closest handling EL, rather than with an UNDEF.I think this needs 'in the feature ID space' adding. Something like:
With
FEAT_IDST, unimplemented system registers in the feature ID space must be reported using EC=0x18 at the closest handling EL, rather than with an UNDEF.
Ah, good point. I got (slightly) carried away!
I'll fold these updates in.
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Marc Zyngier
maintainerPATCH 5/5 KVM: arm64: selftests: Add a test for FEAT_IDST
In reply to Marc ZyngierAdd a very basic test checking that FEAT_IDST actually works for the {GMID,SMIDR,CSSIDR2}_EL1 registers.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../testing/selftests/kvm/arm64/idreg-idst.c | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tools/testing/selftests/kvm/arm64/idreg-idst.c
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -171,6 +171,7 @@ TEST_GEN_PROGS_arm64 += arm64/vgic_irq TEST_GEN_PROGS_arm64 += arm64/vgic_lpi_stress TEST_GEN_PROGS_arm64 += arm64/vpmu_counter_access TEST_GEN_PROGS_arm64 += arm64/no-vgic-v3 +TEST_GEN_PROGS_arm64 += arm64/idreg-idst TEST_GEN_PROGS_arm64 += arm64/kvm-uuid TEST_GEN_PROGS_arm64 += access_tracking_perf_test TEST_GEN_PROGS_arm64 += arch_timer
tools/testing/selftests/kvm/arm64/idreg-idst.c new file +117 -0
117 lines not on this page · raw message
Signature · 1 line
2.47.3