kvm异常退出问题分析
问题现象:
虚拟机在启动的过程中报错,启动失败。日志中提示如下:
KVM internal error.Suberror:3
extra data[0]:800000ef
extra data[1]:31
寄存器值
------
分析:我觉得这个问题应该从“寄存器值RIP分析”。 这些寄存器的值是完全准确的虚拟机退出guest mode前的运行状态。 这里需要说明一下知识点,虚拟机运行时的寄存器怎么保存。
虽然我没看过完整的intel文档,但我起码可以分辨出它的设计。
1。 对于那些运行过程中使用的寄存器,比如RAX,RBX,RCS,R1,R2------等寄存器,当虚拟机陷出的时候是保存在vcpu的结构体中去的,这些是在vmx_vcpu_run函数中那一段汇编去做的。仔细阅读那一段汇编可以明白它的过程:save host registers、reload guest registers from vmsc,enter guest mode,exit guest mode and save guest registers to vmcs。
2. 还有一些寄存器是没办法使用软件自行去保存的,比如RIP,RSP,CR3等。因为当从guest mode切换到root mode的时候这些寄存器必然会变化。所以这些都是硬件去完成的,所以呢,这些寄存器是不需要软件去保存。 所以在exit guest mode后我们并不需要做什么,当这些特殊的寄存器需要被读取的时候。在vmx_cache_reg中,它重新读取VMCS,它需要从vmcs中重新读取。
static void vmx_vcpu_run
{
一大段汇编
vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
| (1 << VCPU_EXREG_RFLAGS)
| (1 << VCPU_EXREG_PDPTR)
| (1 << VCPU_EXREG_SEGMENTS)
| (1 << VCPU_EXREG_CR3));
vcpu->arch.regs_dirty = 0;
}
static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu,
enum kvm_reg reg)
{
if (!test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail))
kvm_x86_ops->cache_reg(vcpu, reg);
return vcpu->arch.regs[reg];
}
static void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg) { __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail); switch (reg) { case VCPU_REGS_RSP: vcpu->arch.regs[VCPU_REGS_RSP] = vmcs_readl(GUEST_RSP); break; case VCPU_REGS_RIP: vcpu->arch.regs[VCPU_REGS_RIP] = vmcs_readl(GUEST_RIP); break; case VCPU_EXREG_PDPTR: if (enable_ept) ept_save_pdptrs(vcpu); break; default: break; } }
在这个问题当中,这些信息是qemu打印的,它的路径是kvm_cpu_exec --> kvm_handle_internal_error,因为处理不了,所以继续走到cpu_dump_state中处理。在这个函数中它先去cpu_synchronize_state获取CPU各种寄存器等运行环境,然后打印出来。
说了这么多,其实就是想说这个RIP是真实的反映虚拟机最后执行的位置(之前不知道这段日志是谁打印的,所以才有了上面这么多的分析。如果早知道是qemu打印的,估计就不用看这么多代码。 不过看了这么多代码也是有所收获了)
其实寄存器都是这样,有些会拦截,有些是透传的。这个有一个bitmap去控制哪些寄存器会被拦截。 另外还有一些寄存器MSR,他们的读取很“费时”。但是原理一样,如果虚拟机与主机不一样,那么就是需要被store and reload的。
所以我们应该从guest RIP中去找出guest中出问题的代码。
https://bugzilla.redhat.com/show_bug.cgi?id=923653

浙公网安备 33010602011771号