vmcs.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,066 行 · 第 1/3 页

C
1,066
字号
/* * vmcs.c: VMCS management * Copyright (c) 2004, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */#include <xen/config.h>#include <xen/init.h>#include <xen/mm.h>#include <xen/lib.h>#include <xen/errno.h>#include <xen/domain_page.h>#include <asm/current.h>#include <asm/cpufeature.h>#include <asm/processor.h>#include <asm/msr.h>#include <asm/hvm/hvm.h>#include <asm/hvm/io.h>#include <asm/hvm/support.h>#include <asm/hvm/vmx/vmx.h>#include <asm/hvm/vmx/vmcs.h>#include <asm/flushtlb.h>#include <xen/event.h>#include <xen/kernel.h>#include <xen/keyhandler.h>#include <asm/shadow.h>#include <asm/tboot.h>static int opt_vpid_enabled = 1;boolean_param("vpid", opt_vpid_enabled);/* Dynamic (run-time adjusted) execution control flags. */u32 vmx_pin_based_exec_control __read_mostly;u32 vmx_cpu_based_exec_control __read_mostly;u32 vmx_secondary_exec_control __read_mostly;u32 vmx_vmexit_control __read_mostly;u32 vmx_vmentry_control __read_mostly;bool_t cpu_has_vmx_ins_outs_instr_info __read_mostly;static DEFINE_PER_CPU(struct vmcs_struct *, host_vmcs);static DEFINE_PER_CPU(struct vmcs_struct *, current_vmcs);static DEFINE_PER_CPU(struct list_head, active_vmcs_list);static u32 vmcs_revision_id __read_mostly;static u32 adjust_vmx_controls(u32 ctl_min, u32 ctl_opt, u32 msr){    u32 vmx_msr_low, vmx_msr_high, ctl = ctl_min | ctl_opt;    rdmsr(msr, vmx_msr_low, vmx_msr_high);    ctl &= vmx_msr_high; /* bit == 0 in high word ==> must be zero */    ctl |= vmx_msr_low;  /* bit == 1 in low word  ==> must be one  */    /* Ensure minimum (required) set of control bits are supported. */    BUG_ON(ctl_min & ~ctl);    return ctl;}static void vmx_init_vmcs_config(void){    u32 vmx_basic_msr_low, vmx_basic_msr_high, min, opt;    u32 _vmx_pin_based_exec_control;    u32 _vmx_cpu_based_exec_control;    u32 _vmx_secondary_exec_control = 0;    u32 _vmx_vmexit_control;    u32 _vmx_vmentry_control;    rdmsr(MSR_IA32_VMX_BASIC, vmx_basic_msr_low, vmx_basic_msr_high);    min = (PIN_BASED_EXT_INTR_MASK |           PIN_BASED_NMI_EXITING);    opt = PIN_BASED_VIRTUAL_NMIS;    _vmx_pin_based_exec_control = adjust_vmx_controls(        min, opt, MSR_IA32_VMX_PINBASED_CTLS);    min = (CPU_BASED_HLT_EXITING |           CPU_BASED_INVLPG_EXITING |           CPU_BASED_CR3_LOAD_EXITING |           CPU_BASED_CR3_STORE_EXITING |           CPU_BASED_MONITOR_EXITING |           CPU_BASED_MWAIT_EXITING |           CPU_BASED_MOV_DR_EXITING |           CPU_BASED_ACTIVATE_IO_BITMAP |           CPU_BASED_USE_TSC_OFFSETING |           (opt_softtsc ? CPU_BASED_RDTSC_EXITING : 0));    opt = (CPU_BASED_ACTIVATE_MSR_BITMAP |           CPU_BASED_TPR_SHADOW |           CPU_BASED_ACTIVATE_SECONDARY_CONTROLS);    _vmx_cpu_based_exec_control = adjust_vmx_controls(        min, opt, MSR_IA32_VMX_PROCBASED_CTLS);#ifdef __x86_64__    if ( !(_vmx_cpu_based_exec_control & CPU_BASED_TPR_SHADOW) )    {        min |= CPU_BASED_CR8_LOAD_EXITING | CPU_BASED_CR8_STORE_EXITING;        _vmx_cpu_based_exec_control = adjust_vmx_controls(            min, opt, MSR_IA32_VMX_PROCBASED_CTLS);    }#endif    if ( _vmx_cpu_based_exec_control & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS )    {        min = 0;        opt = (SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES |               SECONDARY_EXEC_WBINVD_EXITING |               SECONDARY_EXEC_ENABLE_EPT);        if ( opt_vpid_enabled )            opt |= SECONDARY_EXEC_ENABLE_VPID;        _vmx_secondary_exec_control = adjust_vmx_controls(            min, opt, MSR_IA32_VMX_PROCBASED_CTLS2);    }    if ( _vmx_secondary_exec_control & SECONDARY_EXEC_ENABLE_EPT )    {        /*         * To use EPT we expect to be able to clear certain intercepts.         * We check VMX_BASIC_MSR[55] to correctly handle default1 controls.         */        uint32_t must_be_one, must_be_zero, msr = MSR_IA32_VMX_PROCBASED_CTLS;        if ( vmx_basic_msr_high & (1u << 23) )            msr = MSR_IA32_VMX_TRUE_PROCBASED_CTLS;        rdmsr(msr, must_be_one, must_be_zero);        if ( must_be_one & (CPU_BASED_INVLPG_EXITING |                            CPU_BASED_CR3_LOAD_EXITING |                            CPU_BASED_CR3_STORE_EXITING) )            _vmx_secondary_exec_control &= ~SECONDARY_EXEC_ENABLE_EPT;    }#if defined(__i386__)    /* If we can't virtualise APIC accesses, the TPR shadow is pointless. */    if ( !(_vmx_secondary_exec_control &           SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES) )        _vmx_cpu_based_exec_control &= ~CPU_BASED_TPR_SHADOW;#endif    min = VM_EXIT_ACK_INTR_ON_EXIT;    opt = 0;#ifdef __x86_64__    min |= VM_EXIT_IA32E_MODE;#endif    _vmx_vmexit_control = adjust_vmx_controls(        min, opt, MSR_IA32_VMX_EXIT_CTLS);    min = opt = 0;    _vmx_vmentry_control = adjust_vmx_controls(        min, opt, MSR_IA32_VMX_ENTRY_CTLS);    if ( !vmx_pin_based_exec_control )    {        /* First time through. */        vmcs_revision_id = vmx_basic_msr_low;        vmx_pin_based_exec_control = _vmx_pin_based_exec_control;        vmx_cpu_based_exec_control = _vmx_cpu_based_exec_control;        vmx_secondary_exec_control = _vmx_secondary_exec_control;        vmx_vmexit_control         = _vmx_vmexit_control;        vmx_vmentry_control        = _vmx_vmentry_control;        cpu_has_vmx_ins_outs_instr_info = !!(vmx_basic_msr_high & (1U<<22));    }    else    {        /* Globals are already initialised: re-check them. */        BUG_ON(vmcs_revision_id != vmx_basic_msr_low);        BUG_ON(vmx_pin_based_exec_control != _vmx_pin_based_exec_control);        BUG_ON(vmx_cpu_based_exec_control != _vmx_cpu_based_exec_control);        BUG_ON(vmx_secondary_exec_control != _vmx_secondary_exec_control);        BUG_ON(vmx_vmexit_control != _vmx_vmexit_control);        BUG_ON(vmx_vmentry_control != _vmx_vmentry_control);        BUG_ON(cpu_has_vmx_ins_outs_instr_info !=               !!(vmx_basic_msr_high & (1U<<22)));    }    /* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */    BUG_ON((vmx_basic_msr_high & 0x1fff) > PAGE_SIZE);#ifdef __x86_64__    /* IA-32 SDM Vol 3B: 64-bit CPUs always have VMX_BASIC_MSR[48]==0. */    BUG_ON(vmx_basic_msr_high & (1u<<16));#endif    /* Require Write-Back (WB) memory type for VMCS accesses. */    BUG_ON(((vmx_basic_msr_high >> 18) & 15) != 6);}static struct vmcs_struct *vmx_alloc_vmcs(void){    struct vmcs_struct *vmcs;    if ( (vmcs = alloc_xenheap_page()) == NULL )    {        gdprintk(XENLOG_WARNING, "Failed to allocate VMCS.\n");        return NULL;    }    clear_page(vmcs);    vmcs->vmcs_revision_id = vmcs_revision_id;    return vmcs;}static void vmx_free_vmcs(struct vmcs_struct *vmcs){    free_xenheap_page(vmcs);}static void __vmx_clear_vmcs(void *info){    struct vcpu *v = info;    struct arch_vmx_struct *arch_vmx = &v->arch.hvm_vmx;    /* Otherwise we can nest (vmx_cpu_down() vs. vmx_clear_vmcs()). */    ASSERT(!local_irq_is_enabled());    if ( arch_vmx->active_cpu == smp_processor_id() )    {        __vmpclear(virt_to_maddr(arch_vmx->vmcs));        arch_vmx->active_cpu = -1;        arch_vmx->launched   = 0;        list_del(&arch_vmx->active_list);        if ( arch_vmx->vmcs == this_cpu(current_vmcs) )            this_cpu(current_vmcs) = NULL;    }}static void vmx_clear_vmcs(struct vcpu *v){    int cpu = v->arch.hvm_vmx.active_cpu;    if ( cpu != -1 )        on_selected_cpus(cpumask_of_cpu(cpu), __vmx_clear_vmcs, v, 1, 1);}static void vmx_load_vmcs(struct vcpu *v){    unsigned long flags;    local_irq_save(flags);    if ( v->arch.hvm_vmx.active_cpu == -1 )    {        list_add(&v->arch.hvm_vmx.active_list, &this_cpu(active_vmcs_list));        v->arch.hvm_vmx.active_cpu = smp_processor_id();    }    ASSERT(v->arch.hvm_vmx.active_cpu == smp_processor_id());    __vmptrld(virt_to_maddr(v->arch.hvm_vmx.vmcs));    this_cpu(current_vmcs) = v->arch.hvm_vmx.vmcs;    local_irq_restore(flags);}int vmx_cpu_up(void){    u32 eax, edx;    int bios_locked, cpu = smp_processor_id();    u64 cr0, vmx_cr0_fixed0, vmx_cr0_fixed1;    BUG_ON(!(read_cr4() & X86_CR4_VMXE));    /*      * Ensure the current processor operating mode meets      * the requred CRO fixed bits in VMX operation.      */    cr0 = read_cr0();    rdmsrl(MSR_IA32_VMX_CR0_FIXED0, vmx_cr0_fixed0);    rdmsrl(MSR_IA32_VMX_CR0_FIXED1, vmx_cr0_fixed1);    if ( (~cr0 & vmx_cr0_fixed0) || (cr0 & ~vmx_cr0_fixed1) )    {        printk("CPU%d: some settings of host CR0 are "                "not allowed in VMX operation.\n", cpu);        return 0;    }    rdmsr(IA32_FEATURE_CONTROL_MSR, eax, edx);    bios_locked = !!(eax & IA32_FEATURE_CONTROL_MSR_LOCK);    if ( bios_locked )    {        if ( !(eax & (IA32_FEATURE_CONTROL_MSR_ENABLE_VMXON_OUTSIDE_SMX |                      IA32_FEATURE_CONTROL_MSR_ENABLE_VMXON_INSIDE_SMX)) )        {            printk("CPU%d: VMX disabled by BIOS.\n", cpu);            return 0;        }    }    else    {        eax  = IA32_FEATURE_CONTROL_MSR_LOCK;        eax |= IA32_FEATURE_CONTROL_MSR_ENABLE_VMXON_OUTSIDE_SMX;        if ( test_bit(X86_FEATURE_SMXE, &boot_cpu_data.x86_capability) )            eax |= IA32_FEATURE_CONTROL_MSR_ENABLE_VMXON_INSIDE_SMX;        wrmsr(IA32_FEATURE_CONTROL_MSR, eax, 0);    }    vmx_init_vmcs_config();    INIT_LIST_HEAD(&this_cpu(active_vmcs_list));    if ( this_cpu(host_vmcs) == NULL )    {        this_cpu(host_vmcs) = vmx_alloc_vmcs();        if ( this_cpu(host_vmcs) == NULL )        {            printk("CPU%d: Could not allocate host VMCS\n", cpu);            return 0;        }    }    switch ( __vmxon(virt_to_maddr(this_cpu(host_vmcs))) )    {    case -2: /* #UD or #GP */        if ( bios_locked &&             test_bit(X86_FEATURE_SMXE, &boot_cpu_data.x86_capability) &&             (!(eax & IA32_FEATURE_CONTROL_MSR_ENABLE_VMXON_OUTSIDE_SMX) ||              !(eax & IA32_FEATURE_CONTROL_MSR_ENABLE_VMXON_INSIDE_SMX)) )        {            printk("CPU%d: VMXON failed: perhaps because of TXT settings "                   "in your BIOS configuration?\n", cpu);            printk(" --> Disable TXT in your BIOS unless using a secure "                   "bootloader.\n");            return 0;        }        /* fall through */    case -1: /* CF==1 or ZF==1 */        printk("CPU%d: unexpected VMXON failure\n", cpu);        return 0;    case 0: /* success */        break;    default:        BUG();    }    ept_sync_all();    vpid_sync_all();    return 1;}void vmx_cpu_down(void)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?