自制VMM测试Intel VMX时vmlaunch触发无效客户机状态问题排查
哥们,我看你这个问题是只有在注入外部中断后重启虚拟机才会触发,结合你给的VMM流程和中断 stub 代码,大概率是中断处理后的状态保存/恢复环节出了问题,给你几个具体的排查点:
检查客户机状态寄存器的完整性
你用了pusha来保存CPU状态,但要注意:pusha只覆盖了通用寄存器(edi/esi/ebp/ebx等),但VMX要求的客户机状态远不止这些。尤其是注入外部中断后,客户机的EFLAGS.IF位可能被修改,还有段寄存器、CR0/CR4、IDT/GDT的状态,这些都得在VM退出时完整保存,下次启动VM前准确回填到VMCS里。
举个例子:如果中断处理后EFLAGS.IF被清0了,而你重启时没把这个状态正确写入VMCS的客户机RFLAGS字段,就可能触发状态无效;另外,客户机从实模式切到保护模式后,CR0.PE位是1,重启时要是错误地把CR0设回实模式的状态,肯定也会崩。重置VMCS里的中断相关字段
处理完外部中断准备重启虚拟机时,必须把VMCS里和中断/异常相关的字段清零:- 重点检查
VM_ENTRY_INTERRUPTION_INFO_FIELD:如果上次VM退出是因为中断注入,这个字段可能还留着中断的向量号和类型信息,下次VM进入前必须设为0,不然VMX会认为有未处理的中断待注入,直接判定状态无效。 - 还有
VM_ENTRY_EXCEPTION_ERROR_CODE_FIELD,这个字段如果有残留的错误码,也会导致同样的问题。
- 重点检查
确认客户机模式状态的一致性
你的VMM流程是让虚拟机从实模式切到保护模式后退出,注入中断重启时,得明确是让客户机从实模式从头启动,还是从保护模式的断点继续:- 如果是断点继续,那GDT/IDT的基址、CR3页表指针、段寄存器的选择子和属性这些保护模式下的关键状态,必须一个不差地恢复;
- 如果是从头启动实模式,那得把VMCS里的客户机状态全部重置回实模式的初始状态,不能残留保护模式的字段值,否则VMX会认为状态矛盾。
验证栈状态的正确性
pusha会往客户机栈里压8个寄存器的值,VM退出时你得准确保存此时的ESP指针,下次启动时恢复回去。另外,中断处理程序执行完后,一定要确保用popa恢复寄存器,并且正确执行iret返回,让客户机回到中断前的合法状态再触发VM退出——要是退出时客户机还在中断处理流程里(比如栈没恢复、寄存器不全),那保存的状态本身就不合法,重启时自然会报错。
内容的提问来源于stack exchange,提问作者wangt13

