hv: nested: flush L2 VPID only when it could conflict with L1 VPIDs
By changing the way to assign L1 VPID from bottom-up to top-down, the possibilities for VPID conflicts between L1 and L2 guests are small. Then we can flush VPID just in case of conflicting. Tracked-On: #6289 Signed-off-by: Anthony Xu <anthony.xu@intel.com> Signed-off-by: Zide Chen <zide.chen@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
730a275ecc
commit
e9eb72d319
|
@ -238,7 +238,7 @@ int32_t read_vmx_msr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_SHADOW_VMCS_FIELDS 114U
|
#define MAX_SHADOW_VMCS_FIELDS 113U
|
||||||
/*
|
/*
|
||||||
* VMCS fields included in the dual-purpose VMCS: as shadow for L1 and
|
* VMCS fields included in the dual-purpose VMCS: as shadow for L1 and
|
||||||
* as hardware VMCS for nested guest (L2).
|
* as hardware VMCS for nested guest (L2).
|
||||||
|
@ -254,8 +254,6 @@ int32_t read_vmx_msr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val)
|
||||||
*/
|
*/
|
||||||
static const uint32_t vmcs_shadowing_fields[MAX_SHADOW_VMCS_FIELDS] = {
|
static const uint32_t vmcs_shadowing_fields[MAX_SHADOW_VMCS_FIELDS] = {
|
||||||
/* 16-bits */
|
/* 16-bits */
|
||||||
VMX_VPID,
|
|
||||||
|
|
||||||
VMX_GUEST_ES_SEL,
|
VMX_GUEST_ES_SEL,
|
||||||
VMX_GUEST_CS_SEL,
|
VMX_GUEST_CS_SEL,
|
||||||
VMX_GUEST_SS_SEL,
|
VMX_GUEST_SS_SEL,
|
||||||
|
@ -875,6 +873,7 @@ int32_t vmwrite_vmexit_handler(struct acrn_vcpu *vcpu)
|
||||||
|
|
||||||
if ((vmcs_field == VMX_MSR_BITMAP_FULL)
|
if ((vmcs_field == VMX_MSR_BITMAP_FULL)
|
||||||
|| (vmcs_field == VMX_EPT_POINTER_FULL)
|
|| (vmcs_field == VMX_EPT_POINTER_FULL)
|
||||||
|
|| (vmcs_field == VMX_VPID)
|
||||||
|| (vmcs_field == VMX_ENTRY_CONTROLS)
|
|| (vmcs_field == VMX_ENTRY_CONTROLS)
|
||||||
|| (vmcs_field == VMX_EXIT_CONTROLS)) {
|
|| (vmcs_field == VMX_EXIT_CONTROLS)) {
|
||||||
vcpu->arch.nested.control_fields_dirty = true;
|
vcpu->arch.nested.control_fields_dirty = true;
|
||||||
|
@ -945,6 +944,8 @@ static void merge_and_sync_control_fields(struct acrn_vcpu *vcpu)
|
||||||
/* Host is alway runing in 64-bit mode */
|
/* Host is alway runing in 64-bit mode */
|
||||||
value64 = vmcs12->vm_exit_controls | VMX_EXIT_CTLS_HOST_ADDR64;
|
value64 = vmcs12->vm_exit_controls | VMX_EXIT_CTLS_HOST_ADDR64;
|
||||||
exec_vmwrite(VMX_EXIT_CONTROLS, value64);
|
exec_vmwrite(VMX_EXIT_CONTROLS, value64);
|
||||||
|
|
||||||
|
exec_vmwrite(VMX_VPID, vmcs12->vpid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1323,6 +1324,17 @@ static void set_vmcs01_guest_state(struct acrn_vcpu *vcpu)
|
||||||
vcpu_set_rflags(vcpu, 0x2U);
|
vcpu_set_rflags(vcpu, 0x2U);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @pre vcpu != NULL
|
||||||
|
*/
|
||||||
|
static void sanitize_l2_vpid(struct acrn_vmcs12 *vmcs12)
|
||||||
|
{
|
||||||
|
/* Flush VPID if the L2 VPID could be conflicted with any L1 VPIDs */
|
||||||
|
if (vmcs12->vpid >= ALLOCATED_MIN_L1_VPID) {
|
||||||
|
flush_vpid_single(vmcs12->vpid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief handler for all VMEXITs from nested guests
|
* @brief handler for all VMEXITs from nested guests
|
||||||
*
|
*
|
||||||
|
@ -1331,7 +1343,6 @@ static void set_vmcs01_guest_state(struct acrn_vcpu *vcpu)
|
||||||
*/
|
*/
|
||||||
int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu)
|
int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct acrn_vmcs12 *vmcs12 = (struct acrn_vmcs12 *)&vcpu->arch.nested.vmcs12;
|
|
||||||
bool is_l1_vmexit = true;
|
bool is_l1_vmexit = true;
|
||||||
|
|
||||||
if ((vcpu->arch.exit_reason & 0xFFFFU) == VMX_EXIT_REASON_EPT_VIOLATION) {
|
if ((vcpu->arch.exit_reason & 0xFFFFU) == VMX_EXIT_REASON_EPT_VIOLATION) {
|
||||||
|
@ -1339,13 +1350,7 @@ int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_l1_vmexit) {
|
if (is_l1_vmexit) {
|
||||||
/*
|
sanitize_l2_vpid(&vcpu->arch.nested.vmcs12);
|
||||||
* Currerntly VMX_VPID is shadowing to L1, so we flush L2 VPID before
|
|
||||||
* L1 VM entry to avoid conflicting with L1 VPID.
|
|
||||||
*
|
|
||||||
* TODO: emulate VPID for L2 so that we can save this VPID flush
|
|
||||||
*/
|
|
||||||
flush_vpid_single(vmcs12->vpid);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear VMCS02 because: ISDM: Before modifying the shadow-VMCS indicator,
|
* Clear VMCS02 because: ISDM: Before modifying the shadow-VMCS indicator,
|
||||||
|
@ -1411,15 +1416,11 @@ static void nested_vmentry(struct acrn_vcpu *vcpu, bool is_launch)
|
||||||
vmcs12->launch_state = VMCS12_LAUNCH_STATE_LAUNCHED;
|
vmcs12->launch_state = VMCS12_LAUNCH_STATE_LAUNCHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sanitize_l2_vpid(&vcpu->arch.nested.vmcs12);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two reasons to set vcpu->launched to false even for VMRESUME:
|
* set vcpu->launched to false because the launch state of VMCS02 is
|
||||||
*
|
* clear at this moment, even for VMRESUME
|
||||||
* - the launch state of VMCS02 is clear at this moment.
|
|
||||||
* - currently VMX_VPID is shadowing to L1, and it could happens that
|
|
||||||
* L2 VPID will be conflicted with L1 VPID. We rely on run_vcpu() to
|
|
||||||
* flush global vpid in the VMLAUNCH path to resolve this conflict.
|
|
||||||
*
|
|
||||||
* TODO: emulate L2 VPID to avoid VPID flush.
|
|
||||||
*/
|
*/
|
||||||
vcpu->launched = false;
|
vcpu->launched = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -545,7 +545,7 @@ int32_t create_vcpu(uint16_t pcpu_id, struct acrn_vm *vm, struct acrn_vcpu **rtn
|
||||||
*
|
*
|
||||||
* This assignment guarantees a unique non-zero per vcpu vpid at runtime.
|
* This assignment guarantees a unique non-zero per vcpu vpid at runtime.
|
||||||
*/
|
*/
|
||||||
vcpu->arch.vpid = 1U + (vm->vm_id * MAX_VCPUS_PER_VM) + vcpu->vcpu_id;
|
vcpu->arch.vpid = ALLOCATED_MIN_L1_VPID + (vm->vm_id * MAX_VCPUS_PER_VM) + vcpu->vcpu_id;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ACRN uses the following approach to manage VT-d PI notification vectors:
|
* ACRN uses the following approach to manage VT-d PI notification vectors:
|
||||||
|
@ -667,7 +667,9 @@ int32_t run_vcpu(struct acrn_vcpu *vcpu)
|
||||||
* A power-up or a reset invalidates all linear mappings,
|
* A power-up or a reset invalidates all linear mappings,
|
||||||
* guest-physical mappings, and combined mappings
|
* guest-physical mappings, and combined mappings
|
||||||
*/
|
*/
|
||||||
flush_vpid_global();
|
if (!is_vcpu_in_l2_guest(vcpu)) {
|
||||||
|
flush_vpid_global();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HYPERV_ENABLED
|
#ifdef CONFIG_HYPERV_ENABLED
|
||||||
if (is_vcpu_bsp(vcpu)) {
|
if (is_vcpu_bsp(vcpu)) {
|
||||||
|
|
|
@ -238,6 +238,7 @@ struct acrn_vcpu_arch {
|
||||||
/* common MSRs, world_msrs[] is a subset of it */
|
/* common MSRs, world_msrs[] is a subset of it */
|
||||||
uint64_t guest_msrs[NUM_GUEST_MSRS];
|
uint64_t guest_msrs[NUM_GUEST_MSRS];
|
||||||
|
|
||||||
|
#define ALLOCATED_MIN_L1_VPID (0x10000U - CONFIG_MAX_VM_NUM * MAX_VCPUS_PER_VM)
|
||||||
uint16_t vpid;
|
uint16_t vpid;
|
||||||
|
|
||||||
/* Holds the information needed for IRQ/exception handling. */
|
/* Holds the information needed for IRQ/exception handling. */
|
||||||
|
|
Loading…
Reference in New Issue