kvm对EXIT_REASON_MCE_DURING_VMENTRY的处理
本文档主要关心两个问题:
- kvm对EXIT_REASON_MCE_DURING_VMENTRY的处理
- kvm对Guest中触发的machine-check的处理
背景
- EXIT_REASON_MCE_DURING_VMENTRY
该vmexit发生在"HOST执行VMENTRY指令时发生machine-check"的条件下,vmexit_reason为41.
SDM对这种条件的描述为:
VM Entry时发生machine-check events,会有以下三种情况:
- 会以"machine-check发生于VM entry之前情况"处理该machine-check。
如果CR4.MCE=0,CPU的行为取决于处理器是否处于SMX operation.如果处于,会发生一个Intel TXT shutdown condition. 错误码是0xC。意思为:“不可恢复的machine-check情况。”;如果不处于,CPU会直接关闭。
如果CR.MCE=1,即使能了MC机制,会通过HOST IDT提交一次#MC exception。
- 该Machine-Check会在VM entry完成之后处理,
如果VM Entry之后CR.MCE=0,CPU行为取决于是否处于SMX operation. 如果处于,会发生一个Intel TXT shutdown condition. 错误码是0xC。意思为:“不可恢复的machine-check情况。”;如果不处于,CPU会直接关闭。
如果CR4.MCE=1,即,使能了MC机制,会产生一个#MC异常。如果exception bitmap的bit18,也就是#MC bit为0,那么该异常通过guest IDT提交;如果#MC bit为1,该#MC导致一个vmexit,即MC vmexit.
- 发生exit reason为41的vmexit.
如果MC event发生时,Vmentry已经load了任何Guest state,那么情况1就不会发生.
情况2只发生在VM entry能够load进所有 Guest state时。
如果kvm介入处理了,那么一定是发生了情况3,kvm检测到了编号为41的vmexit.
- Guest中触发machine-check进而vmexit
当Guest触发machine-check且exception bitmap的bit18,也就是#MC bit为1时,该#MC导致一个vmexit,即MC vmexit.在VMCS的vmexit_interrupt_info中会存放0x80000312,vmexit_reason=0。
KVM处理方式
- 非Nested-Virtualization环境
在非Nested-Virtualization环境下:
KVM在Guest发生vmexit后,会检查VMEXIT_REASON,当VMEXIT_REASON=41(EXIT_REASON_MCE_DURING_VMENTRY)时,KVM会设置regs.cs=3,regs->flags.IF=1,然后直接调用Host的machine-check函数。(这期间不会检查vmexit_int_info的内容,因为这不是一个中断/异常相关的vmexit)。
当VMEXIT_REASON=0时,KVM会首先检查该vmexit是否是由Guest内的machine-check导致的,即vmexit_int_info中记录的是否为0x80000312(MC_Vector),如果是,则设置regs.cs=3,regs->flags.IF=1,然后直接调用Host的machine-check函数。
- Nested-Virtualization环境
在Nested-Virtualization环境下:
- L2退出后vmexit reason为EXIT_REASON_MCE_DURING_VMENTRY
// 如果CPU运行在L2,而
vmx_handle_exit // 从L2退到L1,L1运行vmx_handle_exit,来处理来自L2的vmexit
=>nested_vmx_reflect_vmexit // L1运行nested_vmx_reflect_vmexit,将不同的vmexit分发到L0或L1
/* L0的优先级高于L1,所以先看L0是否需要处理该exit*/
=> nested_vmx_(whether)_l0_wants_(L2)exit(or not)
/* 如果L2的vmexit reason是41号,那么,L0 wants to handle this exit.*/
=> case EXIT_REASON_MCE_DURING_VMENTRY:
return true;// 返回L0处理
/* 如果L0不想处理,但是L1也不想处理,那么只能由L0处理*/
=> nested_vmx_(whether)_l1_wastopped at 到底L0什么时候处理这个exit_reasonnts_(L2)exit(or not)
=> case EXIT_REASON_MCE_DURING_VMENTRY:
return true; // L1可以处理
事实上,由于nested_vmx_(whether)l0_wants(L2)exit(or not)在nested_vmx_(whether)l1_wants(L2)exit(or not)之前,所以如果遇到L2的vmexit reason = EXIT_REASON_MCE_DURING_VMENTRY(41号),那么会直接返回L0处理,而不会再询问L1的意图。
上面所说的“直接返回L0”只是一个笼统的说法,事实上,如果L1上的nested_vmx_reflect_vmexit返回1,就会导致vmx_handle_exit返回1,进而试图重新进入L2,但还是会发生EXIT_REASON_MCE_DURING_VMENTRY,因而导致vmexit到L0,进行处理。接下来与Host中处理EXIT_REASON_MCE_DURING_VMENTRY的流程就没有区别了。
- L2退出后vmexit reason为0,vmexit_initr_info为0x80000312
这种情况下,L1确认vmexit_reason==0,且vmexit_intr_info为0x80000312,就会调用Guest的machine-check handler进行处理。
总之,在nested环境下,L2发生EXIT_REASON_MCE_DURING_VMENTRY,由Host处理一次用户空间的machine-check异常。L2发生machine-check事件,由L1处理一次发生于L1用户空间的machine-check.