mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-07-16 08:26:41 +00:00
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:
parent
f95d07dc43
commit
7bc1a3f925
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user