mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 18:14:07 +08:00
x86/KVM/VMX: Add L1D flush algorithm
To mitigate the L1 Terminal Fault vulnerability it's required to flush L1D on VMENTER to prevent rogue guests from snooping host memory. CPUs will have a new control MSR via a microcode update to flush L1D with a single MSR write, but in the absence of microcode a fallback to a software based flush algorithm is required. Add a software flush loop which is based on code from Intel. [ tglx: Split out from combo patch ] [ bpetkov: Polish the asm code ] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
a399477e52
commit
a47dd5f067
@ -9563,6 +9563,46 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Software based L1D cache flush which is used when microcode providing
|
||||
* the cache control MSR is not loaded.
|
||||
*
|
||||
* The L1D cache is 32 KiB on Nehalem and later microarchitectures, but to
|
||||
* flush it is required to read in 64 KiB because the replacement algorithm
|
||||
* is not exactly LRU. This could be sized at runtime via topology
|
||||
* information but as all relevant affected CPUs have 32KiB L1D cache size
|
||||
* there is no point in doing so.
|
||||
*/
|
||||
#define L1D_CACHE_ORDER 4
|
||||
static void *vmx_l1d_flush_pages;
|
||||
|
||||
static void __maybe_unused vmx_l1d_flush(void)
|
||||
{
|
||||
int size = PAGE_SIZE << L1D_CACHE_ORDER;
|
||||
|
||||
asm volatile(
|
||||
/* First ensure the pages are in the TLB */
|
||||
"xorl %%eax, %%eax\n"
|
||||
".Lpopulate_tlb:\n\t"
|
||||
"movzbl (%[empty_zp], %%" _ASM_AX "), %%ecx\n\t"
|
||||
"addl $4096, %%eax\n\t"
|
||||
"cmpl %%eax, %[size]\n\t"
|
||||
"jne .Lpopulate_tlb\n\t"
|
||||
"xorl %%eax, %%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
/* Now fill the cache */
|
||||
"xorl %%eax, %%eax\n"
|
||||
".Lfill_cache:\n"
|
||||
"movzbl (%[empty_zp], %%" _ASM_AX "), %%ecx\n\t"
|
||||
"addl $64, %%eax\n\t"
|
||||
"cmpl %%eax, %[size]\n\t"
|
||||
"jne .Lfill_cache\n\t"
|
||||
"lfence\n"
|
||||
:: [empty_zp] "r" (vmx_l1d_flush_pages),
|
||||
[size] "r" (size)
|
||||
: "eax", "ebx", "ecx", "edx");
|
||||
}
|
||||
|
||||
static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
|
||||
{
|
||||
struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
|
||||
@ -13110,13 +13150,29 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
|
||||
.enable_smi_window = enable_smi_window,
|
||||
};
|
||||
|
||||
static void __init vmx_setup_l1d_flush(void)
|
||||
static int __init vmx_setup_l1d_flush(void)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (vmentry_l1d_flush == VMENTER_L1D_FLUSH_NEVER ||
|
||||
!boot_cpu_has_bug(X86_BUG_L1TF))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
page = alloc_pages(GFP_KERNEL, L1D_CACHE_ORDER);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
vmx_l1d_flush_pages = page_address(page);
|
||||
static_branch_enable(&vmx_l1d_should_flush);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmx_free_l1d_flush_pages(void)
|
||||
{
|
||||
if (vmx_l1d_flush_pages) {
|
||||
free_pages((unsigned long)vmx_l1d_flush_pages, L1D_CACHE_ORDER);
|
||||
vmx_l1d_flush_pages = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init vmx_init(void)
|
||||
@ -13152,13 +13208,17 @@ static int __init vmx_init(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
vmx_setup_l1d_flush();
|
||||
|
||||
r = kvm_init(&vmx_x86_ops, sizeof(struct vcpu_vmx),
|
||||
__alignof__(struct vcpu_vmx), THIS_MODULE);
|
||||
r = vmx_setup_l1d_flush();
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = kvm_init(&vmx_x86_ops, sizeof(struct vcpu_vmx),
|
||||
__alignof__(struct vcpu_vmx), THIS_MODULE);
|
||||
if (r) {
|
||||
vmx_free_l1d_flush_pages();
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
rcu_assign_pointer(crash_vmclear_loaded_vmcss,
|
||||
crash_vmclear_local_loaded_vmcss);
|
||||
@ -13199,6 +13259,7 @@ static void __exit vmx_exit(void)
|
||||
static_branch_disable(&enable_evmcs);
|
||||
}
|
||||
#endif
|
||||
vmx_free_l1d_flush_pages();
|
||||
}
|
||||
|
||||
module_init(vmx_init)
|
||||
|
Loading…
Reference in New Issue
Block a user