Skip to the content.

cr0

windows 启动之后

Samples: 104K of event 'kvm:kvm_cr', 1 Hz, Event count (approx.): 330
  60.53%  cr_write 0 = 0x11
  37.41%  cr_write 0 = 0x10
   2.05%  cr_write 0 = 0x80000011

linux 启动之后:

Samples: 3M of event 'kvm:kvm_cr', 1 Hz, Event count (approx.): 245824
  50.19%  cr_write 0 = 0x11
  49.77%  cr_write 0 = 0x10
   0.01%  cr_write 0 = 0x80050033
   0.00%  cr_write 0 = 0x1
   0.00%  cr_write 0 = 0x80010001
   0.00%  cr_write 0 = 0xc0050033
   0.00%  cr_write 4 = 0x1406a0
   0.00%  cr_write 4 = 0x3406a0
   0.00%  cr_write 4 = 0x340ea0
   0.00%  cr_write 4 = 0x406a0
   0.00%  cr_write 4 = 0x406b0
   0.00%  cr_write 4 = 0x740ea0
   0.00%  cr_write 4 = 0x740ee0
   0.00%  cr_write 4 = 0xa0

看似是中断的打开关闭导致的。

通用的分析

sudo perf trace -e kvm:kvm_cr
static int cr_interception(struct kvm_vcpu *vcpu)
{
	struct vcpu_svm *svm = to_svm(vcpu);
	int reg, cr;
	unsigned long val;
	int err;

	if (!static_cpu_has(X86_FEATURE_DECODEASSISTS))
		return emulate_on_interception(vcpu);

	if (unlikely((svm->vmcb->control.exit_info_1 & CR_VALID) == 0))
		return emulate_on_interception(vcpu);

	reg = svm->vmcb->control.exit_info_1 & SVM_EXITINFO_REG_MASK;
	if (svm->vmcb->control.exit_code == SVM_EXIT_CR0_SEL_WRITE)
		cr = SVM_EXIT_WRITE_CR0 - SVM_EXIT_READ_CR0;
	else
		cr = svm->vmcb->control.exit_code - SVM_EXIT_READ_CR0;

	err = 0;

最后调用到这里的:

static const struct x86_emulate_ops emulate_ops = {
	.vm_bugged           = emulator_vm_bugged,
	.read_gpr            = emulator_read_gpr,
	.write_gpr           = emulator_write_gpr,
	.read_std            = emulator_read_std,
	.write_std           = emulator_write_std,
	.fetch               = kvm_fetch_guest_virt,
	.read_emulated       = emulator_read_emulated,
	.write_emulated      = emulator_write_emulated,
	.cmpxchg_emulated    = emulator_cmpxchg_emulated,
	.invlpg              = emulator_invlpg,
	.pio_in_emulated     = emulator_pio_in_emulated,
	.pio_out_emulated    = emulator_pio_out_emulated,
	.get_segment         = emulator_get_segment,
	.set_segment         = emulator_set_segment,
	.get_cached_segment_base = emulator_get_cached_segment_base,
	.get_gdt             = emulator_get_gdt,
	.get_idt	     = emulator_get_idt,
	.set_gdt             = emulator_set_gdt,
	.set_idt	     = emulator_set_idt,
	.get_cr              = emulator_get_cr,
	.set_cr              = emulator_set_cr,
	.cpl                 = emulator_get_cpl,
	.get_dr              = emulator_get_dr,
	.set_dr              = emulator_set_dr,
	.set_msr_with_filter = emulator_set_msr_with_filter,
	.get_msr_with_filter = emulator_get_msr_with_filter,
	.get_msr             = emulator_get_msr,
	.check_rdpmc_early   = emulator_check_rdpmc_early,
	.read_pmc            = emulator_read_pmc,
	.halt                = emulator_halt,
	.wbinvd              = emulator_wbinvd,
	.fix_hypercall       = emulator_fix_hypercall,
	.intercept           = emulator_intercept,
	.get_cpuid           = emulator_get_cpuid,
	.guest_has_movbe     = emulator_guest_has_movbe,
	.guest_has_fxsr      = emulator_guest_has_fxsr,
	.guest_has_rdpid     = emulator_guest_has_rdpid,
	.set_nmi_mask        = emulator_set_nmi_mask,
	.is_smm              = emulator_is_smm,
	.is_guest_mode       = emulator_is_guest_mode,
	.leave_smm           = emulator_leave_smm,
	.triple_fault        = emulator_triple_fault,
	.set_xcr             = emulator_set_xcr,
	.get_untagged_addr   = emulator_get_untagged_addr,
};

cr3

ept 打开的时候,看不到 cr3 的使用

分析 arch/x86/kvm/svm/svm.c:cr_interception

sudo perf trace -e kvm:kvm_cr

但是 cr3 看上去从来不会有。

cr8

amd64/intel-sdm/v3-ch02.md

docs/kernel/cpuinfo/material/query-cpu-definitions.json

1385: “cr8legacy”,

141: clflush clflushopt clwb clzero cmov cmp-legacy core-capability cr8legacy 162: vmx-cr3-store-noexit vmx-cr8-load-exit vmx-cr8-store-exit vmx-desc-exit

static void apic_set_tpr(struct kvm_lapic *apic, u32 tpr)
{
	kvm_lapic_set_reg(apic, APIC_TASKPRI, tpr);
	apic_update_ppr(apic);
}

docs/qemu/int.md 中继续分析

int kvm_set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8)
{
	if (cr8 & CR8_RESERVED_BITS)
		return 1;
	if (lapic_in_kernel(vcpu))
		kvm_lapic_set_tpr(vcpu, cr8);
	else
		vcpu->arch.cr8 = cr8;
	return 0;
}
EXPORT_SYMBOL_GPL(kvm_set_cr8);

linux 会使用 CR8 或者 TPR 吗?

setup_local_APIC 中看,linux 应该是不去使用的:

	/*
	 * Set Task Priority to 'accept all except vectors 0-31'.  An APIC
	 * vector in the 16-31 range could be delivered if TPR == 0, but we
	 * would think it's an exception and terrible things will happen.  We
	 * never change this later on.
	 */
	value = apic_read(APIC_TASKPRI);
	value &= ~APIC_TPRI_MASK;
	value |= 0x10;
	apic_write(APIC_TASKPRI, value);

[ ] 那么就可以在 linux 中模拟这个操作了

之前的 EOI 模拟前面加上 tpr 的修改。

可以控制 cr8 的 exit 情况吗?

vmx_update_msr_bitmap_x2apic

	/*
	 * TPR reads and writes can be virtualized even if virtual interrupt
	 * delivery is not in use.
	 */
	vmx_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_RW,
				  !(mode & MSR_BITMAP_MODE_X2APIC));

注释如何理解?

init_vmcs

	if (cpu_has_vmx_tpr_shadow()) {
		vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0;ic->regs));
		vmcs_write32(TPR_THRESHOLD, 0);
	}

		if (cpu_need_tpr_shadow(&vmx->vcpu))
			vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
				     __pa(vmx->vcpu.arch.ap)

vmx_exec_control

#ifdef CONFIG_X86_64
	if (exec_control & CPU_BASED_TPR_SHADOW)
		exec_control &= ~(CPU_BASED_CR8_LOAD_EXITING |
				  CPU_BASED_CR8_STORE_EXITING);
	else
		exec_control |= CPU_BASED_CR8_STORE_EXITING |
				CPU_BASED_CR8_LOAD_EXITING;
#endif

这里还理解一个事情,是不是如果使用 mmio ,APIC_TASKPRI 的访问无论如何都无法被虚拟化,总是必须 exit ?

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。