问题描述
说明:
我正在关注有关虚拟机管理程序开发的tutorial。在该系列的day 4之后,我无法运行我的代码。在__vmx_vmlaunch
指令成功执行之后,正在测试虚拟机管理程序的虚拟机立即重新启动。我相信这是由于VMCS Host State Area (Chapter 24.5)的某些错误设置引起的。 WinDbg中没有BSOD,崩溃或错误消息。
我看到两种解决此问题的方法。一种是以某种方式从WinDbg中提取更多信息。第二个要求有人在vmcs初始化中发现我正在做或缺少的愚蠢事情。
很遗憾,教程中的代码不完整。我花了一些时间来确保本教程涵盖的所有内容与我的代码相同。我将突出显示我添加的部分。
我真的很抱歉这么多代码。再次让我强调一下,我认为问题出在HOST
字段之内(因为来宾虚拟机内部的崩溃不应使主机崩溃)。
int init_vmcs(struct __vcpu_t* vcpu)
{
log_entry("init_vmcs()\n");
// Determinate exact size which is implementation specific.
// We expect it to be 4KB,but it is not guaranteed.
union __vmx_basic_msr_t vmx_basic_msr = { 0 };
vmx_basic_msr.control = __readmsr(IA32_VMX_BASIC);
if (vmx_basic_msr.bits.vmxon_region_size != sizeof(struct __vmcs_t)) {
log_error("Non standard vmcs region size: %llx. Support not yet implemented.\n",vmx_basic_msr.bits.vmxon_region_size);
log_exit("init_vmcs()\n");
return -1;
}
PHYSICAL_ADDRESS physical_max;
physical_max.QuadPart = MAXULONG64;
vcpu->vmcs = MmAllocateContiguousMemory(sizeof(struct __vmcs_t),physical_max);
if (!vcpu->vmcs) {
log_error("Failed to allocate vcpu->vmcs(MmAllocateContiguousMemory Failed).\n");
log_exit("init_vmcs()\n");
return -1;
}
RtlSecureZeroMemory(vcpu->vmcs,sizeof(struct __vmcs_t));
vcpu->vmcs_physical = MmGetPhysicalAddress(vcpu->vmcs).QuadPart;
// discover VMCS revision identifier that a processor uses by reading the
// VMX capability MSR IA32_VMX_BASIC.
vcpu->vmcs->header.bits.revision_identifier = (unsigned int)vmx_basic_msr.bits.vmcs_revision_identifier;
vcpu->vmcs->header.bits.shadow_vmcs_indicator = 0;
// Before loading vmcs we invoke vmclear to flush data which might be cached by processor.
if (__vmx_vmclear(&vcpu->vmcs_physical) || __vmx_vmptrld(&vcpu->vmcs_physical)) {
log_error("Failed to flush data or load vmcs. (__vmx_vmclear or __vmx_vmptrld Failed).\n");
log_exit("init_vmcs()\n");
return -1;
}
// Initialize VMCS Guest State Area.
if (__vmx_vmwrite(GUEST_CR0,__readcr0()) ||
__vmx_vmwrite(GUEST_CR3,__readcr3()) ||
__vmx_vmwrite(GUEST_CR4,__readcr4()) ||
__vmx_vmwrite(GUEST_DR7,__readdr(7)) ||
__vmx_vmwrite(GUEST_RSP,vcpu->guest_rsp) ||
__vmx_vmwrite(GUEST_RIP,vcpu->guest_rip) ||
__vmx_vmwrite(GUEST_RFLAGS,__readeflags()) ||
__vmx_vmwrite(GUEST_DEBUG_CONTROL,__readmsr(IA32_DEBUGCTL)) ||
__vmx_vmwrite(GUEST_SYSENTER_ESP,__readmsr(IA32_SYSENTER_ESP)) ||
__vmx_vmwrite(GUEST_SYSENTER_EIP,__readmsr(IA32_SYSENTER_EIP)) ||
__vmx_vmwrite(GUEST_SYSENTER_CS,__readmsr(IA32_SYSENTER_CS)) ||
__vmx_vmwrite(GUEST_VMCS_LINK_POINTER,~0ULL) ||
__vmx_vmwrite(GUEST_FS_BASE,__readmsr(IA32_FS_BASE)) ||
__vmx_vmwrite(GUEST_GS_BASE,__readmsr(IA32_GS_BASE))
) {
log_error("Failed to set guest state. (__vmx_vmwrite Failed).\n");
log_exit("init_vmcs()\n");
return -1;
}
if (__vmx_vmwrite(CR0_READ_SHADOW,__readcr0()) ||
__vmx_vmwrite(CR4_READ_SHADOW,__readcr4())
) {
log_error("Failed to set cr0_read_shadow or cr4_read_shadow. (__vmx_vmwrite Failed).\n");
log_exit("init_vmcs()\n");
return -1;
}
union __vmx_entry_control_t entry_controls = { 0 };
entry_controls.bits.ia32e_mode_guest = 1;
vmx_adjust_entry_controls(&entry_controls);
__vmx_vmwrite(VM_ENTRY_CONTROLS,entry_controls.control);
union __vmx_exit_control_t exit_controls = { 0 };
exit_controls.bits.host_address_space_size = 1;
vmx_adjust_exit_controls(&exit_controls);
__vmx_vmwrite(VM_EXIT_CONTROLS,exit_controls.control);
union __vmx_pinbased_control_msr_t pinbased_controls = { 0 };
vmx_adjust_pinbased_controls(&pinbased_controls);
__vmx_vmwrite(PIN_BASED_VM_EXECUTION_CONTROLS,pinbased_controls.control);
union __vmx_primary_processor_based_control_t primary_controls = { 0 };
primary_controls.bits.use_msr_bitmaps = 1;
primary_controls.bits.active_secondary_controls = 1;
vmx_adjust_primary_processor_based_controls(&primary_controls);
__vmx_vmwrite(PRIMARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS,primary_controls.control);
union __vmx_secondary_processor_based_control_t secondary_controls = { 0 };
secondary_controls.bits.enable_rdtscp = 1;
secondary_controls.bits.enable_xsave_xrstor = 1;
secondary_controls.bits.enable_invpcid = 1;
vmx_adjust_secondary_processor_based_controls(&secondary_controls);
__vmx_vmwrite(SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS,secondary_controls.control);
__vmx_vmwrite(GUEST_CS_SELECTOR,__read_cs());
__vmx_vmwrite(GUEST_SS_SELECTOR,__read_ss());
__vmx_vmwrite(GUEST_DS_SELECTOR,__read_ds());
__vmx_vmwrite(GUEST_ES_SELECTOR,__read_es());
__vmx_vmwrite(GUEST_FS_SELECTOR,__read_fs());
__vmx_vmwrite(GUEST_GS_SELECTOR,__read_gs());
__vmx_vmwrite(GUEST_LDTR_SELECTOR,__read_ldtr());
__vmx_vmwrite(GUEST_TR_SELECTOR,__read_tr());
__vmx_vmwrite(GUEST_CS_LIMIT,__segmentlimit(__read_cs()));
__vmx_vmwrite(GUEST_SS_LIMIT,__segmentlimit(__read_ss()));
__vmx_vmwrite(GUEST_DS_LIMIT,__segmentlimit(__read_ds()));
__vmx_vmwrite(GUEST_ES_LIMIT,__segmentlimit(__read_es()));
__vmx_vmwrite(GUEST_FS_LIMIT,__segmentlimit(__read_fs()));
__vmx_vmwrite(GUEST_GS_LIMIT,__segmentlimit(__read_gs()));
__vmx_vmwrite(GUEST_LDTR_LIMIT,__segmentlimit(__read_ldtr()));
__vmx_vmwrite(GUEST_TR_LIMIT,__segmentlimit(__read_tr()));
struct __pseudo_descriptor_64_t gdtr;
struct __pseudo_descriptor_64_t idtr;
_sgdt(&gdtr);
__sidt(&idtr);
__vmx_vmwrite(GUEST_GDTR_BASE,gdtr.base_address);
__vmx_vmwrite(GUEST_GDTR_LIMIT,gdtr.limit);
__vmx_vmwrite(GUEST_IDTR_BASE,idtr.base_address);
__vmx_vmwrite(GUEST_IDTR_LIMIT,idtr.limit);
__vmx_vmwrite(GUEST_CS_BASE,get_segment_base(gdtr.base_address,__read_cs()));
__vmx_vmwrite(GUEST_DS_BASE,__read_ds()));
__vmx_vmwrite(GUEST_SS_BASE,__read_ss()));
__vmx_vmwrite(GUEST_ES_BASE,__read_es()));
__vmx_vmwrite(GUEST_CS_ACCESS_RIGHTS,read_segment_access_rights(__read_cs()));
__vmx_vmwrite(GUEST_SS_ACCESS_RIGHTS,read_segment_access_rights(__read_ss()));
__vmx_vmwrite(GUEST_DS_ACCESS_RIGHTS,read_segment_access_rights(__read_ds()));
__vmx_vmwrite(GUEST_ES_ACCESS_RIGHTS,read_segment_access_rights(__read_es()));
__vmx_vmwrite(GUEST_FS_ACCESS_RIGHTS,read_segment_access_rights(__read_fs()));
__vmx_vmwrite(GUEST_GS_ACCESS_RIGHTS,read_segment_access_rights(__read_gs()));
__vmx_vmwrite(GUEST_LDTR_ACCESS_RIGHTS,read_segment_access_rights(__read_ldtr()));
__vmx_vmwrite(GUEST_TR_ACCESS_RIGHTS,read_segment_access_rights(__read_tr()));
__vmx_vmwrite(GUEST_LDTR_BASE,__read_ldtr()));
__vmx_vmwrite(GUEST_TR_BASE,__read_tr()));
// Initialize VMCS Host State Area.
__vmx_vmwrite(HOST_CR0,__readcr0()); // Added by me
__vmx_vmwrite(HOST_CR3,__readcr3()); // Added by me
__vmx_vmwrite(HOST_CR4,__readcr4()); // Added by me
// Fields RPL and TI in host selector fields must be cleared.
unsigned short host_selector_mask = 7;
__vmx_vmwrite(HOST_CS_SELECTOR,__read_cs() & ~host_selector_mask);
__vmx_vmwrite(HOST_SS_SELECTOR,__read_ss() & ~host_selector_mask);
__vmx_vmwrite(HOST_DS_SELECTOR,__read_ds() & ~host_selector_mask);
__vmx_vmwrite(HOST_ES_SELECTOR,__read_es() & ~host_selector_mask);
__vmx_vmwrite(HOST_FS_SELECTOR,__read_fs() & ~host_selector_mask);
__vmx_vmwrite(HOST_GS_SELECTOR,__read_gs() & ~host_selector_mask);
__vmx_vmwrite(HOST_TR_SELECTOR,__read_tr() & ~host_selector_mask);
__vmx_vmwrite(HOST_TR_BASE,__read_tr()));
__vmx_vmwrite(HOST_GDTR_BASE,gdtr.base_address);
__vmx_vmwrite(HOST_IDTR_BASE,idtr.base_address);
unsigned __int64 vmm_stack = (unsigned __int64)vcpu->vmm_context->stack + VMM_STACK_SIZE;
if (__vmx_vmwrite(HOST_RSP,vmm_stack) ||
__vmx_vmwrite(HOST_RIP,vmm_entrypoint)
) {
log_error("Failed to set host_rsp,host_rip. (__vmx_vmwrite Failed).\n");
log_exit("init_vmcs()\n");
return -1;
}
log_exit("init_vmcs()\n");
return 0;
}
void init_logical_processor(struct __vmm_context_t *vmm_context,void *guest_rsp)
{
log_entry("init_logical_processor()\n");
unsigned long cur_processor_number = KeGetCurrentProcessorNumber();
struct __vcpu_t* vcpu = vmm_context->vcpu_table[cur_processor_number];
log_debug("vcpu: %llx,guest_rsp: %llx\n",cur_processor_number,guest_rsp);
vcpu->guest_rsp = guest_rsp;
vcpu->guest_rip = (void*) guest_entry_stub;
adjust_control_registers();
if (enable_vmx_operation() != 0) {
log_error("Failed to enable_vmx_operation.\n");
goto _end;
}
if (!vm_has_cpuid_support()) {
log_error("VMX operation is not supported by the processor.\n");
goto _end;
}
log_success("VMX operation is supported by the processor.\n");
if (init_vmxon(vcpu)) {
log_error("Failed to initialize vmxon region.\n");
goto _end;
}
log_success("Initialized vmxon region.\n");
unsigned char vmxon_res = __vmx_on(&vcpu->vmxon_physical);
if (vmxon_res != 0) {
log_error("Failed to put vcpu into VMX operation. Error code: %d\n",vmxon_res);
goto _end;
}
log_success("vmx_on succeeded.\n");
if (init_vmcs(vcpu)) {
log_error("Failed to initialize vmcs.\n");
goto _end;
}
log_success("Initialized vmcs.\n");
unsigned char vmlaunch_res = vmxlaunch(); // just a wrapper over __vmx_vmlaunch
if (vmlaunch_res != 0) {
goto _end;
}
_end:
log_exit("init_logical_processor()\n");
}
vmm_entrypoint proc
int 3 ; addded by me
vmm_entrypoint endp
guest_entry_stub proc
mov rax,1337h
hlt
guest_entry_stub endp
解决方法
更新
我再次阅读了有关VM-entry checks的英特尔手册部分,发现我的init_vmcs
函数未设置HOST_FS_BASE
,HOST_GS_BASE
。添加完这些字段后,它终于可以工作并被困在vmm_entrypoint
中。
但是,我很想听听一些有关如何调试意外关机的解决方案。