KVM/arm64 fixes for 5.10, take #3
Marc Zyngier
maintainercover KVM/arm64 fixes for 5.10, take #3
Paolo,
Here's another small set of fixes for 5.10, this time fixing some corner cases of the Spectre mitigation rework. Note that I had to pull 5.10-rc1 into kvmarm/next in order to avoid some annoying conflicts.
Please pull,
M.
The following changes since commit 4f6b838c378a52ea3ae0b15f12ca8a20849072fa:
Merge tag 'v5.10-rc1' into kvmarm-master/next (2020-11-12 21:20:43 +0000)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git tags/kvmarm-fixes-5.10-3
for you to fetch changes up to ed4ffaf49bf9ce1002b516d8c6aa04937b7950bc:
KVM: arm64: Handle SCXTNUM_ELx traps (2020-11-12 21:22:46 +0000)
----------------------------------------------------------------
KVM/arm64 fixes for v5.10, take #3
- Allow userspace to downgrade ID_AA64PFR0_EL1.CSV2
- Inject UNDEF on SCXTNUM_ELx access
----------------------------------------------------------------
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(-)
Marc Zyngier
maintainerPATCH 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
Paolo Bonzini
maintainerQuoted 131 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).
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]>Acked-by Will Deacon <[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(-)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 +36
-3@@ -1128,9 +1128,8 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,if (!vcpu_has_sve (vcpu)) val &= ~(0xfUL <<ID_AA64PFR0_SVE_SHIFT ); val &= ~(0xfUL <<ID_AA64PFR0_AMU_SHIFT ); -if (!(val & (0xfUL <<ID_AA64PFR0_CSV2_SHIFT )) && -arm64_get_spectre_v2_state () ==SPECTRE_UNAFFECTED ) - val |= (1UL <<ID_AA64PFR0_CSV2_SHIFT ); + val &= ~(0xfUL <<ID_AA64PFR0_CSV2_SHIFT ); + val |= ((u64 )vcpu->kvm->arch.pfr0_csv2 <<ID_AA64PFR0_CSV2_SHIFT ); }else if (id ==SYS_ID_AA64PFR1_EL1 ) { val &= ~(0xfUL <<ID_AA64PFR1_MTE_SHIFT ); }else if (id ==SYS_ID_AA64ISAR1_EL1 && !vcpu_has_ptrauth (vcpu)) {@@ -1213,6 +1212,40 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,return REG_HIDDEN ; } +static int set_id_aa64pfr0_el1 (struct kvm_vcpu *vcpu, +const struct sys_reg_desc *rd, +const struct kvm_one_reg *reg,void __user *uaddr) +{ +const u64 id =sys_reg_to_index (rd); +int err; +u64 val; +u8 csv2; + + err =reg_from_user (&val, uaddr, id); +if (err) +return err; + +/* +* Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as +* it doesn't promise more than what is actually provided (the +* guest could otherwise be covered in ectoplasmic residue). +*/ + csv2 =cpuid_feature_extract_unsigned_field (val,ID_AA64PFR0_CSV2_SHIFT ); +if (csv2 >1 || + (csv2 &&arm64_get_spectre_v2_state () !=SPECTRE_UNAFFECTED )) +return -EINVAL ; + +/* We can only differ with CSV2, and anything else is an error */ + val ^=read_id_reg (vcpu, rd, false); + val &= ~(0xFUL <<ID_AA64PFR0_CSV2_SHIFT ); +if (val) +return -EINVAL ; + + vcpu->kvm->arch.pfr0_csv2 = csv2; + +return 0 ; +} +/* * cpufeature ID register user accessors *
arch/arm64/kvm/sys_regs.c static const struct sys_reg_desc sys_reg_descs[] = { · line 1512
Quoted 9 lines
arch/arm64/kvm/sys_regs.c +2
-1@@ -1472,7 +1505,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {/* AArch64 ID registers */ /* CRm=4 */ -ID_SANITISED (ID_AA64PFR0_EL1 ), + { SYS_DESC(SYS_ID_AA64PFR0_EL1), .access = access_id_reg, + .get_user = get_id_reg, .set_user = set_id_aa64pfr0_el1, },ID_SANITISED (ID_AA64PFR1_EL1 ),ID_UNALLOCATED (4 ,2 ), ID_UNALLOCATED(4,3),
Pulled, thanks.
Paolo
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Marc Zyngier
maintainerPATCH 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 ; } +/* +* 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) \
@@ -1374,13 +1366,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), \
@@ -1549,8 +1534,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 },
@@ -1576,8 +1561,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 },
@@ -1641,14 +1626,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
Marc Zyngier
maintainerPATCH 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
@@ -1598,6 +1598,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 },
@@ -1626,6 +1628,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