vmx.c

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

C
2,245
字号
/* * vmx.c: handling VMX architecture-related VM exits * 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/lib.h>#include <xen/trace.h>#include <xen/sched.h>#include <xen/irq.h>#include <xen/softirq.h>#include <xen/domain_page.h>#include <xen/hypercall.h>#include <xen/perfc.h>#include <asm/current.h>#include <asm/io.h>#include <asm/regs.h>#include <asm/cpufeature.h>#include <asm/processor.h>#include <asm/types.h>#include <asm/debugreg.h>#include <asm/msr.h>#include <asm/spinlock.h>#include <asm/paging.h>#include <asm/p2m.h>#include <asm/hvm/hvm.h>#include <asm/hvm/support.h>#include <asm/hvm/vmx/vmx.h>#include <asm/hvm/vmx/vmcs.h>#include <public/sched.h>#include <public/hvm/ioreq.h>#include <asm/hvm/vpic.h>#include <asm/hvm/vlapic.h>#include <asm/x86_emulate.h>#include <asm/hvm/vpt.h>#include <public/hvm/save.h>#include <asm/hvm/trace.h>enum handler_return { HNDL_done, HNDL_unhandled, HNDL_exception_raised };static void vmx_ctxt_switch_from(struct vcpu *v);static void vmx_ctxt_switch_to(struct vcpu *v);static int  vmx_alloc_vlapic_mapping(struct domain *d);static void vmx_free_vlapic_mapping(struct domain *d);static int  vmx_alloc_vpid(struct domain *d);static void vmx_free_vpid(struct domain *d);static void vmx_install_vlapic_mapping(struct vcpu *v);static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr);static void vmx_update_guest_efer(struct vcpu *v);static void vmx_cpuid_intercept(    unsigned int *eax, unsigned int *ebx,    unsigned int *ecx, unsigned int *edx);static void vmx_wbinvd_intercept(void);static void vmx_fpu_dirty_intercept(void);static int vmx_msr_read_intercept(struct cpu_user_regs *regs);static int vmx_msr_write_intercept(struct cpu_user_regs *regs);static void vmx_invlpg_intercept(unsigned long vaddr);static int vmx_domain_initialise(struct domain *d){    int rc;    d->arch.hvm_domain.vmx.ept_control.etmt = EPT_DEFAULT_MT;    d->arch.hvm_domain.vmx.ept_control.gaw  = EPT_DEFAULT_GAW;    d->arch.hvm_domain.vmx.ept_control.asr  =        pagetable_get_pfn(d->arch.phys_table);    if ( (rc = vmx_alloc_vpid(d)) != 0 )        return rc;    if ( (rc = vmx_alloc_vlapic_mapping(d)) != 0 )    {        vmx_free_vpid(d);        return rc;    }    return 0;}static void vmx_domain_destroy(struct domain *d){    ept_sync_domain(d);    vmx_free_vlapic_mapping(d);    vmx_free_vpid(d);}static int vmx_vcpu_initialise(struct vcpu *v){    int rc;    spin_lock_init(&v->arch.hvm_vmx.vmcs_lock);    v->arch.schedule_tail    = vmx_do_resume;    v->arch.ctxt_switch_from = vmx_ctxt_switch_from;    v->arch.ctxt_switch_to   = vmx_ctxt_switch_to;    if ( (rc = vmx_create_vmcs(v)) != 0 )    {        dprintk(XENLOG_WARNING,                "Failed to create VMCS for vcpu %d: err=%d.\n",                v->vcpu_id, rc);        return rc;    }    vpmu_initialise(v);    vmx_install_vlapic_mapping(v);    /* %eax == 1 signals full real-mode support to the guest loader. */    if ( v->vcpu_id == 0 )        v->arch.guest_context.user_regs.eax = 1;    return 0;}static void vmx_vcpu_destroy(struct vcpu *v){    vmx_destroy_vmcs(v);    vpmu_destroy(v);}#ifdef __x86_64__static DEFINE_PER_CPU(struct vmx_msr_state, host_msr_state);static u32 msr_index[VMX_MSR_COUNT] ={    MSR_LSTAR, MSR_STAR, MSR_SYSCALL_MASK};static void vmx_save_host_msrs(void){    struct vmx_msr_state *host_msr_state = &this_cpu(host_msr_state);    int i;    for ( i = 0; i < VMX_MSR_COUNT; i++ )        rdmsrl(msr_index[i], host_msr_state->msrs[i]);}#define WRITE_MSR(address)                                              \        guest_msr_state->msrs[VMX_INDEX_MSR_ ## address] = msr_content; \        set_bit(VMX_INDEX_MSR_ ## address, &guest_msr_state->flags);    \        wrmsrl(MSR_ ## address, msr_content);                           \        set_bit(VMX_INDEX_MSR_ ## address, &host_msr_state->flags);     \        breakstatic enum handler_return long_mode_do_msr_read(struct cpu_user_regs *regs){    u64 msr_content = 0;    u32 ecx = regs->ecx;    struct vcpu *v = current;    struct vmx_msr_state *guest_msr_state = &v->arch.hvm_vmx.msr_state;    switch ( ecx )    {    case MSR_EFER:        msr_content = v->arch.hvm_vcpu.guest_efer;        break;    case MSR_FS_BASE:        msr_content = __vmread(GUEST_FS_BASE);        goto check_long_mode;    case MSR_GS_BASE:        msr_content = __vmread(GUEST_GS_BASE);        goto check_long_mode;    case MSR_SHADOW_GS_BASE:        msr_content = v->arch.hvm_vmx.shadow_gs;    check_long_mode:        if ( !(hvm_long_mode_enabled(v)) )        {            vmx_inject_hw_exception(v, TRAP_gp_fault, 0);            return HNDL_exception_raised;        }        break;    case MSR_STAR:        msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_STAR];        break;    case MSR_LSTAR:        msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_LSTAR];        break;    case MSR_CSTAR:        msr_content = v->arch.hvm_vmx.cstar;        break;    case MSR_SYSCALL_MASK:        msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_SYSCALL_MASK];        break;    default:        return HNDL_unhandled;    }    HVM_DBG_LOG(DBG_LEVEL_0, "msr 0x%x content 0x%"PRIx64, ecx, msr_content);    regs->eax = (u32)(msr_content >>  0);    regs->edx = (u32)(msr_content >> 32);    return HNDL_done;}static enum handler_return long_mode_do_msr_write(struct cpu_user_regs *regs){    u64 msr_content = (u32)regs->eax | ((u64)regs->edx << 32);    u32 ecx = regs->ecx;    struct vcpu *v = current;    struct vmx_msr_state *guest_msr_state = &v->arch.hvm_vmx.msr_state;    struct vmx_msr_state *host_msr_state = &this_cpu(host_msr_state);    HVM_DBG_LOG(DBG_LEVEL_0, "msr 0x%x content 0x%"PRIx64, ecx, msr_content);    switch ( ecx )    {    case MSR_EFER:        if ( hvm_set_efer(msr_content) )            goto exception_raised;        break;    case MSR_FS_BASE:    case MSR_GS_BASE:    case MSR_SHADOW_GS_BASE:        if ( !hvm_long_mode_enabled(v) )            goto gp_fault;        if ( !is_canonical_address(msr_content) )            goto uncanonical_address;        if ( ecx == MSR_FS_BASE )            __vmwrite(GUEST_FS_BASE, msr_content);        else if ( ecx == MSR_GS_BASE )            __vmwrite(GUEST_GS_BASE, msr_content);        else        {            v->arch.hvm_vmx.shadow_gs = msr_content;            wrmsrl(MSR_SHADOW_GS_BASE, msr_content);        }        break;    case MSR_STAR:        WRITE_MSR(STAR);    case MSR_LSTAR:        if ( !is_canonical_address(msr_content) )            goto uncanonical_address;        WRITE_MSR(LSTAR);    case MSR_CSTAR:        if ( !is_canonical_address(msr_content) )            goto uncanonical_address;        v->arch.hvm_vmx.cstar = msr_content;        break;    case MSR_SYSCALL_MASK:        WRITE_MSR(SYSCALL_MASK);    default:        return HNDL_unhandled;    }    return HNDL_done; uncanonical_address:    HVM_DBG_LOG(DBG_LEVEL_0, "Not cano address of msr write %x", ecx); gp_fault:    vmx_inject_hw_exception(v, TRAP_gp_fault, 0); exception_raised:    return HNDL_exception_raised;}/* * To avoid MSR save/restore at every VM exit/entry time, we restore * the x86_64 specific MSRs at domain switch time. Since these MSRs * are not modified once set for para domains, we don't save them, * but simply reset them to values set in percpu_traps_init(). */static void vmx_restore_host_msrs(void){    struct vmx_msr_state *host_msr_state = &this_cpu(host_msr_state);    int i;    while ( host_msr_state->flags )    {        i = find_first_set_bit(host_msr_state->flags);        wrmsrl(msr_index[i], host_msr_state->msrs[i]);        clear_bit(i, &host_msr_state->flags);    }    if ( cpu_has_nx && !(read_efer() & EFER_NX) )        write_efer(read_efer() | EFER_NX);}static void vmx_save_guest_msrs(struct vcpu *v){    /* MSR_SHADOW_GS_BASE may have been changed by swapgs instruction. */    rdmsrl(MSR_SHADOW_GS_BASE, v->arch.hvm_vmx.shadow_gs);}static void vmx_restore_guest_msrs(struct vcpu *v){    struct vmx_msr_state *guest_msr_state, *host_msr_state;    unsigned long guest_flags;    int i;    guest_msr_state = &v->arch.hvm_vmx.msr_state;    host_msr_state = &this_cpu(host_msr_state);    wrmsrl(MSR_SHADOW_GS_BASE, v->arch.hvm_vmx.shadow_gs);    guest_flags = guest_msr_state->flags;    while ( guest_flags )    {        i = find_first_set_bit(guest_flags);        HVM_DBG_LOG(DBG_LEVEL_2,                    "restore guest's index %d msr %x with value %lx",                    i, msr_index[i], guest_msr_state->msrs[i]);        set_bit(i, &host_msr_state->flags);        wrmsrl(msr_index[i], guest_msr_state->msrs[i]);        clear_bit(i, &guest_flags);    }    if ( (v->arch.hvm_vcpu.guest_efer ^ read_efer()) & (EFER_NX | EFER_SCE) )    {        HVM_DBG_LOG(DBG_LEVEL_2,                    "restore guest's EFER with value %lx",                    v->arch.hvm_vcpu.guest_efer);        write_efer((read_efer() & ~(EFER_NX | EFER_SCE)) |                   (v->arch.hvm_vcpu.guest_efer & (EFER_NX | EFER_SCE)));    }}#else  /* __i386__ */#define vmx_save_host_msrs()        ((void)0)static void vmx_restore_host_msrs(void){    if ( cpu_has_nx && !(read_efer() & EFER_NX) )        write_efer(read_efer() | EFER_NX);}#define vmx_save_guest_msrs(v)      ((void)0)static void vmx_restore_guest_msrs(struct vcpu *v){    if ( (v->arch.hvm_vcpu.guest_efer ^ read_efer()) & EFER_NX )    {        HVM_DBG_LOG(DBG_LEVEL_2,                    "restore guest's EFER with value %lx",                    v->arch.hvm_vcpu.guest_efer);        write_efer((read_efer() & ~EFER_NX) |                   (v->arch.hvm_vcpu.guest_efer & EFER_NX));    }}static enum handler_return long_mode_do_msr_read(struct cpu_user_regs *regs){    u64 msr_content = 0;    struct vcpu *v = current;    switch ( regs->ecx )    {    case MSR_EFER:        msr_content = v->arch.hvm_vcpu.guest_efer;        break;    default:        return HNDL_unhandled;    }    regs->eax = msr_content >>  0;    regs->edx = msr_content >> 32;    return HNDL_done;}static enum handler_return long_mode_do_msr_write(struct cpu_user_regs *regs){    u64 msr_content = regs->eax | ((u64)regs->edx << 32);    switch ( regs->ecx )    {    case MSR_EFER:        if ( hvm_set_efer(msr_content) )            return HNDL_exception_raised;        break;    default:        return HNDL_unhandled;    }    return HNDL_done;}#endif /* __i386__ */static int vmx_guest_x86_mode(struct vcpu *v){    unsigned int cs_ar_bytes;    if ( unlikely(!(v->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE)) )        return 0;    if ( unlikely(guest_cpu_user_regs()->eflags & X86_EFLAGS_VM) )        return 1;    cs_ar_bytes = __vmread(GUEST_CS_AR_BYTES);    if ( hvm_long_mode_enabled(v) &&         likely(cs_ar_bytes & X86_SEG_AR_CS_LM_ACTIVE) )        return 8;    return (likely(cs_ar_bytes & X86_SEG_AR_DEF_OP_SIZE) ? 4 : 2);}static void vmx_save_dr(struct vcpu *v){    if ( !v->arch.hvm_vcpu.flag_dr_dirty )        return;    /* Clear the DR dirty flag and re-enable intercepts for DR accesses. */    v->arch.hvm_vcpu.flag_dr_dirty = 0;    v->arch.hvm_vmx.exec_control |= CPU_BASED_MOV_DR_EXITING;    __vmwrite(CPU_BASED_VM_EXEC_CONTROL, v->arch.hvm_vmx.exec_control);    v->arch.guest_context.debugreg[0] = read_debugreg(0);    v->arch.guest_context.debugreg[1] = read_debugreg(1);    v->arch.guest_context.debugreg[2] = read_debugreg(2);    v->arch.guest_context.debugreg[3] = read_debugreg(3);    v->arch.guest_context.debugreg[6] = read_debugreg(6);    /* DR7 must be saved as it is used by vmx_restore_dr(). */    v->arch.guest_context.debugreg[7] = __vmread(GUEST_DR7);

⌨️ 快捷键说明

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