Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64/sve: KVM: Prevent guests from using SVE

Until KVM has full SVE support, guests must not be allowed to
execute SVE instructions.

This patch enables the necessary traps, and also ensures that the
traps are disabled again on exit from the guest so that the host
can still use SVE if it wants to.

On guest exit, high bits of the SVE Zn registers may have been
clobbered as a side-effect the execution of FPSIMD instructions in
the guest. The existing KVM host FPSIMD restore code is not
sufficient to restore these bits, so this patch explicitly marks
the CPU as not containing cached vector state for any task, thus
forcing a reload on the next return to userspace. This is an
interim measure, in advance of adding full SVE awareness to KVM.

This marking of cached vector state in the CPU as invalid is done
using __this_cpu_write(fpsimd_last_state, NULL) in fpsimd.c. Due
to the repeated use of this rather obscure operation, it makes
sense to factor it out as a separate helper with a clearer name.
This patch factors it out as fpsimd_flush_cpu_state(), and ports
all callers to use it.

As a side effect of this refactoring, a this_cpu_write() in
fpsimd_cpu_pm_notifier() is changed to __this_cpu_write(). This
should be fine, since cpu_pm_enter() is supposed to be called only
with interrupts disabled.

Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>

authored by

Dave Martin and committed by
Will Deacon
17eed27b 4ffa09a9

+53 -6
+3
arch/arm/include/asm/kvm_host.h
··· 293 293 int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu, 294 294 struct kvm_device_attr *attr); 295 295 296 + /* All host FP/SIMD state is restored on guest exit, so nothing to save: */ 297 + static inline void kvm_fpsimd_flush_cpu_state(void) {} 298 + 296 299 #endif /* __ARM_KVM_HOST_H__ */
+1
arch/arm64/include/asm/fpsimd.h
··· 74 74 extern void fpsimd_update_current_state(struct fpsimd_state *state); 75 75 76 76 extern void fpsimd_flush_task_state(struct task_struct *target); 77 + extern void sve_flush_cpu_state(void); 77 78 78 79 /* Maximum VL that SVE VL-agnostic software can transparently support */ 79 80 #define SVE_VL_ARCH_MAX 0x100
+3 -1
arch/arm64/include/asm/kvm_arm.h
··· 186 186 #define CPTR_EL2_TTA (1 << 20) 187 187 #define CPTR_EL2_TFP (1 << CPTR_EL2_TFP_SHIFT) 188 188 #define CPTR_EL2_TZ (1 << 8) 189 - #define CPTR_EL2_DEFAULT 0x000033ff 189 + #define CPTR_EL2_RES1 0x000032ff /* known RES1 bits in CPTR_EL2 */ 190 + #define CPTR_EL2_DEFAULT CPTR_EL2_RES1 190 191 191 192 /* Hyp Debug Configuration Register bits */ 192 193 #define MDCR_EL2_TPMS (1 << 14) ··· 238 237 239 238 #define CPACR_EL1_FPEN (3 << 20) 240 239 #define CPACR_EL1_TTA (1 << 28) 240 + #define CPACR_EL1_DEFAULT (CPACR_EL1_FPEN | CPACR_EL1_ZEN_EL1EN) 241 241 242 242 #endif /* __ARM64_KVM_ARM_H__ */
+11
arch/arm64/include/asm/kvm_host.h
··· 25 25 #include <linux/types.h> 26 26 #include <linux/kvm_types.h> 27 27 #include <asm/cpufeature.h> 28 + #include <asm/fpsimd.h> 28 29 #include <asm/kvm.h> 29 30 #include <asm/kvm_asm.h> 30 31 #include <asm/kvm_mmio.h> ··· 383 382 384 383 WARN_ONCE(parange < 40, 385 384 "PARange is %d bits, unsupported configuration!", parange); 385 + } 386 + 387 + /* 388 + * All host FP/SIMD state is restored on guest exit, so nothing needs 389 + * doing here except in the SVE case: 390 + */ 391 + static inline void kvm_fpsimd_flush_cpu_state(void) 392 + { 393 + if (system_supports_sve()) 394 + sve_flush_cpu_state(); 386 395 } 387 396 388 397 #endif /* __ARM64_KVM_HOST_H__ */
+29 -2
arch/arm64/kernel/fpsimd.c
··· 1050 1050 t->thread.fpsimd_state.cpu = NR_CPUS; 1051 1051 } 1052 1052 1053 + static inline void fpsimd_flush_cpu_state(void) 1054 + { 1055 + __this_cpu_write(fpsimd_last_state, NULL); 1056 + } 1057 + 1058 + /* 1059 + * Invalidate any task SVE state currently held in this CPU's regs. 1060 + * 1061 + * This is used to prevent the kernel from trying to reuse SVE register data 1062 + * that is detroyed by KVM guest enter/exit. This function should go away when 1063 + * KVM SVE support is implemented. Don't use it for anything else. 1064 + */ 1065 + #ifdef CONFIG_ARM64_SVE 1066 + void sve_flush_cpu_state(void) 1067 + { 1068 + struct fpsimd_state *const fpstate = __this_cpu_read(fpsimd_last_state); 1069 + struct task_struct *tsk; 1070 + 1071 + if (!fpstate) 1072 + return; 1073 + 1074 + tsk = container_of(fpstate, struct task_struct, thread.fpsimd_state); 1075 + if (test_tsk_thread_flag(tsk, TIF_SVE)) 1076 + fpsimd_flush_cpu_state(); 1077 + } 1078 + #endif /* CONFIG_ARM64_SVE */ 1079 + 1053 1080 #ifdef CONFIG_KERNEL_MODE_NEON 1054 1081 1055 1082 DEFINE_PER_CPU(bool, kernel_neon_busy); ··· 1117 1090 } 1118 1091 1119 1092 /* Invalidate any task state remaining in the fpsimd regs: */ 1120 - __this_cpu_write(fpsimd_last_state, NULL); 1093 + fpsimd_flush_cpu_state(); 1121 1094 1122 1095 preempt_disable(); 1123 1096 ··· 1238 1211 case CPU_PM_ENTER: 1239 1212 if (current->mm) 1240 1213 task_fpsimd_save(); 1241 - this_cpu_write(fpsimd_last_state, NULL); 1214 + fpsimd_flush_cpu_state(); 1242 1215 break; 1243 1216 case CPU_PM_EXIT: 1244 1217 if (current->mm)
+3 -3
arch/arm64/kvm/hyp/switch.c
··· 48 48 49 49 val = read_sysreg(cpacr_el1); 50 50 val |= CPACR_EL1_TTA; 51 - val &= ~CPACR_EL1_FPEN; 51 + val &= ~(CPACR_EL1_FPEN | CPACR_EL1_ZEN); 52 52 write_sysreg(val, cpacr_el1); 53 53 54 54 write_sysreg(__kvm_hyp_vector, vbar_el1); ··· 59 59 u64 val; 60 60 61 61 val = CPTR_EL2_DEFAULT; 62 - val |= CPTR_EL2_TTA | CPTR_EL2_TFP; 62 + val |= CPTR_EL2_TTA | CPTR_EL2_TFP | CPTR_EL2_TZ; 63 63 write_sysreg(val, cptr_el2); 64 64 } 65 65 ··· 117 117 118 118 write_sysreg(mdcr_el2, mdcr_el2); 119 119 write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2); 120 - write_sysreg(CPACR_EL1_FPEN, cpacr_el1); 120 + write_sysreg(CPACR_EL1_DEFAULT, cpacr_el1); 121 121 write_sysreg(vectors, vbar_el1); 122 122 } 123 123
+3
virt/kvm/arm/arm.c
··· 652 652 */ 653 653 preempt_disable(); 654 654 655 + /* Flush FP/SIMD state that can't survive guest entry/exit */ 656 + kvm_fpsimd_flush_cpu_state(); 657 + 655 658 kvm_pmu_flush_hwstate(vcpu); 656 659 657 660 kvm_timer_flush_hwstate(vcpu);