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 <yonghua.huang@intel.com>
Acked-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
Yonghua Huang 2018-08-23 18:29:31 +08:00 committed by lijinxia
parent f95d07dc43
commit 7bc1a3f925
5 changed files with 51 additions and 189 deletions

View File

@ -790,26 +790,27 @@ static void apicv_cap_detect(void)
uint8_t features; uint8_t features;
uint64_t msr_val; uint64_t msr_val;
features = 0U;
msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS); msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS);
if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS_TPR_SHADOW)) { 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; return;
} }
features |= VAPIC_FEATURE_TPR_SHADOW;
msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2); msr_val = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2);
if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC)) { 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; return;
} }
features |= VAPIC_FEATURE_VIRT_ACCESS;
if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC_REGS)) { if (!is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VAPIC_REGS)) {
features |= VAPIC_FEATURE_VIRT_REG; 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)) { if (is_ctrl_setting_allowed(msr_val, VMX_PROCBASED_CTLS2_VX2APIC)) {
features |= VAPIC_FEATURE_VX2APIC_MODE; features |= VAPIC_FEATURE_VX2APIC_MODE;
} }
@ -823,7 +824,6 @@ static void apicv_cap_detect(void)
features |= VAPIC_FEATURE_POST_INTR; features |= VAPIC_FEATURE_POST_INTR;
} }
} }
cpu_caps.apicv_features = features; cpu_caps.apicv_features = features;
} }
@ -838,20 +838,11 @@ bool is_ept_supported(void)
return (cpu_caps.ept_features != 0U); 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) bool is_apicv_intr_delivery_supported(void)
{ {
return ((cpu_caps.apicv_features & VAPIC_FEATURE_INTR_DELIVERY) != 0U); 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) static void cpu_xsave_init(void)
{ {

View File

@ -1950,81 +1950,6 @@ vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t wval)
return error; 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) int vlapic_create(struct vcpu *vcpu)
{ {
struct acrn_vlapic *vlapic = calloc(1U, sizeof(struct acrn_vlapic)); struct acrn_vlapic *vlapic = calloc(1U, sizeof(struct acrn_vlapic));
@ -2032,7 +1957,7 @@ int vlapic_create(struct vcpu *vcpu)
ASSERT(vlapic != NULL, "vlapic allocate failed"); ASSERT(vlapic != NULL, "vlapic allocate failed");
vlapic->vm = vcpu->vm; vlapic->vm = vcpu->vm;
vlapic->vcpu = vcpu; vlapic->vcpu = vcpu;
if (is_apicv_supported()) {
if (is_vcpu_bsp(vcpu)) { if (is_vcpu_bsp(vcpu)) {
uint64_t *pml4_page = uint64_t *pml4_page =
(uint64_t *)vcpu->vm->arch_vm.nworld_eptp; (uint64_t *)vcpu->vm->arch_vm.nworld_eptp;
@ -2044,22 +1969,9 @@ int vlapic_create(struct vcpu *vcpu)
DEFAULT_APIC_BASE, CPU_PAGE_SIZE, DEFAULT_APIC_BASE, CPU_PAGE_SIZE,
EPT_WR | EPT_RD | EPT_UNCACHED); 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;
}
}
vcpu->arch_vcpu.vlapic = vlapic; vcpu->arch_vcpu.vlapic = vlapic;
vlapic_init(vlapic); vlapic_init(vlapic);
return 0; return 0;
} }
@ -2075,15 +1987,8 @@ void vlapic_free(struct vcpu *vcpu)
if (vlapic == NULL) { if (vlapic == NULL) {
return; return;
} }
del_timer(&vlapic->vtimer.timer); 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); free(vlapic);
} }

View File

@ -953,6 +953,7 @@ static void init_exec_ctrl(struct vcpu *vcpu)
value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS, value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS,
VMX_PROCBASED_CTLS_TSC_OFF | VMX_PROCBASED_CTLS_TSC_OFF |
/* VMX_PROCBASED_CTLS_RDTSC | */ /* VMX_PROCBASED_CTLS_RDTSC | */
VMX_PROCBASED_CTLS_TPR_SHADOW|
VMX_PROCBASED_CTLS_IO_BITMAP | VMX_PROCBASED_CTLS_IO_BITMAP |
VMX_PROCBASED_CTLS_MSR_BITMAP | VMX_PROCBASED_CTLS_MSR_BITMAP |
VMX_PROCBASED_CTLS_SECONDARY); VMX_PROCBASED_CTLS_SECONDARY);
@ -966,15 +967,6 @@ static void init_exec_ctrl(struct vcpu *vcpu)
*/ */
value32 &= ~VMX_PROCBASED_CTLS_INVLPG; 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); exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS, value32);
pr_dbg("VMX_PROC_VM_EXEC_CONTROLS: 0x%x ", 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) * guest (optional)
*/ */
value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS2, value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS2,
VMX_PROCBASED_CTLS2_VAPIC |
VMX_PROCBASED_CTLS2_EPT | VMX_PROCBASED_CTLS2_EPT |
VMX_PROCBASED_CTLS2_RDTSCP | VMX_PROCBASED_CTLS2_RDTSCP |
VMX_PROCBASED_CTLS2_UNRESTRICT); VMX_PROCBASED_CTLS2_UNRESTRICT|
VMX_PROCBASED_CTLS2_VAPIC_REGS);
if (vcpu->arch_vcpu.vpid != 0U) { if (vcpu->arch_vcpu.vpid != 0U) {
value32 |= VMX_PROCBASED_CTLS2_VPID; value32 |= VMX_PROCBASED_CTLS2_VPID;
@ -993,17 +987,9 @@ static void init_exec_ctrl(struct vcpu *vcpu)
value32 &= ~VMX_PROCBASED_CTLS2_VPID; 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()) { if (is_apicv_intr_delivery_supported()) {
value32 |= VMX_PROCBASED_CTLS2_VIRQ; value32 |= VMX_PROCBASED_CTLS2_VIRQ;
} } else {
else {
/* /*
* This field exists only on processors that support * This field exists only on processors that support
* the 1-setting of the "use TPR shadow" * the 1-setting of the "use TPR shadow"
@ -1014,7 +1000,6 @@ static void init_exec_ctrl(struct vcpu *vcpu)
*/ */
exec_vmwrite32(VMX_TPR_THRESHOLD, 0U); exec_vmwrite32(VMX_TPR_THRESHOLD, 0U);
} }
}
if (cpu_has_cap(X86_FEATURE_OSXSAVE)) { if (cpu_has_cap(X86_FEATURE_OSXSAVE)) {
exec_vmwrite64(VMX_XSS_EXITING_BITMAP_FULL, 0UL); exec_vmwrite64(VMX_XSS_EXITING_BITMAP_FULL, 0UL);
@ -1024,17 +1009,13 @@ static void init_exec_ctrl(struct vcpu *vcpu)
exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS2, value32); exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS2, value32);
pr_dbg("VMX_PROC_VM_EXEC_CONTROLS2: 0x%x ", value32); pr_dbg("VMX_PROC_VM_EXEC_CONTROLS2: 0x%x ", value32);
if (is_apicv_supported()) {
/*APIC-v, config APIC-access address*/ /*APIC-v, config APIC-access address*/
value64 = vlapic_apicv_get_apic_access_addr(vcpu->vm); value64 = vlapic_apicv_get_apic_access_addr(vcpu->vm);
exec_vmwrite64(VMX_APIC_ACCESS_ADDR_FULL, exec_vmwrite64(VMX_APIC_ACCESS_ADDR_FULL, value64);
value64);
/*APIC-v, config APIC virtualized page address*/ /*APIC-v, config APIC virtualized page address*/
value64 = vlapic_apicv_get_apic_page_addr( value64 = vlapic_apicv_get_apic_page_addr(vcpu->arch_vcpu.vlapic);
vcpu->arch_vcpu.vlapic); exec_vmwrite64(VMX_VIRTUAL_APIC_PAGE_ADDR_FULL, value64);
exec_vmwrite64(VMX_VIRTUAL_APIC_PAGE_ADDR_FULL,
value64);
if (is_apicv_intr_delivery_supported()) { if (is_apicv_intr_delivery_supported()) {
/* Disable all EOI VMEXIT by default and /* Disable all EOI VMEXIT by default and
@ -1047,7 +1028,6 @@ static void init_exec_ctrl(struct vcpu *vcpu)
exec_vmwrite16(VMX_GUEST_INTR_STATUS, 0); exec_vmwrite16(VMX_GUEST_INTR_STATUS, 0);
} }
}
/* Load EPTP execution control /* Load EPTP execution control
* TODO: introduce API to make this data driven based * TODO: introduce API to make this data driven based

View File

@ -323,9 +323,7 @@ extern struct cpuinfo_x86 boot_cpu_data;
void cpu_do_idle(__unused uint16_t pcpu_id); void cpu_do_idle(__unused uint16_t pcpu_id);
void cpu_dead(uint16_t pcpu_id); void cpu_dead(uint16_t pcpu_id);
void trampoline_start16(void); void trampoline_start16(void);
bool is_apicv_supported(void);
bool is_apicv_intr_delivery_supported(void); bool is_apicv_intr_delivery_supported(void);
bool is_apicv_virt_reg_supported(void);
bool is_ept_supported(void); bool is_ept_supported(void);
bool cpu_has_cap(uint32_t bit); bool cpu_has_cap(uint32_t bit);
void load_cpu_state_data(void); void load_cpu_state_data(void);

View File

@ -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_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval);
int vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t wval); 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 * Signals to the LAPIC that an interrupt at 'vector' needs to be generated
* to the 'cpu', the state is recorded in IRR. * 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, void vlapic_set_tmr_one_vec(struct acrn_vlapic *vlapic, uint32_t delmode,
uint32_t vector, bool level); uint32_t vector, bool level);
void void vlapic_apicv_batch_set_tmr(struct acrn_vlapic *vlapic);
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);
uint32_t vlapic_get_id(struct acrn_vlapic *vlapic); uint32_t vlapic_get_id(struct acrn_vlapic *vlapic);
uint8_t vlapic_get_apicid(struct acrn_vlapic *vlapic); uint8_t vlapic_get_apicid(struct acrn_vlapic *vlapic);
int vlapic_create(struct vcpu *vcpu); int vlapic_create(struct vcpu *vcpu);
void vlapic_free(struct vcpu *vcpu); void vlapic_free(struct vcpu *vcpu);
void vlapic_init(struct acrn_vlapic *vlapic); 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 apic_write_vmexit_handler(struct vcpu *vcpu);
int veoi_vmexit_handler(struct vcpu *vcpu); int veoi_vmexit_handler(struct vcpu *vcpu);
int tpr_below_threshold_vmexit_handler(__unused 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); void calcvdest(struct vm *vm, uint64_t *dmask, uint32_t dest, bool phys);
#endif /* _VLAPIC_H_ */ #endif /* _VLAPIC_H_ */