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 + -
显示快捷键?