diff --git a/doc/developer-guides/hld/hv-memmgt.rst b/doc/developer-guides/hld/hv-memmgt.rst index f9db4f9a5..1a4fd98fa 100644 --- a/doc/developer-guides/hld/hv-memmgt.rst +++ b/doc/developer-guides/hld/hv-memmgt.rst @@ -418,9 +418,6 @@ Here is a list of major memory related APIs in HV: EPT/VPID Capability Checking ---------------------------- -.. doxygenfunction:: check_vmx_mmu_cap - :project: Project ACRN - Data Transferring between hypervisor and VM ------------------------------------------- diff --git a/hypervisor/Makefile b/hypervisor/Makefile index e805c65d0..568cbca0d 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -136,6 +136,7 @@ S_SRCS += arch/x86/idt.S C_SRCS += arch/x86/ioapic.c C_SRCS += arch/x86/lapic.c C_SRCS += arch/x86/cpu.c +C_SRCS += arch/x86/cpu_caps.c C_SRCS += arch/x86/mmu.c C_SRCS += arch/x86/e820.c C_SRCS += arch/x86/pagetable.c diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index 9b651e6c3..fc78822ce 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -10,6 +10,7 @@ #include #include #include +#include struct per_cpu_region per_cpu_data[CONFIG_MAX_PCPU_NUM] __aligned(PAGE_SIZE); uint16_t phys_cpu_num = 0U; @@ -20,265 +21,12 @@ static uint64_t startup_paddr = 0UL; /* physical cpu active bitmap, support up to 64 cpus */ uint64_t pcpu_active_bitmap = 0UL; -static bool skip_l1dfl_vmentry; -static uint64_t x86_arch_capabilities; - -/* TODO: add more capability per requirement */ -/* APICv features */ -#define VAPIC_FEATURE_VIRT_ACCESS (1U << 0U) -#define VAPIC_FEATURE_VIRT_REG (1U << 1U) -#define VAPIC_FEATURE_INTR_DELIVERY (1U << 2U) -#define VAPIC_FEATURE_TPR_SHADOW (1U << 3U) -#define VAPIC_FEATURE_POST_INTR (1U << 4U) -#define VAPIC_FEATURE_VX2APIC_MODE (1U << 5U) - -struct cpu_capability { - uint8_t apicv_features; - uint8_t ept_features; -}; -static struct cpu_capability cpu_caps; - -struct cpuinfo_x86 boot_cpu_data; - -static void cpu_cap_detect(void); static void cpu_xsave_init(void); static void set_current_cpu_id(uint16_t pcpu_id); static void print_hv_banner(void); static uint16_t get_cpu_id_from_lapic_id(uint32_t lapic_id); -int32_t ibrs_type; static uint64_t start_tsc __attribute__((__section__(".bss_noinit"))); -bool cpu_has_cap(uint32_t bit) -{ - uint32_t feat_idx = bit >> 5U; - uint32_t feat_bit = bit & 0x1fU; - bool ret; - - if (feat_idx >= FEATURE_WORDS) { - ret = false; - } else { - ret = ((boot_cpu_data.cpuid_leaves[feat_idx] & (1U << feat_bit)) != 0U); - } - - return ret; -} - -static inline bool get_monitor_cap(void) -{ - if (cpu_has_cap(X86_FEATURE_MONITOR)) { - /* don't use monitor for CPU (family: 0x6 model: 0x5c) - * in hypervisor, but still expose it to the guests and - * let them handle it correctly - */ - if ((boot_cpu_data.family != 0x6U) || (boot_cpu_data.model != 0x5cU)) { - return true; - } - } - - return false; -} - -static inline bool is_fast_string_erms_supported_and_enabled(void) -{ - bool ret = false; - uint32_t misc_enable = (uint32_t)msr_read(MSR_IA32_MISC_ENABLE); - - if ((misc_enable & MSR_IA32_MISC_ENABLE_FAST_STRING) == 0U) { - pr_fatal("%s, fast string is not enabled\n", __func__); - } else { - if (!cpu_has_cap(X86_FEATURE_ERMS)) { - pr_fatal("%s, enhanced rep movsb/stosb not supported\n", __func__); - } else { - ret = true; - } - } - - return ret; -} - -static uint64_t get_address_mask(uint8_t limit) -{ - return ((1UL << limit) - 1UL) & PAGE_MASK; -} - -static void get_cpu_capabilities(void) -{ - uint32_t eax, unused; - uint32_t family, model; - - cpuid(CPUID_VENDORSTRING, - &boot_cpu_data.cpuid_level, - &unused, &unused, &unused); - - cpuid(CPUID_FEATURES, &eax, &unused, - &boot_cpu_data.cpuid_leaves[FEAT_1_ECX], - &boot_cpu_data.cpuid_leaves[FEAT_1_EDX]); - family = (eax >> 8U) & 0xffU; - if (family == 0xFU) { - family += (eax >> 20U) & 0xffU; - } - boot_cpu_data.family = (uint8_t)family; - - model = (eax >> 4U) & 0xfU; - if (family >= 0x06U) { - model += ((eax >> 16U) & 0xfU) << 4U; - } - boot_cpu_data.model = (uint8_t)model; - - - cpuid(CPUID_EXTEND_FEATURE, &unused, - &boot_cpu_data.cpuid_leaves[FEAT_7_0_EBX], - &boot_cpu_data.cpuid_leaves[FEAT_7_0_ECX], - &boot_cpu_data.cpuid_leaves[FEAT_7_0_EDX]); - - cpuid(CPUID_MAX_EXTENDED_FUNCTION, - &boot_cpu_data.extended_cpuid_level, - &unused, &unused, &unused); - - if (boot_cpu_data.extended_cpuid_level >= CPUID_EXTEND_FUNCTION_1) { - cpuid(CPUID_EXTEND_FUNCTION_1, &unused, &unused, - &boot_cpu_data.cpuid_leaves[FEAT_8000_0001_ECX], - &boot_cpu_data.cpuid_leaves[FEAT_8000_0001_EDX]); - } - - if (boot_cpu_data.extended_cpuid_level >= CPUID_EXTEND_ADDRESS_SIZE) { - cpuid(CPUID_EXTEND_ADDRESS_SIZE, &eax, - &boot_cpu_data.cpuid_leaves[FEAT_8000_0008_EBX], - &unused, &unused); - - /* EAX bits 07-00: #Physical Address Bits - * bits 15-08: #Linear Address Bits - */ - boot_cpu_data.virt_bits = (uint8_t)((eax >> 8U) & 0xffU); - boot_cpu_data.phys_bits = (uint8_t)(eax & 0xffU); - boot_cpu_data.physical_address_mask = - get_address_mask(boot_cpu_data.phys_bits); - } - - /* For speculation defence. - * The default way is to set IBRS at vmexit and then do IBPB at vcpu - * context switch(ibrs_type == IBRS_RAW). - * Now provide an optimized way (ibrs_type == IBRS_OPT) which set - * STIBP and do IBPB at vmexit,since having STIBP always set has less - * impact than having IBRS always set. Also since IBPB is already done - * at vmexit, it is no necessary to do so at vcpu context switch then. - */ - ibrs_type = IBRS_NONE; - - /* Currently for APL, if we enabled retpoline, then IBRS should not - * take effect - * TODO: add IA32_ARCH_CAPABILITIES[1] check, if this bit is set, IBRS - * should be set all the time instead of relying on retpoline - */ -#ifndef CONFIG_RETPOLINE - if (cpu_has_cap(X86_FEATURE_IBRS_IBPB)) { - ibrs_type = IBRS_RAW; - if (cpu_has_cap(X86_FEATURE_STIBP)) { - ibrs_type = IBRS_OPT; - } - } -#endif -} - -/* - * basic hardware capability check - * we should supplement which feature/capability we must support - * here later. - */ -static int32_t hardware_detect_support(void) -{ - int32_t ret; - - /* Long Mode (x86-64, 64-bit support) */ - if (!cpu_has_cap(X86_FEATURE_LM)) { - pr_fatal("%s, LM not supported\n", __func__); - return -ENODEV; - } - if ((boot_cpu_data.phys_bits == 0U) || - (boot_cpu_data.virt_bits == 0U)) { - pr_fatal("%s, can't detect Linear/Physical Address size\n", - __func__); - return -ENODEV; - } - - /* lapic TSC deadline timer */ - if (!cpu_has_cap(X86_FEATURE_TSC_DEADLINE)) { - pr_fatal("%s, TSC deadline not supported\n", __func__); - return -ENODEV; - } - - /* Execute Disable */ - if (!cpu_has_cap(X86_FEATURE_NX)) { - pr_fatal("%s, NX not supported\n", __func__); - return -ENODEV; - } - - /* Supervisor-Mode Execution Prevention */ - if (!cpu_has_cap(X86_FEATURE_SMEP)) { - pr_fatal("%s, SMEP not supported\n", __func__); - return -ENODEV; - } - - /* Supervisor-Mode Access Prevention */ - if (!cpu_has_cap(X86_FEATURE_SMAP)) { - pr_fatal("%s, SMAP not supported\n", __func__); - return -ENODEV; - } - - if (!cpu_has_cap(X86_FEATURE_MTRR)) { - pr_fatal("%s, MTRR not supported\n", __func__); - return -ENODEV; - } - - if (!cpu_has_cap(X86_FEATURE_PAGE1GB)) { - pr_fatal("%s, not support 1GB page\n", __func__); - return -ENODEV; - } - - if (!cpu_has_cap(X86_FEATURE_VMX)) { - pr_fatal("%s, vmx not supported\n", __func__); - return -ENODEV; - } - - if (!is_fast_string_erms_supported_and_enabled()) { - return -ENODEV; - } - - - if (!cpu_has_vmx_unrestricted_guest_cap()) { - pr_fatal("%s, unrestricted guest not supported\n", __func__); - return -ENODEV; - } - - if (!is_ept_supported()) { - pr_fatal("%s, EPT not supported\n", __func__); - return -ENODEV; - } - - if (boot_cpu_data.cpuid_level < 0x15U) { - pr_fatal("%s, required CPU feature not supported\n", __func__); - return -ENODEV; - } - - if (is_vmx_disabled()) { - pr_fatal("%s, VMX can not be enabled\n", __func__); - return -ENODEV; - } - - if (phys_cpu_num > CONFIG_MAX_PCPU_NUM) { - pr_fatal("%s, pcpu number(%d) is out of range\n", __func__, phys_cpu_num); - return -ENODEV; - } - - ret = check_vmx_mmu_cap(); - if (ret != 0) { - return ret; - } - - pr_acrnlog("hardware support HV"); - return 0; -} - static void init_percpu_lapic_id(void) { uint16_t i; @@ -342,49 +90,6 @@ static void set_fs_base(void) } #endif -static void get_cpu_name(void) -{ - cpuid(CPUID_EXTEND_FUNCTION_2, - (uint32_t *)(boot_cpu_data.model_name), - (uint32_t *)(&boot_cpu_data.model_name[4]), - (uint32_t *)(&boot_cpu_data.model_name[8]), - (uint32_t *)(&boot_cpu_data.model_name[12])); - cpuid(CPUID_EXTEND_FUNCTION_3, - (uint32_t *)(&boot_cpu_data.model_name[16]), - (uint32_t *)(&boot_cpu_data.model_name[20]), - (uint32_t *)(&boot_cpu_data.model_name[24]), - (uint32_t *)(&boot_cpu_data.model_name[28])); - cpuid(CPUID_EXTEND_FUNCTION_4, - (uint32_t *)(&boot_cpu_data.model_name[32]), - (uint32_t *)(&boot_cpu_data.model_name[36]), - (uint32_t *)(&boot_cpu_data.model_name[40]), - (uint32_t *)(&boot_cpu_data.model_name[44])); - - boot_cpu_data.model_name[48] = '\0'; -} - -static bool check_cpu_security_config(void) -{ - if (cpu_has_cap(X86_FEATURE_ARCH_CAP)) { - x86_arch_capabilities = msr_read(MSR_IA32_ARCH_CAPABILITIES); - skip_l1dfl_vmentry = ((x86_arch_capabilities - & IA32_ARCH_CAP_SKIP_L1DFL_VMENTRY) != 0UL); - } else { - return false; - } - - if ((!cpu_has_cap(X86_FEATURE_L1D_FLUSH)) && (!skip_l1dfl_vmentry)) { - return false; - } - - if (!cpu_has_cap(X86_FEATURE_IBRS_IBPB) && - !cpu_has_cap(X86_FEATURE_STIBP)) { - return false; - } - - return true; -} - void init_cpu_pre(uint16_t pcpu_id) { if (pcpu_id == BOOT_CPU_ID) { @@ -688,117 +393,6 @@ void wait_sync_change(uint64_t *sync, uint64_t wake_sync) } } -/*check allowed ONEs setting in vmx control*/ -static bool is_ctrl_setting_allowed(uint64_t msr_val, uint32_t ctrl) -{ - /* - * Intel SDM Appendix A.3 - * - bitX in ctrl can be set 1 - * only if bit 32+X in msr_val is 1 - */ - return ((((uint32_t)(msr_val >> 32UL)) & ctrl) == ctrl); -} - -static void ept_cap_detect(void) -{ - uint64_t msr_val; - - cpu_caps.ept_features = 0U; - - /* Read primary processor based VM control. */ - msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS); - - /* - * According to SDM A.3.2 Primary Processor-Based VM-Execution Controls: - * The IA32_VMX_PROCBASED_CTLS MSR (index 482H) reports on the allowed - * settings of most of the primary processor-based VM-execution controls - * (see Section 24.6.2): - * Bits 63:32 indicate the allowed 1-settings of these controls. - * VM entry allows control X to be 1 if bit 32+X in the MSR is set to 1; - * if bit 32+X in the MSR is cleared to 0, VM entry fails if control X - * is 1. - */ - msr_val = msr_val >> 32U; - - /* Check if secondary processor based VM control is available. */ - if ((msr_val & VMX_PROCBASED_CTLS_SECONDARY) != 0UL) { - /* Read secondary processor based VM control. */ - msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2); - - if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_EPT)) { - cpu_caps.ept_features = 1U; - } - } -} - -static void apicv_cap_detect(void) -{ - uint8_t features; - uint64_t msr_val; - - msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS); - if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS_TPR_SHADOW)) { - pr_fatal("APICv: No APIC TPR virtualization support."); - return; - } - - msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2); - if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC)) { - pr_fatal("APICv: No APIC-access virtualization support."); - return; - } - - if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC_REGS)) { - pr_fatal("APICv: No APIC-register virtualization support."); - return; - } - - features = (VAPIC_FEATURE_TPR_SHADOW - | VAPIC_FEATURE_VIRT_ACCESS - | VAPIC_FEATURE_VIRT_REG); - - if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VX2APIC)) { - features |= VAPIC_FEATURE_VX2APIC_MODE; - } - - if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VIRQ)) { - features |= VAPIC_FEATURE_INTR_DELIVERY; - - msr_val = msr_read(MSR_IA32_VMX_PINBASED_CTLS); - if (is_ctrl_setting_allowed(msr_val, - VMX_PINBASED_CTLS_POST_IRQ)) { - features |= VAPIC_FEATURE_POST_INTR; - } - } - cpu_caps.apicv_features = features; -} - -static void cpu_cap_detect(void) -{ - apicv_cap_detect(); - ept_cap_detect(); -} - -bool is_ept_supported(void) -{ - return (cpu_caps.ept_features != 0U); -} - -bool is_apicv_reg_virtualization_supported(void) -{ - return ((cpu_caps.apicv_features & VAPIC_FEATURE_VIRT_REG) != 0U); -} - -bool is_apicv_intr_delivery_supported(void) -{ - return ((cpu_caps.apicv_features & VAPIC_FEATURE_INTR_DELIVERY) != 0U); -} - -bool is_apicv_posted_intr_supported(void) -{ - return ((cpu_caps.apicv_features & VAPIC_FEATURE_POST_INTR) != 0U); -} - static void cpu_xsave_init(void) { uint64_t val64; @@ -820,18 +414,3 @@ static void cpu_xsave_init(void) } } } - -void cpu_l1d_flush(void) -{ - /* - * 'skip_l1dfl_vmentry' will be true on platform that - * is not affected by L1TF. - * - */ - if (!skip_l1dfl_vmentry) { - if (cpu_has_cap(X86_FEATURE_L1D_FLUSH)) { - msr_write(MSR_IA32_FLUSH_CMD, IA32_L1D_FLUSH); - } - } - -} diff --git a/hypervisor/arch/x86/cpu_caps.c b/hypervisor/arch/x86/cpu_caps.c new file mode 100644 index 000000000..98f6b92d1 --- /dev/null +++ b/hypervisor/arch/x86/cpu_caps.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TODO: add more capability per requirement */ +/* APICv features */ +#define VAPIC_FEATURE_VIRT_ACCESS (1U << 0U) +#define VAPIC_FEATURE_VIRT_REG (1U << 1U) +#define VAPIC_FEATURE_INTR_DELIVERY (1U << 2U) +#define VAPIC_FEATURE_TPR_SHADOW (1U << 3U) +#define VAPIC_FEATURE_POST_INTR (1U << 4U) +#define VAPIC_FEATURE_VX2APIC_MODE (1U << 5U) + +static struct vmx_capability { + uint32_t ept; + uint32_t vpid; +} vmx_caps; + +struct cpu_capability { + uint8_t apicv_features; + uint8_t ept_features; +}; +static struct cpu_capability cpu_caps; + +struct cpuinfo_x86 boot_cpu_data; + +static bool skip_l1dfl_vmentry; +static uint64_t x86_arch_capabilities; +int32_t ibrs_type; + +bool cpu_has_cap(uint32_t bit) +{ + uint32_t feat_idx = bit >> 5U; + uint32_t feat_bit = bit & 0x1fU; + bool ret; + + if (feat_idx >= FEATURE_WORDS) { + ret = false; + } else { + ret = ((boot_cpu_data.cpuid_leaves[feat_idx] & (1U << feat_bit)) != 0U); + } + + return ret; +} + +bool get_monitor_cap(void) +{ + if (cpu_has_cap(X86_FEATURE_MONITOR)) { + /* don't use monitor for CPU (family: 0x6 model: 0x5c) + * in hypervisor, but still expose it to the guests and + * let them handle it correctly + */ + if ((boot_cpu_data.family != 0x6U) || (boot_cpu_data.model != 0x5cU)) { + return true; + } + } + + return false; +} + +static inline bool is_fast_string_erms_supported_and_enabled(void) +{ + bool ret = false; + uint32_t misc_enable = (uint32_t)msr_read(MSR_IA32_MISC_ENABLE); + + if ((misc_enable & MSR_IA32_MISC_ENABLE_FAST_STRING) == 0U) { + pr_fatal("%s, fast string is not enabled\n", __func__); + } else { + if (!cpu_has_cap(X86_FEATURE_ERMS)) { + pr_fatal("%s, enhanced rep movsb/stosb not supported\n", __func__); + } else { + ret = true; + } + } + + return ret; +} + +static uint64_t get_address_mask(uint8_t limit) +{ + return ((1UL << limit) - 1UL) & PAGE_MASK; +} + +void get_cpu_capabilities(void) +{ + uint32_t eax, unused; + uint32_t family, model; + + cpuid(CPUID_VENDORSTRING, + &boot_cpu_data.cpuid_level, + &unused, &unused, &unused); + + cpuid(CPUID_FEATURES, &eax, &unused, + &boot_cpu_data.cpuid_leaves[FEAT_1_ECX], + &boot_cpu_data.cpuid_leaves[FEAT_1_EDX]); + family = (eax >> 8U) & 0xffU; + if (family == 0xFU) { + family += (eax >> 20U) & 0xffU; + } + boot_cpu_data.family = (uint8_t)family; + + model = (eax >> 4U) & 0xfU; + if (family >= 0x06U) { + model += ((eax >> 16U) & 0xfU) << 4U; + } + boot_cpu_data.model = (uint8_t)model; + + + cpuid(CPUID_EXTEND_FEATURE, &unused, + &boot_cpu_data.cpuid_leaves[FEAT_7_0_EBX], + &boot_cpu_data.cpuid_leaves[FEAT_7_0_ECX], + &boot_cpu_data.cpuid_leaves[FEAT_7_0_EDX]); + + cpuid(CPUID_MAX_EXTENDED_FUNCTION, + &boot_cpu_data.extended_cpuid_level, + &unused, &unused, &unused); + + if (boot_cpu_data.extended_cpuid_level >= CPUID_EXTEND_FUNCTION_1) { + cpuid(CPUID_EXTEND_FUNCTION_1, &unused, &unused, + &boot_cpu_data.cpuid_leaves[FEAT_8000_0001_ECX], + &boot_cpu_data.cpuid_leaves[FEAT_8000_0001_EDX]); + } + + if (boot_cpu_data.extended_cpuid_level >= CPUID_EXTEND_ADDRESS_SIZE) { + cpuid(CPUID_EXTEND_ADDRESS_SIZE, &eax, + &boot_cpu_data.cpuid_leaves[FEAT_8000_0008_EBX], + &unused, &unused); + + /* EAX bits 07-00: #Physical Address Bits + * bits 15-08: #Linear Address Bits + */ + boot_cpu_data.virt_bits = (uint8_t)((eax >> 8U) & 0xffU); + boot_cpu_data.phys_bits = (uint8_t)(eax & 0xffU); + boot_cpu_data.physical_address_mask = + get_address_mask(boot_cpu_data.phys_bits); + } + + /* For speculation defence. + * The default way is to set IBRS at vmexit and then do IBPB at vcpu + * context switch(ibrs_type == IBRS_RAW). + * Now provide an optimized way (ibrs_type == IBRS_OPT) which set + * STIBP and do IBPB at vmexit,since having STIBP always set has less + * impact than having IBRS always set. Also since IBPB is already done + * at vmexit, it is no necessary to do so at vcpu context switch then. + */ + ibrs_type = IBRS_NONE; + + /* Currently for APL, if we enabled retpoline, then IBRS should not + * take effect + * TODO: add IA32_ARCH_CAPABILITIES[1] check, if this bit is set, IBRS + * should be set all the time instead of relying on retpoline + */ +#ifndef CONFIG_RETPOLINE + if (cpu_has_cap(X86_FEATURE_IBRS_IBPB)) { + ibrs_type = IBRS_RAW; + if (cpu_has_cap(X86_FEATURE_STIBP)) { + ibrs_type = IBRS_OPT; + } + } +#endif +} + + +/*check allowed ONEs setting in vmx control*/ +static bool is_ctrl_setting_allowed(uint64_t msr_val, uint32_t ctrl) +{ + /* + * Intel SDM Appendix A.3 + * - bitX in ctrl can be set 1 + * only if bit 32+X in msr_val is 1 + */ + return ((((uint32_t)(msr_val >> 32UL)) & ctrl) == ctrl); +} + +static void ept_cap_detect(void) +{ + uint64_t msr_val; + + cpu_caps.ept_features = 0U; + + /* Read primary processor based VM control. */ + msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS); + + /* + * According to SDM A.3.2 Primary Processor-Based VM-Execution Controls: + * The IA32_VMX_PROCBASED_CTLS MSR (index 482H) reports on the allowed + * settings of most of the primary processor-based VM-execution controls + * (see Section 24.6.2): + * Bits 63:32 indicate the allowed 1-settings of these controls. + * VM entry allows control X to be 1 if bit 32+X in the MSR is set to 1; + * if bit 32+X in the MSR is cleared to 0, VM entry fails if control X + * is 1. + */ + msr_val = msr_val >> 32U; + + /* Check if secondary processor based VM control is available. */ + if ((msr_val & VMX_PROCBASED_CTLS_SECONDARY) != 0UL) { + /* Read secondary processor based VM control. */ + msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2); + + if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_EPT)) { + cpu_caps.ept_features = 1U; + } + } +} + +static void apicv_cap_detect(void) +{ + uint8_t features; + uint64_t msr_val; + + msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS); + if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS_TPR_SHADOW)) { + pr_fatal("APICv: No APIC TPR virtualization support."); + return; + } + + msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2); + if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC)) { + pr_fatal("APICv: No APIC-access virtualization support."); + return; + } + + if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC_REGS)) { + pr_fatal("APICv: No APIC-register virtualization support."); + return; + } + + features = (VAPIC_FEATURE_TPR_SHADOW + | VAPIC_FEATURE_VIRT_ACCESS + | VAPIC_FEATURE_VIRT_REG); + + if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VX2APIC)) { + features |= VAPIC_FEATURE_VX2APIC_MODE; + } + + if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VIRQ)) { + features |= VAPIC_FEATURE_INTR_DELIVERY; + + msr_val = msr_read(MSR_IA32_VMX_PINBASED_CTLS); + if (is_ctrl_setting_allowed(msr_val, + VMX_PINBASED_CTLS_POST_IRQ)) { + features |= VAPIC_FEATURE_POST_INTR; + } + } + cpu_caps.apicv_features = features; +} + +void cpu_cap_detect(void) +{ + apicv_cap_detect(); + ept_cap_detect(); +} + +bool is_ept_supported(void) +{ + return (cpu_caps.ept_features != 0U); +} + +bool is_apicv_reg_virtualization_supported(void) +{ + return ((cpu_caps.apicv_features & VAPIC_FEATURE_VIRT_REG) != 0U); +} + +bool is_apicv_intr_delivery_supported(void) +{ + return ((cpu_caps.apicv_features & VAPIC_FEATURE_INTR_DELIVERY) != 0U); +} + +bool is_apicv_posted_intr_supported(void) +{ + return ((cpu_caps.apicv_features & VAPIC_FEATURE_POST_INTR) != 0U); +} + +void get_cpu_name(void) +{ + cpuid(CPUID_EXTEND_FUNCTION_2, + (uint32_t *)(boot_cpu_data.model_name), + (uint32_t *)(&boot_cpu_data.model_name[4]), + (uint32_t *)(&boot_cpu_data.model_name[8]), + (uint32_t *)(&boot_cpu_data.model_name[12])); + cpuid(CPUID_EXTEND_FUNCTION_3, + (uint32_t *)(&boot_cpu_data.model_name[16]), + (uint32_t *)(&boot_cpu_data.model_name[20]), + (uint32_t *)(&boot_cpu_data.model_name[24]), + (uint32_t *)(&boot_cpu_data.model_name[28])); + cpuid(CPUID_EXTEND_FUNCTION_4, + (uint32_t *)(&boot_cpu_data.model_name[32]), + (uint32_t *)(&boot_cpu_data.model_name[36]), + (uint32_t *)(&boot_cpu_data.model_name[40]), + (uint32_t *)(&boot_cpu_data.model_name[44])); + + boot_cpu_data.model_name[48] = '\0'; +} + +bool check_cpu_security_config(void) +{ + if (cpu_has_cap(X86_FEATURE_ARCH_CAP)) { + x86_arch_capabilities = msr_read(MSR_IA32_ARCH_CAPABILITIES); + skip_l1dfl_vmentry = ((x86_arch_capabilities + & IA32_ARCH_CAP_SKIP_L1DFL_VMENTRY) != 0UL); + } else { + return false; + } + + if ((!cpu_has_cap(X86_FEATURE_L1D_FLUSH)) && (!skip_l1dfl_vmentry)) { + return false; + } + + if (!cpu_has_cap(X86_FEATURE_IBRS_IBPB) && + !cpu_has_cap(X86_FEATURE_STIBP)) { + return false; + } + + return true; +} + +void cpu_l1d_flush(void) +{ + /* + * 'skip_l1dfl_vmentry' will be true on platform that + * is not affected by L1TF. + * + */ + if (!skip_l1dfl_vmentry) { + if (cpu_has_cap(X86_FEATURE_L1D_FLUSH)) { + msr_write(MSR_IA32_FLUSH_CMD, IA32_L1D_FLUSH); + } + } + +} + +static inline bool is_vmx_disabled(void) +{ + uint64_t msr_val; + + /* Read Feature ControL MSR */ + msr_val = msr_read(MSR_IA32_FEATURE_CONTROL); + + /* Check if feature control is locked and vmx cannot be enabled */ + if ((msr_val & MSR_IA32_FEATURE_CONTROL_LOCK) != 0U && + (msr_val & MSR_IA32_FEATURE_CONTROL_VMX_NO_SMX) == 0U) { + return true; + } + return false; +} + +static inline bool cpu_has_vmx_unrestricted_guest_cap(void) +{ + return ((msr_read(MSR_IA32_VMX_MISC) & VMX_SUPPORT_UNRESTRICTED_GUEST) + != 0UL); +} + +bool cpu_has_vmx_ept_cap(uint32_t bit_mask) +{ + return ((vmx_caps.ept & bit_mask) != 0U); +} + +bool cpu_has_vmx_vpid_cap(uint32_t bit_mask) +{ + return ((vmx_caps.vpid & bit_mask) != 0U); +} + +static int32_t check_vmx_mmu_cap(void) +{ + uint64_t val; + + /* Read the MSR register of EPT and VPID Capability - SDM A.10 */ + val = msr_read(MSR_IA32_VMX_EPT_VPID_CAP); + vmx_caps.ept = (uint32_t) val; + vmx_caps.vpid = (uint32_t) (val >> 32U); + + if (!cpu_has_vmx_ept_cap(VMX_EPT_INVEPT)) { + pr_fatal("%s, invept not supported\n", __func__); + return -ENODEV; + } + + if (!cpu_has_vmx_vpid_cap(VMX_VPID_INVVPID) || + !cpu_has_vmx_vpid_cap(VMX_VPID_INVVPID_SINGLE_CONTEXT) || + !cpu_has_vmx_vpid_cap(VMX_VPID_INVVPID_GLOBAL_CONTEXT)) { + pr_fatal("%s, invvpid not supported\n", __func__); + return -ENODEV; + } + + if (!cpu_has_vmx_ept_cap(VMX_EPT_1GB_PAGE)) { + pr_fatal("%s, ept not support 1GB large page\n", __func__); + return -ENODEV; + } + + return 0; +} + + +/* + * basic hardware capability check + * we should supplement which feature/capability we must support + * here later. + */ +int32_t hardware_detect_support(void) +{ + int32_t ret; + + /* Long Mode (x86-64, 64-bit support) */ + if (!cpu_has_cap(X86_FEATURE_LM)) { + pr_fatal("%s, LM not supported\n", __func__); + return -ENODEV; + } + if ((boot_cpu_data.phys_bits == 0U) || + (boot_cpu_data.virt_bits == 0U)) { + pr_fatal("%s, can't detect Linear/Physical Address size\n", + __func__); + return -ENODEV; + } + + /* lapic TSC deadline timer */ + if (!cpu_has_cap(X86_FEATURE_TSC_DEADLINE)) { + pr_fatal("%s, TSC deadline not supported\n", __func__); + return -ENODEV; + } + + /* Execute Disable */ + if (!cpu_has_cap(X86_FEATURE_NX)) { + pr_fatal("%s, NX not supported\n", __func__); + return -ENODEV; + } + + /* Supervisor-Mode Execution Prevention */ + if (!cpu_has_cap(X86_FEATURE_SMEP)) { + pr_fatal("%s, SMEP not supported\n", __func__); + return -ENODEV; + } + + /* Supervisor-Mode Access Prevention */ + if (!cpu_has_cap(X86_FEATURE_SMAP)) { + pr_fatal("%s, SMAP not supported\n", __func__); + return -ENODEV; + } + + if (!cpu_has_cap(X86_FEATURE_MTRR)) { + pr_fatal("%s, MTRR not supported\n", __func__); + return -ENODEV; + } + + if (!cpu_has_cap(X86_FEATURE_PAGE1GB)) { + pr_fatal("%s, not support 1GB page\n", __func__); + return -ENODEV; + } + + if (!cpu_has_cap(X86_FEATURE_VMX)) { + pr_fatal("%s, vmx not supported\n", __func__); + return -ENODEV; + } + + if (!is_fast_string_erms_supported_and_enabled()) { + return -ENODEV; + } + + if (!cpu_has_vmx_unrestricted_guest_cap()) { + pr_fatal("%s, unrestricted guest not supported\n", __func__); + return -ENODEV; + } + + if (!is_ept_supported()) { + pr_fatal("%s, EPT not supported\n", __func__); + return -ENODEV; + } + + if (boot_cpu_data.cpuid_level < 0x15U) { + pr_fatal("%s, required CPU feature not supported\n", __func__); + return -ENODEV; + } + + if (is_vmx_disabled()) { + pr_fatal("%s, VMX can not be enabled\n", __func__); + return -ENODEV; + } + + if (phys_cpu_num > CONFIG_MAX_PCPU_NUM) { + pr_fatal("%s, pcpu number(%d) is out of range\n", __func__, phys_cpu_num); + return -ENODEV; + } + + ret = check_vmx_mmu_cap(); + if (ret != 0) { + return ret; + } + + pr_acrnlog("hardware support HV"); + return 0; +} diff --git a/hypervisor/arch/x86/mmu.c b/hypervisor/arch/x86/mmu.c index b88795b5c..180d6bc04 100644 --- a/hypervisor/arch/x86/mmu.c +++ b/hypervisor/arch/x86/mmu.c @@ -34,11 +34,6 @@ static void *ppt_mmu_pml4_addr; static uint8_t sanitized_page[PAGE_SIZE] __aligned(PAGE_SIZE); -static struct vmx_capability { - uint32_t ept; - uint32_t vpid; -} vmx_caps; - /* * If the logical processor is in VMX non-root operation and * the "enable VPID" VM-execution control is 1, the current VPID @@ -97,45 +92,6 @@ static inline void local_invept(uint64_t type, struct invept_desc desc) ASSERT(error == 0, "invept error"); } -static inline bool cpu_has_vmx_ept_cap(uint32_t bit_mask) -{ - return ((vmx_caps.ept & bit_mask) != 0U); -} - -static inline bool cpu_has_vmx_vpid_cap(uint32_t bit_mask) -{ - return ((vmx_caps.vpid & bit_mask) != 0U); -} - -int32_t check_vmx_mmu_cap(void) -{ - uint64_t val; - - /* Read the MSR register of EPT and VPID Capability - SDM A.10 */ - val = msr_read(MSR_IA32_VMX_EPT_VPID_CAP); - vmx_caps.ept = (uint32_t) val; - vmx_caps.vpid = (uint32_t) (val >> 32U); - - if (!cpu_has_vmx_ept_cap(VMX_EPT_INVEPT)) { - pr_fatal("%s, invept not supported\n", __func__); - return -ENODEV; - } - - if (!cpu_has_vmx_vpid_cap(VMX_VPID_INVVPID) || - !cpu_has_vmx_vpid_cap(VMX_VPID_INVVPID_SINGLE_CONTEXT) || - !cpu_has_vmx_vpid_cap(VMX_VPID_INVVPID_GLOBAL_CONTEXT)) { - pr_fatal("%s, invvpid not supported\n", __func__); - return -ENODEV; - } - - if (!cpu_has_vmx_ept_cap(VMX_EPT_1GB_PAGE)) { - pr_fatal("%s, ept not support 1GB large page\n", __func__); - return -ENODEV; - } - - return 0; -} - uint16_t allocate_vpid(void) { uint16_t vpid = atomic_xadd16(&vmx_vpid_nr, 1U); diff --git a/hypervisor/arch/x86/vmx_asm.S b/hypervisor/arch/x86/vmx_asm.S index 40c9f07d9..460b7b8fc 100644 --- a/hypervisor/arch/x86/vmx_asm.S +++ b/hypervisor/arch/x86/vmx_asm.S @@ -9,6 +9,7 @@ #include #include #include +#include #include /* NOTE: diff --git a/hypervisor/include/arch/x86/cpu.h b/hypervisor/include/arch/x86/cpu.h index e22818374..824732a07 100644 --- a/hypervisor/include/arch/x86/cpu.h +++ b/hypervisor/include/arch/x86/cpu.h @@ -127,15 +127,6 @@ /* Boot CPU ID */ #define BOOT_CPU_ID 0U -/* type of speculation control - * 0 - no speculation control support - * 1 - raw IBRS + IPBP support - * 2 - with STIBP optimization support - */ -#define IBRS_NONE 0 -#define IBRS_RAW 1 -#define IBRS_OPT 2 - #ifndef ASSEMBLER #define BUS_LOCK "lock ; " @@ -209,8 +200,6 @@ extern uint8_t ld_bss_end; extern uint64_t main_entry[1]; extern uint64_t secondary_cpu_stack[1]; -extern int32_t ibrs_type; - /* * To support per_cpu access, we use a special struct "per_cpu_region" to hold * the pattern of per CPU data. And we allocate memory for per CPU data @@ -229,16 +218,6 @@ extern int32_t ibrs_type; * to locate the per cpu data. */ -/* CPUID feature words */ -#define FEAT_1_ECX 0U /* CPUID[1].ECX */ -#define FEAT_1_EDX 1U /* CPUID[1].EDX */ -#define FEAT_7_0_EBX 2U /* CPUID[EAX=7,ECX=0].EBX */ -#define FEAT_7_0_ECX 3U /* CPUID[EAX=7,ECX=0].ECX */ -#define FEAT_7_0_EDX 4U /* CPUID[EAX=7,ECX=0].EDX */ -#define FEAT_8000_0001_ECX 5U /* CPUID[8000_0001].ECX */ -#define FEAT_8000_0001_EDX 6U /* CPUID[8000_0001].EDX */ -#define FEAT_8000_0008_EBX 7U /* CPUID[8000_0008].EAX */ -#define FEATURE_WORDS 8U /** *The invalid cpu_id (INVALID_CPU_ID) is error *code for error handling, this means that @@ -267,24 +246,6 @@ enum pcpu_boot_state { PCPU_STATE_DEAD, }; -struct cpu_state_info { - uint8_t px_cnt; /* count of all Px states */ - const struct cpu_px_data *px_data; - uint8_t cx_cnt; /* count of all Cx entries */ - const struct cpu_cx_data *cx_data; -}; - -struct cpuinfo_x86 { - uint8_t family, model; - uint8_t virt_bits; - uint8_t phys_bits; - uint32_t cpuid_level; - uint32_t extended_cpuid_level; - uint64_t physical_address_mask; - uint32_t cpuid_leaves[FEATURE_WORDS]; - char model_name[64]; - struct cpu_state_info state_info; -}; #ifdef STACK_PROTECTOR struct stack_canary { /* Gcc generates extra code, using [fs:40] to access canary */ @@ -294,32 +255,16 @@ struct stack_canary { void __stack_chk_fail(void); #endif -extern struct cpuinfo_x86 boot_cpu_data; - -#define MAX_PSTATE 20U /* max num of supported Px count */ -#define MAX_CSTATE 8U /* max num of supported Cx count */ - -/* We support MAX_CSTATE num of Cx, means have (MAX_CSTATE - 1) Cx entries, - * i.e. supported Cx entry index range from 1 to MAX_CX_ENTRY. - */ -#define MAX_CX_ENTRY (MAX_CSTATE - 1U) - /* Function prototypes */ void cpu_do_idle(void); void cpu_dead(void); void trampoline_start16(void); -bool is_apicv_reg_virtualization_supported(void); -bool is_apicv_intr_delivery_supported(void); -bool is_apicv_posted_intr_supported(void); -bool is_ept_supported(void); -bool cpu_has_cap(uint32_t bit); void load_cpu_state_data(void); void init_cpu_pre(uint16_t pcpu_id); void init_cpu_post(uint16_t pcpu_id); void start_cpus(void); void stop_cpus(void); void wait_sync_change(uint64_t *sync, uint64_t wake_sync); -void cpu_l1d_flush(void); #define CPU_SEG_READ(seg, result_ptr) \ { \ diff --git a/hypervisor/include/arch/x86/cpu_caps.h b/hypervisor/include/arch/x86/cpu_caps.h new file mode 100644 index 000000000..20959f300 --- /dev/null +++ b/hypervisor/include/arch/x86/cpu_caps.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CPUINFO_H +#define CPUINFO_H + +/* type of speculation control + * 0 - no speculation control support + * 1 - raw IBRS + IPBP support + * 2 - with STIBP optimization support + */ +#define IBRS_NONE 0 +#define IBRS_RAW 1 +#define IBRS_OPT 2 + +#ifndef ASSEMBLER + +extern int32_t ibrs_type; + +struct cpu_state_info { + uint8_t px_cnt; /* count of all Px states */ + const struct cpu_px_data *px_data; + uint8_t cx_cnt; /* count of all Cx entries */ + const struct cpu_cx_data *cx_data; +}; + +#define MAX_PSTATE 20U /* max num of supported Px count */ +#define MAX_CSTATE 8U /* max num of supported Cx count */ +/* We support MAX_CSTATE num of Cx, means have (MAX_CSTATE - 1) Cx entries, + * i.e. supported Cx entry index range from 1 to MAX_CX_ENTRY. + */ +#define MAX_CX_ENTRY (MAX_CSTATE - 1U) + +/* CPUID feature words */ +#define FEAT_1_ECX 0U /* CPUID[1].ECX */ +#define FEAT_1_EDX 1U /* CPUID[1].EDX */ +#define FEAT_7_0_EBX 2U /* CPUID[EAX=7,ECX=0].EBX */ +#define FEAT_7_0_ECX 3U /* CPUID[EAX=7,ECX=0].ECX */ +#define FEAT_7_0_EDX 4U /* CPUID[EAX=7,ECX=0].EDX */ +#define FEAT_8000_0001_ECX 5U /* CPUID[8000_0001].ECX */ +#define FEAT_8000_0001_EDX 6U /* CPUID[8000_0001].EDX */ +#define FEAT_8000_0008_EBX 7U /* CPUID[8000_0008].EAX */ +#define FEATURE_WORDS 8U + +struct cpuinfo_x86 { + uint8_t family, model; + uint8_t virt_bits; + uint8_t phys_bits; + uint32_t cpuid_level; + uint32_t extended_cpuid_level; + uint64_t physical_address_mask; + uint32_t cpuid_leaves[FEATURE_WORDS]; + char model_name[64]; + struct cpu_state_info state_info; +}; + +extern struct cpuinfo_x86 boot_cpu_data; + +bool get_monitor_cap(void); +bool is_apicv_reg_virtualization_supported(void); +bool is_apicv_intr_delivery_supported(void); +bool is_apicv_posted_intr_supported(void); +bool is_ept_supported(void); +bool cpu_has_cap(uint32_t bit); +bool cpu_has_vmx_ept_cap(uint32_t bit_mask); +bool cpu_has_vmx_vpid_cap(uint32_t bit_mask); +void get_cpu_capabilities(void); +void get_cpu_name(void); +void cpu_cap_detect(void); +bool check_cpu_security_config(void); +void cpu_l1d_flush(void); +int hardware_detect_support(void); + +#endif /* ASSEMBLER */ + +#endif /* CPUINFO_H */ diff --git a/hypervisor/include/arch/x86/guest/vm.h b/hypervisor/include/arch/x86/guest/vm.h index a0fff4cf2..423357635 100644 --- a/hypervisor/include/arch/x86/guest/vm.h +++ b/hypervisor/include/arch/x86/guest/vm.h @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef CONFIG_PARTITION_MODE #include diff --git a/hypervisor/include/arch/x86/mmu.h b/hypervisor/include/arch/x86/mmu.h index 12447a047..6663fc3d8 100644 --- a/hypervisor/include/arch/x86/mmu.h +++ b/hypervisor/include/arch/x86/mmu.h @@ -126,13 +126,6 @@ void mmu_modify_or_del(uint64_t *pml4_page, uint64_t vaddr_base, uint64_t size, uint64_t prot_set, uint64_t prot_clr, const struct memory_ops *mem_ops, uint32_t type); void hv_access_memory_region_update(uint64_t base, uint64_t size); -/** - * @brief EPT and VPID capability checking - * - * @retval 0 on success - * @retval -ENODEV Don't support EPT or VPID capability - */ -int32_t check_vmx_mmu_cap(void); /** * @brief VPID allocation * diff --git a/hypervisor/include/arch/x86/vmcs.h b/hypervisor/include/arch/x86/vmcs.h index c0c7eb689..d5e2a5371 100644 --- a/hypervisor/include/arch/x86/vmcs.h +++ b/hypervisor/include/arch/x86/vmcs.h @@ -66,7 +66,6 @@ int32_t vmx_wrmsr_pat(struct acrn_vcpu *vcpu, uint64_t value); void vmx_write_cr0(struct acrn_vcpu *vcpu, uint64_t cr0); void vmx_write_cr4(struct acrn_vcpu *vcpu, uint64_t cr4); -bool is_vmx_disabled(void); void switch_apicv_mode_x2apic(struct acrn_vcpu *vcpu); static inline enum vm_cpu_mode get_vcpu_mode(const struct acrn_vcpu *vcpu) @@ -74,12 +73,6 @@ static inline enum vm_cpu_mode get_vcpu_mode(const struct acrn_vcpu *vcpu) return vcpu->arch.cpu_mode; } -static inline bool cpu_has_vmx_unrestricted_guest_cap(void) -{ - return ((msr_read(MSR_IA32_VMX_MISC) & VMX_SUPPORT_UNRESTRICTED_GUEST) - != 0UL); -} - #endif /* ASSEMBLER */ #endif /* VMCS_H_ */