vmx.c

来自「linux 内核源代码」· C语言 代码 · 共 2,567 行 · 第 1/5 页

C
2,567
字号
	vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, 0);	rdmsr(MSR_IA32_SYSENTER_CS, host_sysenter_cs, junk);	vmcs_write32(HOST_IA32_SYSENTER_CS, host_sysenter_cs);	rdmsrl(MSR_IA32_SYSENTER_ESP, a);	vmcs_writel(HOST_IA32_SYSENTER_ESP, a);   /* 22.2.3 */	rdmsrl(MSR_IA32_SYSENTER_EIP, a);	vmcs_writel(HOST_IA32_SYSENTER_EIP, a);   /* 22.2.3 */	for (i = 0; i < NR_VMX_MSR; ++i) {		u32 index = vmx_msr_index[i];		u32 data_low, data_high;		u64 data;		int j = vmx->nmsrs;		if (rdmsr_safe(index, &data_low, &data_high) < 0)			continue;		if (wrmsr_safe(index, data_low, data_high) < 0)			continue;		data = data_low | ((u64)data_high << 32);		vmx->host_msrs[j].index = index;		vmx->host_msrs[j].reserved = 0;		vmx->host_msrs[j].data = data;		vmx->guest_msrs[j] = vmx->host_msrs[j];		++vmx->nmsrs;	}	setup_msrs(vmx);	vmcs_write32(VM_EXIT_CONTROLS, vmcs_config.vmexit_ctrl);	/* 22.2.1, 20.8.1 */	vmcs_write32(VM_ENTRY_CONTROLS, vmcs_config.vmentry_ctrl);	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);  /* 22.2.1 */#ifdef CONFIG_X86_64	vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);	if (vm_need_tpr_shadow(vmx->vcpu.kvm))		vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,			     page_to_phys(vmx->vcpu.apic->regs_page));	vmcs_write32(TPR_THRESHOLD, 0);#endif	vmcs_writel(CR0_GUEST_HOST_MASK, ~0UL);	vmcs_writel(CR4_GUEST_HOST_MASK, KVM_GUEST_CR4_MASK);	vmx->vcpu.cr0 = 0x60000010;	vmx_set_cr0(&vmx->vcpu, vmx->vcpu.cr0); // enter rmode	vmx_set_cr4(&vmx->vcpu, 0);#ifdef CONFIG_X86_64	vmx_set_efer(&vmx->vcpu, 0);#endif	vmx_fpu_activate(&vmx->vcpu);	update_exception_bitmap(&vmx->vcpu);	return 0;out:	return ret;}static void vmx_vcpu_reset(struct kvm_vcpu *vcpu){	struct vcpu_vmx *vmx = to_vmx(vcpu);	vmx_vcpu_setup(vmx);}static void inject_rmode_irq(struct kvm_vcpu *vcpu, int irq){	u16 ent[2];	u16 cs;	u16 ip;	unsigned long flags;	unsigned long ss_base = vmcs_readl(GUEST_SS_BASE);	u16 sp =  vmcs_readl(GUEST_RSP);	u32 ss_limit = vmcs_read32(GUEST_SS_LIMIT);	if (sp > ss_limit || sp < 6 ) {		vcpu_printf(vcpu, "%s: #SS, rsp 0x%lx ss 0x%lx limit 0x%x\n",			    __FUNCTION__,			    vmcs_readl(GUEST_RSP),			    vmcs_readl(GUEST_SS_BASE),			    vmcs_read32(GUEST_SS_LIMIT));		return;	}	if (emulator_read_std(irq * sizeof(ent), &ent, sizeof(ent), vcpu) !=							X86EMUL_CONTINUE) {		vcpu_printf(vcpu, "%s: read guest err\n", __FUNCTION__);		return;	}	flags =  vmcs_readl(GUEST_RFLAGS);	cs =  vmcs_readl(GUEST_CS_BASE) >> 4;	ip =  vmcs_readl(GUEST_RIP);	if (emulator_write_emulated(ss_base + sp - 2, &flags, 2, vcpu) != X86EMUL_CONTINUE ||	    emulator_write_emulated(ss_base + sp - 4, &cs, 2, vcpu) != X86EMUL_CONTINUE ||	    emulator_write_emulated(ss_base + sp - 6, &ip, 2, vcpu) != X86EMUL_CONTINUE) {		vcpu_printf(vcpu, "%s: write guest err\n", __FUNCTION__);		return;	}	vmcs_writel(GUEST_RFLAGS, flags &		    ~( X86_EFLAGS_IF | X86_EFLAGS_AC | X86_EFLAGS_TF));	vmcs_write16(GUEST_CS_SELECTOR, ent[1]) ;	vmcs_writel(GUEST_CS_BASE, ent[1] << 4);	vmcs_writel(GUEST_RIP, ent[0]);	vmcs_writel(GUEST_RSP, (vmcs_readl(GUEST_RSP) & ~0xffff) | (sp - 6));}static void vmx_inject_irq(struct kvm_vcpu *vcpu, int irq){	if (vcpu->rmode.active) {		inject_rmode_irq(vcpu, irq);		return;	}	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,			irq | INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK);}static void kvm_do_inject_irq(struct kvm_vcpu *vcpu){	int word_index = __ffs(vcpu->irq_summary);	int bit_index = __ffs(vcpu->irq_pending[word_index]);	int irq = word_index * BITS_PER_LONG + bit_index;	clear_bit(bit_index, &vcpu->irq_pending[word_index]);	if (!vcpu->irq_pending[word_index])		clear_bit(word_index, &vcpu->irq_summary);	vmx_inject_irq(vcpu, irq);}static void do_interrupt_requests(struct kvm_vcpu *vcpu,				       struct kvm_run *kvm_run){	u32 cpu_based_vm_exec_control;	vcpu->interrupt_window_open =		((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&		 (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0);	if (vcpu->interrupt_window_open &&	    vcpu->irq_summary &&	    !(vmcs_read32(VM_ENTRY_INTR_INFO_FIELD) & INTR_INFO_VALID_MASK))		/*		 * If interrupts enabled, and not blocked by sti or mov ss. Good.		 */		kvm_do_inject_irq(vcpu);	cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);	if (!vcpu->interrupt_window_open &&	    (vcpu->irq_summary || kvm_run->request_interrupt_window))		/*		 * Interrupts blocked.  Wait for unblock.		 */		cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_INTR_PENDING;	else		cpu_based_vm_exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;	vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);}static void kvm_guest_debug_pre(struct kvm_vcpu *vcpu){	struct kvm_guest_debug *dbg = &vcpu->guest_debug;	set_debugreg(dbg->bp[0], 0);	set_debugreg(dbg->bp[1], 1);	set_debugreg(dbg->bp[2], 2);	set_debugreg(dbg->bp[3], 3);	if (dbg->singlestep) {		unsigned long flags;		flags = vmcs_readl(GUEST_RFLAGS);		flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;		vmcs_writel(GUEST_RFLAGS, flags);	}}static int handle_rmode_exception(struct kvm_vcpu *vcpu,				  int vec, u32 err_code){	if (!vcpu->rmode.active)		return 0;	/*	 * Instruction with address size override prefix opcode 0x67	 * Cause the #SS fault with 0 error code in VM86 mode.	 */	if (((vec == GP_VECTOR) || (vec == SS_VECTOR)) && err_code == 0)		if (emulate_instruction(vcpu, NULL, 0, 0) == EMULATE_DONE)			return 1;	return 0;}static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	u32 intr_info, error_code;	unsigned long cr2, rip;	u32 vect_info;	enum emulation_result er;	int r;	vect_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);	intr_info = vmcs_read32(VM_EXIT_INTR_INFO);	if ((vect_info & VECTORING_INFO_VALID_MASK) &&						!is_page_fault(intr_info)) {		printk(KERN_ERR "%s: unexpected, vectoring info 0x%x "		       "intr info 0x%x\n", __FUNCTION__, vect_info, intr_info);	}	if (!irqchip_in_kernel(vcpu->kvm) && is_external_interrupt(vect_info)) {		int irq = vect_info & VECTORING_INFO_VECTOR_MASK;		set_bit(irq, vcpu->irq_pending);		set_bit(irq / BITS_PER_LONG, &vcpu->irq_summary);	}	if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == 0x200) /* nmi */		return 1;  /* already handled by vmx_vcpu_run() */	if (is_no_device(intr_info)) {		vmx_fpu_activate(vcpu);		return 1;	}	error_code = 0;	rip = vmcs_readl(GUEST_RIP);	if (intr_info & INTR_INFO_DELIEVER_CODE_MASK)		error_code = vmcs_read32(VM_EXIT_INTR_ERROR_CODE);	if (is_page_fault(intr_info)) {		cr2 = vmcs_readl(EXIT_QUALIFICATION);		mutex_lock(&vcpu->kvm->lock);		r = kvm_mmu_page_fault(vcpu, cr2, error_code);		if (r < 0) {			mutex_unlock(&vcpu->kvm->lock);			return r;		}		if (!r) {			mutex_unlock(&vcpu->kvm->lock);			return 1;		}		er = emulate_instruction(vcpu, kvm_run, cr2, error_code);		mutex_unlock(&vcpu->kvm->lock);		switch (er) {		case EMULATE_DONE:			return 1;		case EMULATE_DO_MMIO:			++vcpu->stat.mmio_exits;			return 0;		 case EMULATE_FAIL:			kvm_report_emulation_failure(vcpu, "pagetable");			break;		default:			BUG();		}	}	if (vcpu->rmode.active &&	    handle_rmode_exception(vcpu, intr_info & INTR_INFO_VECTOR_MASK,								error_code)) {		if (vcpu->halt_request) {			vcpu->halt_request = 0;			return kvm_emulate_halt(vcpu);		}		return 1;	}	if ((intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK)) == (INTR_TYPE_EXCEPTION | 1)) {		kvm_run->exit_reason = KVM_EXIT_DEBUG;		return 0;	}	kvm_run->exit_reason = KVM_EXIT_EXCEPTION;	kvm_run->ex.exception = intr_info & INTR_INFO_VECTOR_MASK;	kvm_run->ex.error_code = error_code;	return 0;}static int handle_external_interrupt(struct kvm_vcpu *vcpu,				     struct kvm_run *kvm_run){	++vcpu->stat.irq_exits;	return 1;}static int handle_triple_fault(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	kvm_run->exit_reason = KVM_EXIT_SHUTDOWN;	return 0;}static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	unsigned long exit_qualification;	int size, down, in, string, rep;	unsigned port;	++vcpu->stat.io_exits;	exit_qualification = vmcs_readl(EXIT_QUALIFICATION);	string = (exit_qualification & 16) != 0;	if (string) {		if (emulate_instruction(vcpu, kvm_run, 0, 0) == EMULATE_DO_MMIO)			return 0;		return 1;	}	size = (exit_qualification & 7) + 1;	in = (exit_qualification & 8) != 0;	down = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_DF) != 0;	rep = (exit_qualification & 32) != 0;	port = exit_qualification >> 16;	return kvm_emulate_pio(vcpu, kvm_run, in, size, port);}static voidvmx_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall){	/*	 * Patch in the VMCALL instruction:	 */	hypercall[0] = 0x0f;	hypercall[1] = 0x01;	hypercall[2] = 0xc1;	hypercall[3] = 0xc3;}static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	unsigned long exit_qualification;	int cr;	int reg;	exit_qualification = vmcs_readl(EXIT_QUALIFICATION);	cr = exit_qualification & 15;	reg = (exit_qualification >> 8) & 15;	switch ((exit_qualification >> 4) & 3) {	case 0: /* mov to cr */		switch (cr) {		case 0:			vcpu_load_rsp_rip(vcpu);			set_cr0(vcpu, vcpu->regs[reg]);			skip_emulated_instruction(vcpu);			return 1;		case 3:			vcpu_load_rsp_rip(vcpu);			set_cr3(vcpu, vcpu->regs[reg]);			skip_emulated_instruction(vcpu);			return 1;		case 4:			vcpu_load_rsp_rip(vcpu);			set_cr4(vcpu, vcpu->regs[reg]);			skip_emulated_instruction(vcpu);			return 1;		case 8:			vcpu_load_rsp_rip(vcpu);			set_cr8(vcpu, vcpu->regs[reg]);			skip_emulated_instruction(vcpu);			kvm_run->exit_reason = KVM_EXIT_SET_TPR;			return 0;		};		break;	case 2: /* clts */		vcpu_load_rsp_rip(vcpu);		vmx_fpu_deactivate(vcpu);		vcpu->cr0 &= ~X86_CR0_TS;		vmcs_writel(CR0_READ_SHADOW, vcpu->cr0);		vmx_fpu_activate(vcpu);		skip_emulated_instruction(vcpu);		return 1;	case 1: /*mov from cr*/		switch (cr) {		case 3:			vcpu_load_rsp_rip(vcpu);			vcpu->regs[reg] = vcpu->cr3;			vcpu_put_rsp_rip(vcpu);			skip_emulated_instruction(vcpu);			return 1;		case 8:			vcpu_load_rsp_rip(vcpu);			vcpu->regs[reg] = get_cr8(vcpu);			vcpu_put_rsp_rip(vcpu);			skip_emulated_instruction(vcpu);			return 1;		}		break;	case 3: /* lmsw */		lmsw(vcpu, (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f);		skip_emulated_instruction(vcpu);		return 1;	default:		break;	}	kvm_run->exit_reason = 0;	pr_unimpl(vcpu, "unhandled control register: op %d cr %d\n",	       (int)(exit_qualification >> 4) & 3, cr);	return 0;}static int handle_dr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	unsigned long exit_qualification;	unsigned long val;	int dr, reg;	/*	 * FIXME: this code assumes the host is debugging the guest.	 *        need to deal with guest debugging itself too.	 */	exit_qualification = vmcs_readl(EXIT_QUALIFICATION);	dr = exit_qualification & 7;	reg = (exit_qualification >> 8) & 15;	vcpu_load_rsp_rip(vcpu);	if (exit_qualification & 16) {		/* mov from dr */		switch (dr) {		case 6:			val = 0xffff0ff0;			break;		case 7:			val = 0x400;			break;		default:			val = 0;		}		vcpu->regs[reg] = val;	} else {		/* mov to dr */	}	vcpu_put_rsp_rip(vcpu);	skip_emulated_instruction(vcpu);	return 1;}static int handle_cpuid(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	kvm_emulate_cpuid(vcpu);	return 1;}static int handle_rdmsr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	u32 ecx = vcpu->regs[VCPU_REGS_RCX];	u64 data;	if (vmx_get_msr(vcpu, ecx, &data)) {		vmx_inject_gp(vcpu, 0);		return 1;	}	/* FIXME: handling of bits 32:63 of rax, rdx */	vcpu->regs[VCPU_REGS_RAX] = data & -1u;	vcpu->regs[VCPU_REGS_RDX] = (data >> 32) & -1u;	skip_emulated_instruction(vcpu);	return 1;}static int handle_wrmsr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	u32 ecx = vcpu->regs[VCPU_REGS_RCX];	u64 data = (vcpu->regs[VCPU_REGS_RAX] & -1u)		| ((u64)(vcpu->regs[VCPU_REGS_RDX] & -1u) << 32);	if (vmx_set_msr(vcpu, ecx, data) != 0) {		vmx_inject_gp(vcpu, 0);		return 1;	}	skip_emulated_instruction(vcpu);	return 1;}static int handle_tpr_below_threshold(struct kvm_vcpu *vcpu,				      struct kvm_run *kvm_run){	return 1;}static int handle_interrupt_window(struct kvm_vcpu *vcpu,				   struct kvm_run *kvm_run){	u32 cpu_based_vm_exec_control;	/* clear pending irq */	cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);	cpu_based_vm_exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;	vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);	/*	 * If the user space waits to inject interrupts, exit as soon as	 * possible	 */	if (kvm_run->request_interrupt_window &&	    !vcpu->irq_summary) {		kvm_run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN;		++vcpu->stat.irq_window_exits;		return 0;	}	return 1;}static int handle_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run){	skip_emulated_instruction(vcpu);	return kvm_emulate_halt(vcpu);

⌨️ 快捷键说明

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