From 7bc1a3f925c346dabeb165d9caf933f0bd1c7ed5 Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Thu, 23 Aug 2018 18:29:31 +0800 Subject: [PATCH] HV: Refine APICv capabilities detection - by default, ACRN will not support platform without below APICv features: -- Use TPR shadow -- APIC-register virtualization - remove mmio emualtion of local APIC for guest Signed-off-by: Yonghua Huang Acked-by: Anthony Xu --- hypervisor/arch/x86/cpu.c | 27 ++--- hypervisor/arch/x86/guest/vlapic.c | 115 ++------------------- hypervisor/arch/x86/vmx.c | 82 ++++++--------- hypervisor/include/arch/x86/cpu.h | 2 - hypervisor/include/arch/x86/guest/vlapic.h | 14 +-- 5 files changed, 51 insertions(+), 189 deletions(-) diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index 5b640aba0..e14102505 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -790,26 +790,27 @@ static void apicv_cap_detect(void) uint8_t features; uint64_t msr_val; - features = 0U; - msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS); if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS_TPR_SHADOW)) { - cpu_caps.apicv_features = 0U; + pr_fatal("APICv: No APIC TPR virtualization support."); return; } - features |= VAPIC_FEATURE_TPR_SHADOW; msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2); if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC)) { - cpu_caps.apicv_features = features; + pr_fatal("APICv: No APIC-access virtualization support."); return; } - features |= VAPIC_FEATURE_VIRT_ACCESS; - if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC_REGS)) { - features |= VAPIC_FEATURE_VIRT_REG; + 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; } @@ -823,7 +824,6 @@ static void apicv_cap_detect(void) features |= VAPIC_FEATURE_POST_INTR; } } - cpu_caps.apicv_features = features; } @@ -838,20 +838,11 @@ bool is_ept_supported(void) return (cpu_caps.ept_features != 0U); } -bool is_apicv_supported(void) -{ - return ((cpu_caps.apicv_features & VAPIC_FEATURE_VIRT_ACCESS) != 0U); -} - bool is_apicv_intr_delivery_supported(void) { return ((cpu_caps.apicv_features & VAPIC_FEATURE_INTR_DELIVERY) != 0U); } -bool is_apicv_virt_reg_supported(void) -{ - return ((cpu_caps.apicv_features & VAPIC_FEATURE_VIRT_REG) != 0U); -} static void cpu_xsave_init(void) { diff --git a/hypervisor/arch/x86/guest/vlapic.c b/hypervisor/arch/x86/guest/vlapic.c index 3da87a48f..d5867a984 100644 --- a/hypervisor/arch/x86/guest/vlapic.c +++ b/hypervisor/arch/x86/guest/vlapic.c @@ -1950,81 +1950,6 @@ vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t wval) return error; } -int -vlapic_write_mmio_reg(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, - uint8_t size) -{ - int error; - uint32_t off; - struct acrn_vlapic *vlapic; - - off = (uint32_t)(gpa - DEFAULT_APIC_BASE); - - /* - * Memory mapped local apic accesses must be 4 bytes wide and - * aligned on a 16-byte boundary. - */ - if ((size != 4U) || ((off & 0xfU) != 0U)) { - return -EINVAL; - } - - vlapic = vcpu->arch_vcpu.vlapic; - error = vlapic_write(vlapic, off, wval); - return error; -} - -int -vlapic_read_mmio_reg(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, - __unused uint8_t size) -{ - int error; - uint32_t off; - struct acrn_vlapic *vlapic; - - off = (uint32_t)(gpa - DEFAULT_APIC_BASE); - - /* - * Memory mapped local apic accesses should be aligned on a - * 16-byte boundary. They are also suggested to be 4 bytes - * wide, alas not all OSes follow suggestions. - */ - off &= ~0x3U; - if ((off & 0xfU) != 0U) { - return -EINVAL; - } - - vlapic = vcpu->arch_vcpu.vlapic; - error = vlapic_read(vlapic, off, rval); - return error; -} - -int vlapic_mmio_access_handler(struct vcpu *vcpu, struct io_request *io_req, - __unused void *handler_private_data) -{ - struct mmio_request *mmio_req = &io_req->reqs.mmio; - uint64_t gpa = mmio_req->address; - int ret = 0; - - /* Note all RW to LAPIC are 32-Bit in size */ - ASSERT(mmio_req->size == 4UL, "All RW to LAPIC must be 32-bits in size"); - - if (mmio_req->direction == REQUEST_READ) { - ret = vlapic_read_mmio_reg(vcpu, - gpa, - &mmio_req->value, - mmio_req->size); - } else if (mmio_req->direction == REQUEST_WRITE) { - ret = vlapic_write_mmio_reg(vcpu, - gpa, - mmio_req->value, - mmio_req->size); - } else { - /* Can never happen due to the range of mmio_req->direction. */ - } - - return ret; -} - int vlapic_create(struct vcpu *vcpu) { struct acrn_vlapic *vlapic = calloc(1U, sizeof(struct acrn_vlapic)); @@ -2032,34 +1957,21 @@ int vlapic_create(struct vcpu *vcpu) ASSERT(vlapic != NULL, "vlapic allocate failed"); vlapic->vm = vcpu->vm; vlapic->vcpu = vcpu; - if (is_apicv_supported()) { - if (is_vcpu_bsp(vcpu)) { - uint64_t *pml4_page = - (uint64_t *)vcpu->vm->arch_vm.nworld_eptp; - ept_mr_del(vcpu->vm, pml4_page, - DEFAULT_APIC_BASE, CPU_PAGE_SIZE); - ept_mr_add(vcpu->vm, pml4_page, - vlapic_apicv_get_apic_access_addr(vcpu->vm), - DEFAULT_APIC_BASE, CPU_PAGE_SIZE, - EPT_WR | EPT_RD | EPT_UNCACHED); - } - } else { - /*No APICv support*/ - if (register_mmio_emulation_handler(vcpu->vm, - vlapic_mmio_access_handler, - (uint64_t)DEFAULT_APIC_BASE, - (uint64_t)DEFAULT_APIC_BASE + - CPU_PAGE_SIZE, - (void *) 0) != 0) { - free(vlapic); - return -1; - } + if (is_vcpu_bsp(vcpu)) { + uint64_t *pml4_page = + (uint64_t *)vcpu->vm->arch_vm.nworld_eptp; + ept_mr_del(vcpu->vm, pml4_page, + DEFAULT_APIC_BASE, CPU_PAGE_SIZE); + + ept_mr_add(vcpu->vm, pml4_page, + vlapic_apicv_get_apic_access_addr(vcpu->vm), + DEFAULT_APIC_BASE, CPU_PAGE_SIZE, + EPT_WR | EPT_RD | EPT_UNCACHED); } vcpu->arch_vcpu.vlapic = vlapic; vlapic_init(vlapic); - return 0; } @@ -2075,15 +1987,8 @@ void vlapic_free(struct vcpu *vcpu) if (vlapic == NULL) { return; } - del_timer(&vlapic->vtimer.timer); - if (!is_apicv_supported()) { - unregister_mmio_emulation_handler(vcpu->vm, - (uint64_t)DEFAULT_APIC_BASE, - (uint64_t)DEFAULT_APIC_BASE + CPU_PAGE_SIZE); - } - free(vlapic); } diff --git a/hypervisor/arch/x86/vmx.c b/hypervisor/arch/x86/vmx.c index 8b02ccd71..374fc2145 100644 --- a/hypervisor/arch/x86/vmx.c +++ b/hypervisor/arch/x86/vmx.c @@ -953,6 +953,7 @@ static void init_exec_ctrl(struct vcpu *vcpu) value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS, VMX_PROCBASED_CTLS_TSC_OFF | /* VMX_PROCBASED_CTLS_RDTSC | */ + VMX_PROCBASED_CTLS_TPR_SHADOW| VMX_PROCBASED_CTLS_IO_BITMAP | VMX_PROCBASED_CTLS_MSR_BITMAP | VMX_PROCBASED_CTLS_SECONDARY); @@ -966,15 +967,6 @@ static void init_exec_ctrl(struct vcpu *vcpu) */ value32 &= ~VMX_PROCBASED_CTLS_INVLPG; - if (is_apicv_supported()) { - value32 |= VMX_PROCBASED_CTLS_TPR_SHADOW; - } else { - /* Add CR8 VMExit for vlapic */ - value32 |= - (VMX_PROCBASED_CTLS_CR8_LOAD | - VMX_PROCBASED_CTLS_CR8_STORE); - } - exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS, value32); pr_dbg("VMX_PROC_VM_EXEC_CONTROLS: 0x%x ", value32); @@ -983,9 +975,11 @@ static void init_exec_ctrl(struct vcpu *vcpu) * guest (optional) */ value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS2, + VMX_PROCBASED_CTLS2_VAPIC | VMX_PROCBASED_CTLS2_EPT | VMX_PROCBASED_CTLS2_RDTSCP | - VMX_PROCBASED_CTLS2_UNRESTRICT); + VMX_PROCBASED_CTLS2_UNRESTRICT| + VMX_PROCBASED_CTLS2_VAPIC_REGS); if (vcpu->arch_vcpu.vpid != 0U) { value32 |= VMX_PROCBASED_CTLS2_VPID; @@ -993,27 +987,18 @@ static void init_exec_ctrl(struct vcpu *vcpu) value32 &= ~VMX_PROCBASED_CTLS2_VPID; } - if (is_apicv_supported()) { - value32 |= VMX_PROCBASED_CTLS2_VAPIC; - - if (is_apicv_virt_reg_supported()) { - value32 |= VMX_PROCBASED_CTLS2_VAPIC_REGS; - } - - if (is_apicv_intr_delivery_supported()) { - value32 |= VMX_PROCBASED_CTLS2_VIRQ; - } - else { - /* - * This field exists only on processors that support - * the 1-setting of the "use TPR shadow" - * VM-execution control. - * - * Set up TPR threshold for virtual interrupt delivery - * - pg 2904 24.6.8 - */ - exec_vmwrite32(VMX_TPR_THRESHOLD, 0U); - } + if (is_apicv_intr_delivery_supported()) { + value32 |= VMX_PROCBASED_CTLS2_VIRQ; + } else { + /* + * This field exists only on processors that support + * the 1-setting of the "use TPR shadow" + * VM-execution control. + * + * Set up TPR threshold for virtual interrupt delivery + * - pg 2904 24.6.8 + */ + exec_vmwrite32(VMX_TPR_THRESHOLD, 0U); } if (cpu_has_cap(X86_FEATURE_OSXSAVE)) { @@ -1024,29 +1009,24 @@ static void init_exec_ctrl(struct vcpu *vcpu) exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS2, value32); pr_dbg("VMX_PROC_VM_EXEC_CONTROLS2: 0x%x ", value32); - if (is_apicv_supported()) { - /*APIC-v, config APIC-access address*/ - value64 = vlapic_apicv_get_apic_access_addr(vcpu->vm); - exec_vmwrite64(VMX_APIC_ACCESS_ADDR_FULL, - value64); + /*APIC-v, config APIC-access address*/ + value64 = vlapic_apicv_get_apic_access_addr(vcpu->vm); + exec_vmwrite64(VMX_APIC_ACCESS_ADDR_FULL, value64); - /*APIC-v, config APIC virtualized page address*/ - value64 = vlapic_apicv_get_apic_page_addr( - vcpu->arch_vcpu.vlapic); - exec_vmwrite64(VMX_VIRTUAL_APIC_PAGE_ADDR_FULL, - value64); + /*APIC-v, config APIC virtualized page address*/ + value64 = vlapic_apicv_get_apic_page_addr(vcpu->arch_vcpu.vlapic); + exec_vmwrite64(VMX_VIRTUAL_APIC_PAGE_ADDR_FULL, value64); - if (is_apicv_intr_delivery_supported()) { - /* Disable all EOI VMEXIT by default and - * clear RVI and SVI. - */ - exec_vmwrite64(VMX_EOI_EXIT0_FULL, 0UL); - exec_vmwrite64(VMX_EOI_EXIT1_FULL, 0UL); - exec_vmwrite64(VMX_EOI_EXIT2_FULL, 0UL); - exec_vmwrite64(VMX_EOI_EXIT3_FULL, 0UL); + if (is_apicv_intr_delivery_supported()) { + /* Disable all EOI VMEXIT by default and + * clear RVI and SVI. + */ + exec_vmwrite64(VMX_EOI_EXIT0_FULL, 0UL); + exec_vmwrite64(VMX_EOI_EXIT1_FULL, 0UL); + exec_vmwrite64(VMX_EOI_EXIT2_FULL, 0UL); + exec_vmwrite64(VMX_EOI_EXIT3_FULL, 0UL); - exec_vmwrite16(VMX_GUEST_INTR_STATUS, 0); - } + exec_vmwrite16(VMX_GUEST_INTR_STATUS, 0); } /* Load EPTP execution control diff --git a/hypervisor/include/arch/x86/cpu.h b/hypervisor/include/arch/x86/cpu.h index 0691fca57..781096d34 100644 --- a/hypervisor/include/arch/x86/cpu.h +++ b/hypervisor/include/arch/x86/cpu.h @@ -323,9 +323,7 @@ extern struct cpuinfo_x86 boot_cpu_data; void cpu_do_idle(__unused uint16_t pcpu_id); void cpu_dead(uint16_t pcpu_id); void trampoline_start16(void); -bool is_apicv_supported(void); bool is_apicv_intr_delivery_supported(void); -bool is_apicv_virt_reg_supported(void); bool is_ept_supported(void); bool cpu_has_cap(uint32_t bit); void load_cpu_state_data(void); diff --git a/hypervisor/include/arch/x86/guest/vlapic.h b/hypervisor/include/arch/x86/guest/vlapic.h index b93fc7fa9..0206d374c 100644 --- a/hypervisor/include/arch/x86/guest/vlapic.h +++ b/hypervisor/include/arch/x86/guest/vlapic.h @@ -60,11 +60,6 @@ struct acrn_vlapic *vm_lapic_from_pcpuid(struct vm *vm, uint16_t pcpu_id); int vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval); int vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t wval); -int vlapic_read_mmio_reg(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, - __unused uint8_t size); -int vlapic_write_mmio_reg(struct vcpu *vcpu, uint64_t gpa, - uint64_t wval, uint8_t size); - /* * Signals to the LAPIC that an interrupt at 'vector' needs to be generated * to the 'cpu', the state is recorded in IRR. @@ -107,15 +102,9 @@ void vlapic_reset_tmr(struct acrn_vlapic *vlapic); void vlapic_set_tmr_one_vec(struct acrn_vlapic *vlapic, uint32_t delmode, uint32_t vector, bool level); -void -vlapic_apicv_batch_set_tmr(struct acrn_vlapic *vlapic); - -int vlapic_mmio_access_handler(struct vcpu *vcpu, struct io_request *io_req, - __unused void *handler_private_data); - +void vlapic_apicv_batch_set_tmr(struct acrn_vlapic *vlapic); uint32_t vlapic_get_id(struct acrn_vlapic *vlapic); uint8_t vlapic_get_apicid(struct acrn_vlapic *vlapic); - int vlapic_create(struct vcpu *vcpu); void vlapic_free(struct vcpu *vcpu); void vlapic_init(struct acrn_vlapic *vlapic); @@ -129,6 +118,5 @@ int apic_access_vmexit_handler(struct vcpu *vcpu); int apic_write_vmexit_handler(struct vcpu *vcpu); int veoi_vmexit_handler(struct vcpu *vcpu); int tpr_below_threshold_vmexit_handler(__unused struct vcpu *vcpu); - void calcvdest(struct vm *vm, uint64_t *dmask, uint32_t dest, bool phys); #endif /* _VLAPIC_H_ */