mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-04 22:47:00 +00:00
Implement the VMLAUNCH and VMRESUME instructions, allowing a L1 hypervisor to run nested guests. - merge VMCS control fields and VMCS guest fields to VMCS02 - clear shadow VMCS indicator on VMCS02 and load VMCS02 as current - set VMCS12 launch state to "launched" in VMLAUNCH handler Tracked-On: #5923 Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com> Signed-off-by: Zide Chen <zide.chen@intel.com> Signed-off-by: Alex Merritt <alex.merritt@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
367 lines
10 KiB
C
367 lines
10 KiB
C
/*
|
|
* Copyright (C) 2021 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#ifndef NESTED_H
|
|
#define NESTED_H
|
|
|
|
#include <lib/errno.h>
|
|
|
|
/* helper data structure to make VMX capability MSR manipulation easier */
|
|
union value_64 {
|
|
uint64_t full;
|
|
struct {
|
|
uint32_t lo_32;
|
|
uint32_t hi_32;
|
|
} u;
|
|
};
|
|
|
|
/*
|
|
* Following MSRs are supported if nested virtualization is enabled
|
|
* - If CONFIG_NVMX_ENABLED is set, these MSRs are included in emulated_guest_msrs[]
|
|
* - otherwise, they are included in unsupported_msrs[]
|
|
*/
|
|
#define NUM_VMX_MSRS 20U
|
|
#define LIST_OF_VMX_MSRS \
|
|
MSR_IA32_SMBASE, \
|
|
MSR_IA32_VMX_BASIC, \
|
|
MSR_IA32_VMX_PINBASED_CTLS, \
|
|
MSR_IA32_VMX_PROCBASED_CTLS, \
|
|
MSR_IA32_VMX_EXIT_CTLS, \
|
|
MSR_IA32_VMX_ENTRY_CTLS, \
|
|
MSR_IA32_VMX_MISC, \
|
|
MSR_IA32_VMX_CR0_FIXED0, \
|
|
MSR_IA32_VMX_CR0_FIXED1, \
|
|
MSR_IA32_VMX_CR4_FIXED0, \
|
|
MSR_IA32_VMX_CR4_FIXED1, \
|
|
MSR_IA32_VMX_VMCS_ENUM, \
|
|
MSR_IA32_VMX_PROCBASED_CTLS2, \
|
|
MSR_IA32_VMX_EPT_VPID_CAP, \
|
|
MSR_IA32_VMX_TRUE_PINBASED_CTLS, \
|
|
MSR_IA32_VMX_TRUE_PROCBASED_CTLS, \
|
|
MSR_IA32_VMX_TRUE_EXIT_CTLS, \
|
|
MSR_IA32_VMX_TRUE_ENTRY_CTLS, \
|
|
MSR_IA32_VMX_VMFUNC, \
|
|
MSR_IA32_VMX_PROCBASED_CTLS3
|
|
|
|
/* refer to ISDM APPENDIX B: FIELD ENCODING IN VMCS */
|
|
#define VMX_VMCS_FIELD_ACCESS_HIGH(v) (((v) >> 0U) & 0x1U)
|
|
#define VMX_VMCS_FIELD_INDEX(v) (((v) >> 1U) & 0x1ffU)
|
|
#define VMX_VMCS_FIELD_TYPE(v) (((v) >> 10U) & 0x3U)
|
|
#define VMX_VMCS_FIELD_TYPE_CTL (0U)
|
|
#define VMX_VMCS_FIELD_TYPE_VMEXIT (1U)
|
|
#define VMX_VMCS_FIELD_TYPE_GUEST (2U)
|
|
#define VMX_VMCS_FIELD_TYPE_HOST (3U)
|
|
#define VMX_VMCS_FIELD_WIDTH(v) (((v) >> 13U) & 0x3U)
|
|
#define VMX_VMCS_FIELD_WIDTH_16 (0U)
|
|
#define VMX_VMCS_FIELD_WIDTH_64 (1U)
|
|
#define VMX_VMCS_FIELD_WIDTH_32 (2U)
|
|
#define VMX_VMCS_FIELD_WIDTH_NATURAL (3U)
|
|
|
|
/*
|
|
* VM-Exit Instruction-Information Field
|
|
*
|
|
* ISDM Vol 3C Table 27-9: INVEPT, INVPCID, INVVPID
|
|
* ISDM Vol 3C Table 27-13: VMCLEAR, VMPTRLD, VMPTRST, VMXON, XRSTORS, and XSAVES.
|
|
* ISDM Vol 3C Table 27-14: VMREAD and VMWRITE
|
|
*
|
|
* Either Table 27-9 or Table 27-13 is a subset of Table 27-14, so we are able to
|
|
* define the following macros to be used for the above mentioned instructions.
|
|
*/
|
|
#define VMX_II_SCALING(v) (((v) >> 0U) & 0x3U)
|
|
#define VMX_II_REG1(v) (((v) >> 3U) & 0xfU)
|
|
#define VMX_II_ADDR_SIZE(v) (((v) >> 7U) & 0x7U)
|
|
#define VMX_II_IS_REG(v) (((v) >> 10U) & 0x1U)
|
|
#define VMX_II_SEG_REG(v) (((v) >> 15U) & 0x7U)
|
|
#define VMX_II_IDX_REG(v) (((v) >> 18U) & 0xfU)
|
|
#define VMX_II_IDX_REG_VALID(v) ((((v) >> 22U) & 0x1U) == 0U)
|
|
#define VMX_II_BASE_REG(v) (((v) >> 23U) & 0xfU)
|
|
#define VMX_II_BASE_REG_VALID(v) ((((v) >> 27U) & 0x1U) == 0U)
|
|
#define VMX_II_REG2(v) (((v) >> 28U) & 0xfU)
|
|
|
|
#define VMCS_SHADOW_BIT_INDICATOR (1U << 31U)
|
|
|
|
/* refer to ISDM: Table 30-1. VM-Instruction Error Numbers */
|
|
#define VMXERR_VMCLEAR_VMXON_POINTER (3)
|
|
#define VMXERR_VMLAUNCH_NONCLEAR_VMCS (4)
|
|
#define VMXERR_VMRESUME_NONLAUNCHED_VMCS (5)
|
|
#define VMXERR_VMRESUME_AFTER_VMXOFF (6)
|
|
#define VMXERR_VMPTRLD_INVALID_ADDRESS (9)
|
|
#define VMXERR_VMPTRLD_INCORRECT_VMCS_REVISION_ID (10)
|
|
#define VMXERR_VMPTRLD_VMXON_POINTER (11)
|
|
#define VMXERR_UNSUPPORTED_COMPONENT (12)
|
|
#define VMXERR_VMWRITE_RO_COMPONENT (13)
|
|
#define VMXERR_VMXON_IN_VMX_ROOT_OPERATION (15)
|
|
|
|
/*
|
|
* This VMCS12 revision id is chosen arbitrarily.
|
|
* The emulated MSR_IA32_VMX_BASIC returns this ID in bits 30:0.
|
|
*/
|
|
#define VMCS12_REVISION_ID 0x15407E12U
|
|
|
|
#define VMCS12_LAUNCH_STATE_CLEAR (0U)
|
|
#define VMCS12_LAUNCH_STATE_LAUNCHED (1U)
|
|
|
|
/*
|
|
* struct acrn_vmcs12 describes the emulated VMCS for the nested guest (L2).
|
|
*/
|
|
struct acrn_vmcs12 {
|
|
uint8_t vmcs_hdr[4];
|
|
uint32_t abort;
|
|
|
|
/*
|
|
* Rest of the memory is used for "VMCS Data"
|
|
* Layout of VMCS Data is non-architectural and processor
|
|
* implemetation specific.
|
|
*/
|
|
uint32_t launch_state;
|
|
|
|
/* 16-bit Control Fields */
|
|
uint16_t vpid;
|
|
uint16_t posted_intr_nv;
|
|
uint16_t eptp_index;
|
|
|
|
/* 16-bit Read-only Fields */
|
|
uint16_t padding;
|
|
|
|
/* 16-bit Guest-State Fields */
|
|
uint16_t guest_es;
|
|
uint16_t guest_cs;
|
|
uint16_t guest_ss;
|
|
uint16_t guest_ds;
|
|
uint16_t guest_fs;
|
|
uint16_t guest_gs;
|
|
uint16_t guest_ldtr;
|
|
uint16_t guest_tr;
|
|
uint16_t guest_intr_status;
|
|
uint16_t pml_index;
|
|
|
|
/* 16-bit Host-State Fields */
|
|
uint16_t host_es;
|
|
uint16_t host_cs;
|
|
uint16_t host_ss;
|
|
uint16_t host_ds;
|
|
uint16_t host_fs;
|
|
uint16_t host_gs;
|
|
uint16_t host_tr;
|
|
|
|
/* 64-bit Control Fields */
|
|
uint64_t io_bitmap_a;
|
|
uint64_t io_bitmap_b;
|
|
uint64_t msr_bitmap;
|
|
uint64_t vm_exit_msr_store_addr;
|
|
uint64_t vm_exit_msr_load_addr;
|
|
uint64_t vm_entry_load_addr;
|
|
uint64_t executive_vmcs_ptr;
|
|
uint64_t pml_addr;
|
|
uint64_t tsc_offset;
|
|
uint64_t virtual_apic_addr;
|
|
uint64_t apic_access_addr;
|
|
uint64_t posted_interrupt_desc_addr;
|
|
uint64_t vm_func_controls;
|
|
uint64_t ept_pointer;
|
|
uint64_t eoi_exit_bitmap0;
|
|
uint64_t eoi_exit_bitmap1;
|
|
uint64_t eoi_exit_bitmap2;
|
|
uint64_t eoi_exit_bitmap3;
|
|
uint64_t eptp_list_addr;
|
|
uint64_t vmread_bitmap_addr;
|
|
uint64_t vmwrite_bitmap_addr;
|
|
uint64_t virt_exception_info_addr;
|
|
uint64_t xss_exiting_bitmap;
|
|
uint64_t encls_exiting_bitmap;
|
|
uint64_t sub_page_permission_ptr;
|
|
uint64_t tsc_multiplier;
|
|
|
|
/* 64-bit Read-Only Data Fields */
|
|
uint64_t guest_phys_addr;
|
|
|
|
/* 64-bit Guest-State Fields */
|
|
uint64_t vmcs_link_ptr;
|
|
uint64_t guest_ia32_debugctl;
|
|
uint64_t guest_ia32_pat;
|
|
uint64_t guest_ia32_efer;
|
|
uint64_t ia32_perf_global_ctrl;
|
|
uint64_t guest_pdpte0;
|
|
uint64_t guest_pdpte1;
|
|
uint64_t guest_pdpte2;
|
|
uint64_t guest_pdpte3;
|
|
uint64_t guest_ia32_bndcfgs;
|
|
uint64_t guest_ia32_rtit_ctl;
|
|
|
|
/* 64-bit Host-State Fields */
|
|
uint64_t host_ia32_pat;
|
|
uint64_t host_ia32_efer;
|
|
uint64_t host_ia32_perf_global_ctrl;
|
|
|
|
/* 32-bit Control Fields */
|
|
uint32_t pin_based_exec_ctrl;
|
|
uint32_t proc_based_exec_ctrl;
|
|
uint32_t exception_bitmap;
|
|
uint32_t page_fault_error_code_mask;
|
|
uint32_t page_fault_error_code_match;
|
|
uint32_t cr3_target_count;
|
|
uint32_t vm_exit_controls;
|
|
uint32_t vm_exit_msr_store_count;
|
|
uint32_t vm_exit_msr_load_count;
|
|
uint32_t vm_entry_controls;
|
|
uint32_t vm_entry_msr_load_count;
|
|
uint32_t vm_entry_intr_info_field;
|
|
uint32_t vm_entry_exception_err_code;
|
|
uint32_t vm_entry_instr_len;
|
|
uint32_t tpr_threshold;
|
|
uint32_t proc_based_exec_ctrl2;
|
|
uint32_t ple_gap;
|
|
uint32_t ple_window;
|
|
|
|
/* 32-bit Read-Only Data Fields */
|
|
uint32_t vm_instr_error;
|
|
uint32_t exit_reason;
|
|
uint32_t vm_exit_intr_info;
|
|
uint32_t vm_exit_intr_error_code;
|
|
uint32_t idt_vectoring_info_field;
|
|
uint32_t idt_vectoring_error_code;
|
|
uint32_t vm_exit_instr_len;
|
|
uint32_t vm_exit_instr_info;
|
|
|
|
/* 32-bit Guest-State Fields */
|
|
uint32_t guest_es_limit;
|
|
uint32_t guest_cs_limit;
|
|
uint32_t guest_ss_limit;
|
|
uint32_t guest_ds_limit;
|
|
uint32_t guest_fs_limit;
|
|
uint32_t guest_gs_limit;
|
|
uint32_t guest_ldtr_limit;
|
|
uint32_t guest_tr_limit;
|
|
uint32_t guest_gdtr_limit;
|
|
uint32_t guest_idtr_limit;
|
|
uint32_t guest_es_ar;
|
|
uint32_t guest_cs_ar;
|
|
uint32_t guest_ss_ar;
|
|
uint32_t guest_ds_ar;
|
|
uint32_t guest_fs_ar;
|
|
uint32_t guest_gs_ar;
|
|
uint32_t guest_ldtr_ar;
|
|
uint32_t guest_tr_ar;
|
|
uint32_t guest_intr_state;
|
|
uint32_t guest_activity_state;
|
|
uint32_t guest_smbase;
|
|
uint32_t guest_ia32_sysenter_cs;
|
|
uint32_t vmx_preempt_timer_val;
|
|
|
|
/* 32-bit Host-State Fields */
|
|
uint32_t host_ia32_sysenter_cs;
|
|
|
|
/* Natural-width Control Fields */
|
|
uint64_t cr0_guest_host_mask;
|
|
uint64_t cr4_guest_host_mask;
|
|
uint64_t cr0_read_shadow;
|
|
uint64_t cr4_read_shadow;
|
|
uint64_t cr3_target_val0;
|
|
uint64_t cr3_target_val1;
|
|
uint64_t cr3_target_val2;
|
|
uint64_t cr3_target_val3;
|
|
|
|
/* Natural-width Read-Only Data Fields */
|
|
uint64_t exit_qual;
|
|
uint64_t io_rcx;
|
|
uint64_t io_rsi;
|
|
uint64_t io_rdi;
|
|
uint64_t io_rip;
|
|
uint64_t guest_linear_addr;
|
|
|
|
/* Natural-width Guest-State Fields */
|
|
uint64_t guest_cr0;
|
|
uint64_t guest_cr3;
|
|
uint64_t guest_cr4;
|
|
uint64_t guest_es_base;
|
|
uint64_t guest_cs_base;
|
|
uint64_t guest_ss_base;
|
|
uint64_t guest_ds_base;
|
|
uint64_t guest_fs_base;
|
|
uint64_t guest_gs_base;
|
|
uint64_t guest_ldtr_base;
|
|
uint64_t guest_tr_base;
|
|
uint64_t guest_gdtr_base;
|
|
uint64_t guest_idtr_base;
|
|
uint64_t guest_dr7;
|
|
uint64_t guest_rsp;
|
|
uint64_t guest_rip;
|
|
uint64_t guest_rflags;
|
|
uint64_t guest_pending_debug_excp;
|
|
uint64_t guest_ia32_sysenter_esp;
|
|
uint64_t guest_ia32_sysenter_eip;
|
|
|
|
/** Natural-width Host-State Fields */
|
|
uint64_t host_cr0;
|
|
uint64_t host_cr3;
|
|
uint64_t host_cr4;
|
|
uint64_t host_fs_base;
|
|
uint64_t host_gs_base;
|
|
uint64_t host_tr_base;
|
|
uint64_t host_gdtr_base;
|
|
uint64_t host_idtr_base;
|
|
uint64_t host_ia32_sysenter_esp;
|
|
uint64_t host_ia32_sysenter_eip;
|
|
uint64_t host_rsp;
|
|
uint64_t host_rip;
|
|
};
|
|
|
|
enum VMXResult {
|
|
VMsucceed,
|
|
VMfailValid,
|
|
VMfailInvalid,
|
|
};
|
|
void nested_vmx_result(enum VMXResult, int error_number);
|
|
int32_t vmxon_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmxoff_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmptrld_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmclear_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmread_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmwrite_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmresume_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
int32_t vmlaunch_vmexit_handler(struct acrn_vcpu *vcpu);
|
|
|
|
#ifdef CONFIG_NVMX_ENABLED
|
|
struct acrn_nested {
|
|
uint8_t vmcs02[PAGE_SIZE]; /* VMCS to run L2 and as Link Pointer in VMCS01 */
|
|
|
|
/* TODO: change this to uint8_t vmcs12[PAGE_SIZE] */
|
|
struct acrn_vmcs12 vmcs12; /* To cache L1's VMCS12*/
|
|
uint64_t current_vmcs12_ptr; /* GPA */
|
|
uint64_t vmxon_ptr; /* GPA */
|
|
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 */
|
|
} __aligned(PAGE_SIZE);
|
|
|
|
void init_nested_vmx(__unused struct acrn_vm *vm);
|
|
bool is_vmx_msr(uint32_t msr);
|
|
void init_vmx_msrs(struct acrn_vcpu *vcpu);
|
|
int32_t read_vmx_msr(__unused struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val);
|
|
#else
|
|
struct acrn_nested {};
|
|
|
|
static inline void init_nested_vmx(__unused struct acrn_vm *vm) {}
|
|
static inline bool is_vmx_msr(__unused uint32_t msr)
|
|
{
|
|
/*
|
|
* if nested virtualization is disabled, return false so that
|
|
* it can be treated as unsupported MSR.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
static inline void init_vmx_msrs(__unused struct acrn_vcpu *vcpu) {}
|
|
|
|
static inline int32_t read_vmx_msr(__unused struct acrn_vcpu *vcpu,
|
|
__unused uint32_t msr, __unused uint64_t *val)
|
|
{
|
|
return -EACCES;
|
|
}
|
|
#endif /* CONFIG_NVMX_ENABLED */
|
|
#endif /* NESTED_H */
|