KVM: arm64: Another set of CSV2-related fixes
Marc Zyngier
maintainerv2 cover KVM: arm64: Another set of CSV2-related fixes
This small series addresses a couple of Spectre-v2 related issues:
- Fix a live migration regression introduced with the setting of CSV2 on systems that are not affected by Spectre-v2, but that don't directly expose it in ID_AA64PFR0_EL1
- Inject an UNDEF exception if the guest tries to access any of SCXTNUM_ELx, as we don't advertise it to guests.
Patches on top of 5.10-rc2.
* From v1:
- Only register a new value for CSV2 on a valid write to ID_AA64PFR0_EL1
- Delete even more code in patch #2
Marc Zyngier (3):
KVM: arm64: Allow setting of ID_AA64PFR0_EL1.CSV2 from userspace
KVM: arm64: Unify trap handlers injecting an UNDEF
KVM: arm64: Handle SCXTNUM_ELx traps
arch/arm64/include/asm/kvm_host.h | 2 + arch/arm64/include/asm/sysreg.h | 4 ++ arch/arm64/kvm/arm.c | 16 +++++ arch/arm64/kvm/sys_regs.c | 111 ++++++++++++++++++------------ 4 files changed, 89 insertions(+), 44 deletions(-)
Signature · 1 line
2.28.0
Marc Zyngier
maintainerPATCH v2 1/3 KVM: arm64: Allow setting of ID_AA64PFR0_EL1.CSV2 from userspace
We now expose ID_AA64PFR0_EL1.CSV2=1 to guests running on hosts that are immune to Spectre-v2, but that don't have this field set, most likely because they predate the specification.
However, this prevents the migration of guests that have started on a host the doesn't fake this CSV2 setting to one that does, as KVM rejects the write to ID_AA64PFR0_EL2 on the grounds that it isn't what is already there.
In order to fix this, allow userspace to set this field as long as this doesn't result in a promising more than what is already there (setting CSV2 to 0 is acceptable, but setting it to 1 when it is already set to 0 isn't).
--- arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/kvm/arm.c | 16 ++++++++++++ arch/arm64/kvm/sys_regs.c | 42 ++++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-)
arch/arm64/include/asm/kvm_host.h +2 -0
@@ -118,6 +118,8 @@ struct kvm_arch {*/ unsigned long *pmu_filter;unsigned int pmuver; + +u8 pfr0_csv2; };struct kvm_vcpu_fault_info {
arch/arm64/kvm/arm.c +16 -0
@@ -102,6 +102,20 @@ static int kvm_arm_default_max_vcpus(void)return vgic_present ?kvm_vgic_get_max_vcpus () :KVM_MAX_VCPUS ; } +static void set_default_csv2 (struct kvm *kvm) +{ +/* +* The default is to expose CSV2 == 1 if the HW isn't affected. +* Although this is a per-CPU feature, we make it global because +* asymmetric systems are just a nuisance. +* +* Userspace can override this as long as it doesn't promise +* the impossible. +*/ +if (arm64_get_spectre_v2_state () ==SPECTRE_UNAFFECTED ) + kvm->arch.pfr0_csv2 =1 ; +} +/** * kvm_arch_init_vm - initializes a VM data structure * @kvm: pointer to the KVM struct
@@ -127,6 +141,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)/* The maximum number of VCPUs is limited by the host's GIC model */ kvm->arch.max_vcpus =kvm_arm_default_max_vcpus (); +set_default_csv2 (kvm); +return ret; out_free_stage2_pgd:kvm_free_stage2_pgd (&kvm->arch.mmu);
arch/arm64/kvm/sys_regs.c +38 -4
60 lines not on this page · raw message
Signature · 1 line
2.28.0
Re: [PATCH v2 1/3] KVM: arm64: Allow setting of ID_AA64PFR0_EL1.CSV2 from userspace
Quoted 14 lines · Marc Zyngier
We now expose ID_AA64PFR0_EL1.CSV2=1 to guests running on hosts that are immune to Spectre-v2, but that don't have this field set, most likely because they predate the specification.
However, this prevents the migration of guests that have started on a host the doesn't fake this CSV2 setting to one that does, as KVM rejects the write to
ID_AA64PFR0_EL2on the grounds that it isn't what is already there.In order to fix this, allow userspace to set this field as long as this doesn't result in a promising more than what is already there (setting CSV2 to 0 is acceptable, but setting it to 1 when it is already set to 0 isn't).
Quoted 8 lines
Fixes e1026237f9067 ("KVM: arm64: Set CSV2 for guests on hardware unaffected by Spectre-v2")Reported-by Peng Liang <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/kvm/arm.c | 16 ++++++++++++ arch/arm64/kvm/sys_regs.c | 42 ++++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-)
Will
Marc Zyngier
maintainerPATCH v2 2/3 KVM: arm64: Unify trap handlers injecting an UNDEF
In reply to Marc ZyngierA large number of system register trap handlers only inject an UNDEF exeption, and yet each class of sysreg seems to provide its own, identical function.
Let's unify them all, saving us introducing yet another one later.
--- arch/arm64/kvm/sys_regs.c | 65 +++++++++++++++------------------------ 1 file changed, 25 insertions(+), 40 deletions(-)
arch/arm64/kvm/sys_regs.c +25 -40
@@ -1038,8 +1038,8 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, {SYS_DESC (SYS_PMEVTYPERn_EL0 (n)), \ access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), } -static bool access_amu(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static bool undef_access(struct kvm_vcpu *vcpu, struct sys_reg_params *p, + const struct sys_reg_desc *r) {kvm_inject_undefined (vcpu);
@@ -1047,24 +1047,10 @@ static bool access_amu(struct kvm_vcpu *vcpu, struct sys_reg_params *p, }/* Macro to expand the AMU counter and type registers*/ -#define AMU_AMEVCNTR0_EL0 (n) { SYS_DESC(SYS_AMEVCNTR0_EL0(n)), access_amu } -#define AMU_AMEVTYPER0_EL0 (n) { SYS_DESC(SYS_AMEVTYPER0_EL0(n)), access_amu } -#define AMU_AMEVCNTR1_EL0 (n) { SYS_DESC(SYS_AMEVCNTR1_EL0(n)), access_amu } -#define AMU_AMEVTYPER1_EL0 (n) { SYS_DESC(SYS_AMEVTYPER1_EL0(n)), access_amu } - -static bool trap_ptrauth (struct kvm_vcpu *vcpu, -struct sys_reg_params *p, -const struct sys_reg_desc *rd) -{ -/* -* If we land here, that is because we didn't fixup the access on exit -* by allowing the PtrAuth sysregs. The only way this happens is when -* the guest does not have PtrAuth support enabled. -*/ -kvm_inject_undefined (vcpu); - -return false; -} +#define AMU_AMEVCNTR0_EL0 (n) { SYS_DESC(SYS_AMEVCNTR0_EL0(n)), undef_access } +#define AMU_AMEVTYPER0_EL0 (n) { SYS_DESC(SYS_AMEVTYPER0_EL0(n)), undef_access } +#define AMU_AMEVCNTR1_EL0 (n) { SYS_DESC(SYS_AMEVCNTR1_EL0(n)), undef_access } +#define AMU_AMEVTYPER1_EL0 (n) { SYS_DESC(SYS_AMEVTYPER1_EL0(n)), undef_access }static unsigned int ptrauth_visibility (const struct kvm_vcpu *vcpu,const struct sys_reg_desc *rd)
@@ -1072,8 +1058,14 @@ static unsigned int ptrauth_visibility(const struct kvm_vcpu *vcpu,return vcpu_has_ptrauth (vcpu) ?0 :REG_HIDDEN_USER |REG_HIDDEN_GUEST ; } +/* +* If we land here on a PtrAuth access, that is because we didn't +* fixup the access on exit by allowing the PtrAuth sysregs. The only +* way this happens is when the guest does not have PtrAuth support +* enabled. +*/ #define __PTRAUTH_KEY (k) \ - { SYS_DESC(SYS_## k), trap_ptrauth, reset_unknown, k, \ + { SYS_DESC(SYS_## k), undef_access, reset_unknown, k, \ .visibility = ptrauth_visibility}#define PTRAUTH_KEY (k) \
@@ -1417,13 +1409,6 @@ static bool access_ccsidr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,return true; } -static bool access_mte_regs (struct kvm_vcpu *vcpu,struct sys_reg_params *p, -const struct sys_reg_desc *r) -{ -kvm_inject_undefined (vcpu); -return false; -} -/* sys_reg_desc initialiser for known cpufeature ID registers */ #define ID_SANITISED (name) { \ SYS_DESC(SYS_##name), \
@@ -1591,8 +1576,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_ACTLR_EL1), access_actlr, reset_actlr, ACTLR_EL1 }, { SYS_DESC(SYS_CPACR_EL1), NULL, reset_val, CPACR_EL1, 0 }, - { SYS_DESC(SYS_RGSR_EL1), access_mte_regs }, - { SYS_DESC(SYS_GCR_EL1), access_mte_regs }, + { SYS_DESC(SYS_RGSR_EL1), undef_access }, + { SYS_DESC(SYS_GCR_EL1), undef_access }, { SYS_DESC(SYS_ZCR_EL1), NULL, reset_val, ZCR_EL1, 0, .visibility = sve_visibility }, { SYS_DESC(SYS_TTBR0_EL1), access_vm_reg, reset_unknown, TTBR0_EL1 },
@@ -1618,8 +1603,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_ERXMISC0_EL1), trap_raz_wi }, { SYS_DESC(SYS_ERXMISC1_EL1), trap_raz_wi }, - { SYS_DESC(SYS_TFSR_EL1), access_mte_regs }, - { SYS_DESC(SYS_TFSRE0_EL1), access_mte_regs }, + { SYS_DESC(SYS_TFSR_EL1), undef_access }, + { SYS_DESC(SYS_TFSRE0_EL1), undef_access }, { SYS_DESC(SYS_FAR_EL1), access_vm_reg, reset_unknown, FAR_EL1 }, { SYS_DESC(SYS_PAR_EL1), NULL, reset_unknown, PAR_EL1 },
@@ -1683,14 +1668,14 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 }, { SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 }, - { SYS_DESC(SYS_AMCR_EL0), access_amu }, - { SYS_DESC(SYS_AMCFGR_EL0), access_amu }, - { SYS_DESC(SYS_AMCGCR_EL0), access_amu }, - { SYS_DESC(SYS_AMUSERENR_EL0), access_amu }, - { SYS_DESC(SYS_AMCNTENCLR0_EL0), access_amu }, - { SYS_DESC(SYS_AMCNTENSET0_EL0), access_amu }, - { SYS_DESC(SYS_AMCNTENCLR1_EL0), access_amu }, - { SYS_DESC(SYS_AMCNTENSET1_EL0), access_amu }, + { SYS_DESC(SYS_AMCR_EL0), undef_access }, + { SYS_DESC(SYS_AMCFGR_EL0), undef_access }, + { SYS_DESC(SYS_AMCGCR_EL0), undef_access }, + { SYS_DESC(SYS_AMUSERENR_EL0), undef_access }, + { SYS_DESC(SYS_AMCNTENCLR0_EL0), undef_access }, + { SYS_DESC(SYS_AMCNTENSET0_EL0), undef_access }, + { SYS_DESC(SYS_AMCNTENCLR1_EL0), undef_access }, + { SYS_DESC(SYS_AMCNTENSET1_EL0), undef_access },AMU_AMEVCNTR0_EL0 (0 ),AMU_AMEVCNTR0_EL0 (1 ), AMU_AMEVCNTR0_EL0(2),
Signature · 1 line
2.28.0
Re: [PATCH v2 2/3] KVM: arm64: Unify trap handlers injecting an UNDEF
Quoted 10 lines · Marc Zyngier
A large number of system register trap handlers only inject an UNDEF exeption, and yet each class of sysreg seems to provide its own, identical function.
Let's unify them all, saving us introducing yet another one later.
Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kvm/sys_regs.c | 65 +++++++++++++++------------------------ 1 file changed, 25 insertions(+), 40 deletions(-)
Will
Marc Zyngier
maintainerPATCH v2 3/3 KVM: arm64: Handle SCXTNUM_ELx traps
In reply to Marc ZyngierAs the kernel never sets HCR_EL2.EnSCXT, accesses to SCXTNUM_ELx will trap to EL2. Let's handle that as gracefully as possible by injecting an UNDEF exception into the guest. This is consistent with the guest's view of ID_AA64PFR0_EL1.CSV2 being at most 1.
--- arch/arm64/include/asm/sysreg.h | 4 ++++ arch/arm64/kvm/sys_regs.c | 4 ++++ 2 files changed, 8 insertions(+)
arch/arm64/include/asm/sysreg.h +4 -0
@@ -372,6 +372,8 @@#define SYS_CONTEXTIDR_EL1 sys_reg(3, 0, 13, 0, 1)#define SYS_TPIDR_EL1 sys_reg(3, 0, 13, 0, 4) +#define SYS_SCXTNUM_EL1 sys_reg(3, 0, 13, 0, 7) +#define SYS_CNTKCTL_EL1 sys_reg(3, 0, 14, 1, 0)#define SYS_CCSIDR_EL1 sys_reg(3, 1, 0, 0, 0)
@@ -404,6 +406,8 @@#define SYS_TPIDR_EL0 sys_reg(3, 3, 13, 0, 2)#define SYS_TPIDRRO_EL0 sys_reg(3, 3, 13, 0, 3) +#define SYS_SCXTNUM_EL0 sys_reg(3, 3, 13, 0, 7) +/* Definitions for system register interface to AMU for ARMv8.4 onwards */ #define SYS_AM_EL0 (crm, op2) sys_reg(3, 3, 13, (crm), (op2))#define SYS_AMCR_EL0 SYS_AM_EL0(2, 0)
arch/arm64/kvm/sys_regs.c +4 -0
@@ -1640,6 +1640,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CONTEXTIDR_EL1), access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 }, { SYS_DESC(SYS_TPIDR_EL1), NULL, reset_unknown, TPIDR_EL1 }, + { SYS_DESC(SYS_SCXTNUM_EL1), undef_access }, + { SYS_DESC(SYS_CNTKCTL_EL1), NULL, reset_val, CNTKCTL_EL1, 0}, { SYS_DESC(SYS_CCSIDR_EL1), access_ccsidr },
@@ -1668,6 +1670,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 }, { SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 }, + { SYS_DESC(SYS_SCXTNUM_EL0), undef_access }, + { SYS_DESC(SYS_AMCR_EL0), undef_access }, { SYS_DESC(SYS_AMCFGR_EL0), undef_access }, { SYS_DESC(SYS_AMCGCR_EL0), undef_access },
Signature · 1 line
2.28.0