hv: nested: merge gpa_field_dirty and control_field_dirty flag

In run time, it's rare for L1 to write to the intercepted non host-state
VMCS fields, and using multiple dirty flags is not necessary.

This patch uses one single dirty flag to manage all non host-state VMCS
fields.  This helps to simplify current code and in the future we may
not need to declare new dirty flags when we intercept more VMCS fields.

Tracked-On: #5923
Signed-off-by: Zide Chen <zide.chen@intel.com>
This commit is contained in:
Zide Chen 2021-08-27 17:02:48 -07:00 committed by wenlingz
parent 6376d5a0d3
commit 1ab65825ba
2 changed files with 21 additions and 61 deletions

View File

@ -737,8 +737,7 @@ int32_t vmxon_vmexit_handler(struct acrn_vcpu *vcpu)
} else {
vcpu->arch.nested.vmxon = true;
vcpu->arch.nested.host_state_dirty = false;
vcpu->arch.nested.gpa_field_dirty = false;
vcpu->arch.nested.control_field_dirty = false;
vcpu->arch.nested.control_fields_dirty = false;
vcpu->arch.nested.in_l2_guest = false;
vcpu->arch.nested.vmxon_ptr = vmptr_gpa;
vcpu->arch.nested.current_vmcs12_ptr = INVALID_GPA;
@ -784,8 +783,7 @@ int32_t vmxoff_vmexit_handler(struct acrn_vcpu *vcpu)
if (check_vmx_permission(vcpu)) {
vcpu->arch.nested.vmxon = false;
vcpu->arch.nested.host_state_dirty = false;
vcpu->arch.nested.gpa_field_dirty = false;
vcpu->arch.nested.control_field_dirty = false;
vcpu->arch.nested.control_fields_dirty = false;
vcpu->arch.nested.in_l2_guest = false;
vcpu->arch.nested.current_vmcs12_ptr = INVALID_GPA;
(void)memset(vcpu->arch.nested.vmcs02, 0U, PAGE_SIZE);
@ -875,13 +873,11 @@ int32_t vmwrite_vmexit_handler(struct acrn_vcpu *vcpu)
vcpu->arch.nested.host_state_dirty = true;
}
/*
* For simplicity, gpa_field_dirty could be used for all VMCS fields that
* are programmed by L1 hypervisor with a GPA address
*/
if ((vmcs_field == VMX_MSR_BITMAP_FULL) ||
(vmcs_field == VMX_EPT_POINTER_FULL)) {
vcpu->arch.nested.gpa_field_dirty = true;
if ((vmcs_field == VMX_MSR_BITMAP_FULL)
|| (vmcs_field == VMX_EPT_POINTER_FULL)
|| (vmcs_field == VMX_ENTRY_CONTROLS)
|| (vmcs_field == VMX_EXIT_CONTROLS)) {
vcpu->arch.nested.control_fields_dirty = true;
if (vmcs_field == VMX_EPT_POINTER_FULL) {
if (vcpu->arch.nested.vmcs12.ept_pointer != vmcs_value) {
@ -891,11 +887,6 @@ int32_t vmwrite_vmexit_handler(struct acrn_vcpu *vcpu)
}
}
if ((vmcs_field == VMX_ENTRY_CONTROLS)
|| (vmcs_field == VMX_EXIT_CONTROLS)) {
vcpu->arch.nested.control_field_dirty = true;
}
pr_dbg("vmcs_field: %x vmcs_value: %llx", vmcs_field, vmcs_value);
vmcs12_write_field((uint64_t)&vcpu->arch.nested.vmcs12, vmcs_field, vmcs_value);
nested_vmx_result(VMsucceed, 0);
@ -928,11 +919,17 @@ static void sync_vmcs02_to_vmcs12(struct acrn_vcpu *vcpu)
* @pre vcpu != NULL
* @pre VMCS02 (as an ordinary VMCS) is current
*/
static void adjust_vmcs02_control_fields(struct acrn_vcpu *vcpu)
static void merge_and_sync_control_fields(struct acrn_vcpu *vcpu)
{
struct acrn_vmcs12 *vmcs12 = &vcpu->arch.nested.vmcs12;
uint64_t value64;
/* Sync VMCS fields that are not shadowing. Don't need to sync these fields back to VMCS12. */
exec_vmwrite(VMX_MSR_BITMAP_FULL, gpa2hpa(vcpu->vm, vmcs12->msr_bitmap));
exec_vmwrite(VMX_EPT_POINTER_FULL, get_shadow_eptp(vmcs12->ept_pointer));
/* For VM-execution, entry and exit controls */
value64 = vmcs12->vm_entry_controls;
if ((value64 & VMX_ENTRY_CTLS_LOAD_EFER) != VMX_ENTRY_CTLS_LOAD_EFER) {
/*
@ -967,16 +964,7 @@ static void sync_vmcs12_to_vmcs02(struct acrn_vcpu *vcpu)
exec_vmwrite(vmcs_shadowing_fields[idx], val64);
}
/* Sync VMCS fields that are not shadowing. Don't need to sync these fields back to VMCS12. */
val64 = vmcs12_read_field(vmcs12, VMX_MSR_BITMAP_FULL);
exec_vmwrite(VMX_MSR_BITMAP_FULL, gpa2hpa(vcpu->vm, val64));
val64 = vmcs12_read_field(vmcs12, VMX_EPT_POINTER_FULL);
exec_vmwrite(VMX_EPT_POINTER_FULL, get_shadow_eptp(val64));
/* For VM-execution, entry and exit controls */
adjust_vmcs02_control_fields(vcpu);
merge_and_sync_control_fields(vcpu);
}
/*
@ -1198,8 +1186,7 @@ int32_t vmclear_vmexit_handler(struct acrn_vcpu *vcpu)
/* If no L2 VM entry happens between VMWRITE and VMCLEAR, need to clear these flags */
vcpu->arch.nested.host_state_dirty = false;
vcpu->arch.nested.gpa_field_dirty = false;
vcpu->arch.nested.control_field_dirty = false;
vcpu->arch.nested.control_fields_dirty = false;
} else {
/*
* we need to update the VMCS12 launch state in L1 memory in these two cases:
@ -1381,34 +1368,6 @@ int32_t nested_vmexit_handler(struct acrn_vcpu *vcpu)
return 0;
}
/*
* @pre vcpu != NULL
* @pre VMCS02 (as an ordinary VMCS) is current
*/
static void merge_and_sync_control_fields(struct acrn_vcpu *vcpu)
{
struct acrn_vmcs12 *vmcs12 = &vcpu->arch.nested.vmcs12;
/*
* Merge non shadowing fields from VMCS12 to VMCS02 if needed.
* If no L2 VM entry happens between VMWRITE and VMCLEAR,
* these dirty VMCS fields will be merged to VMCS02 in sync_vmcs12_to_vmcs02()
* next time when this VMCS12 is being VMPTRLDed.
*/
if (vcpu->arch.nested.gpa_field_dirty) {
vcpu->arch.nested.gpa_field_dirty = false;
exec_vmwrite(VMX_MSR_BITMAP_FULL, gpa2hpa(vcpu->vm, vmcs12->msr_bitmap));
exec_vmwrite(VMX_EPT_POINTER_FULL, get_shadow_eptp(vmcs12->ept_pointer));
}
/* for VM-execution, VM-exit, VM-entry control fields */
if (vcpu->arch.nested.control_field_dirty) {
vcpu->arch.nested.control_field_dirty = false;
adjust_vmcs02_control_fields(vcpu);
}
}
/*
* @pre vcpu != NULL
* @pre VMCS01 is current and VMCS02 is referenced by VMCS Link Pointer
@ -1440,8 +1399,10 @@ static void nested_vmentry(struct acrn_vcpu *vcpu, bool is_launch)
/* as an ordinary VMCS, VMCS02 is active and currernt when L2 guest is running */
load_va_vmcs(vcpu->arch.nested.vmcs02);
/* Merge L0 settings and L1 settings for VMCS Control fields */
merge_and_sync_control_fields(vcpu);
if (vcpu->arch.nested.control_fields_dirty) {
vcpu->arch.nested.control_fields_dirty = false;
merge_and_sync_control_fields(vcpu);
}
/* vCPU is in guest mode from this point */
vcpu->arch.nested.in_l2_guest = true;

View File

@ -339,8 +339,7 @@ struct acrn_nested {
bool vmxon; /* To indicate if vCPU entered VMX operation */
bool in_l2_guest; /* To indicate if vCPU is currently in Guest mode (from L1's perspective) */
bool host_state_dirty; /* To indicate need to merge VMCS12 host-state fields to VMCS01 */
bool gpa_field_dirty;
bool control_field_dirty; /* for VM-execution, VM-exit, VM-entry control fields */
bool control_fields_dirty; /* For all other non-host-state fields that need to be merged */
} __aligned(PAGE_SIZE);
void init_nested_vmx(__unused struct acrn_vm *vm);