у меня такая проблема. я инициализирую VMCS. вроде всё делаю по даташиту. по крайней мере не выдаётся ошибка о неправильной инициализации контрольной области или области хоста. затем делаю vmlaunch ... и всё виснет. не бсодит, а именно виснет, ввиду чего капец осложняется отладка, ибо не видно почему произошло зависание. задачу я себе поставил написать что-то вроде bluepill'а, только для интела. т.е. сделать гипервизор, который бы выходил на определённых ивентах из оригинальной винды (х64), выполняющейся в виртуальном режиме, ну и выводил бы что-то в дебаг. для гипервизора выделяю отдельный стек. у меня есть подозрение что зависает по причине какой-то неправильной инициализации сегментных селекторов. я беру селекторы, парсю гдт, заношу базу, лимит и аттрибуты, а селектору перед записью в VMCS делаю (& ~7), поскольку в даташите написано, что TI и RPL должны быть равны 0. может ли кто-то выложить код инициализации VMCS? думаю что ошибка где-то в инициализации гостевой части.
мой код инициализации VMCS Код (Text): ULONG64 vmx_basic_msr = __readmsr(MSR_IA32_VMX_BASIC); ULONG32 vmcs_revision_id = (ULONG32)vmx_basic_msr; PVMCS p_vmcs = (PVMCS)ExAllocatePool(NonPagedPool, VMCS_SIZE); if (!p_vmcs) { DBG_PRINT(("Cannot allocate memory for VMCS region!")); DBG_PRINT(("Cannot initialize VMCS!")); DBG_END((0)); return FALSE; } memset((PVOID)p_vmcs, 0, VMCS_SIZE); p_vmcs->vmcs_revision_id = vmcs_revision_id; PHYSICAL_ADDRESS pa_vmcs = MmGetPhysicalAddress((PVOID)p_vmcs); _vmclear(&pa_vmcs); _vmptrld(&pa_vmcs); ULONG64 hypervisor_stack_top = (ULONG64)ExAllocatePool(NonPagedPool, KERNEL_STACK_SIZE); memset((PVOID)hypervisor_stack_top, 0, KERNEL_STACK_SIZE); ULONG64 hypervisor_stack_bottom = hypervisor_stack_top + KERNEL_STACK_SIZE; SEGMENT_SELECTOR es; SEGMENT_SELECTOR cs; SEGMENT_SELECTOR ss; SEGMENT_SELECTOR ds; SEGMENT_SELECTOR fs; SEGMENT_SELECTOR gs; SEGMENT_SELECTOR tr; SEGMENT_SELECTOR ldt; segment_selector_init(&es, RegGetEs(), GetGdtBase()); segment_selector_init(&cs, RegGetCs(), GetGdtBase()); segment_selector_init(&ss, RegGetSs(), GetGdtBase()); segment_selector_init(&ds, RegGetDs(), GetGdtBase()); segment_selector_init(&fs, RegGetFs(), GetGdtBase()); segment_selector_init(&gs, RegGetGs(), GetGdtBase()); segment_selector_init(&tr, GetTrSelector(), GetGdtBase()); segment_selector_init(&ldt, GetLDTSelector(), GetGdtBase()); // init guest state area _vmwrite(GUEST_CR0, __readcr0()); _vmwrite(GUEST_CR3, __readcr3()); _vmwrite(GUEST_CR4, __readcr4()); _vmwrite(GUEST_DR7, RegGetDr7()); _vmwrite(GUEST_GDTR_BASE, GetGdtBase()); _vmwrite(GUEST_GDTR_LIMIT, GetGdtLimit()); _vmwrite(GUEST_IDTR_BASE, GetIdtBase()); _vmwrite(GUEST_IDTR_LIMIT, GetIdtLimit()); _vmwrite(GUEST_LDTR_BASE, ldt.base); _vmwrite(GUEST_LDTR_LIMIT, ldt.limit); _vmwrite(GUEST_LDTR_AR_BYTES, ldt.ar.all); _vmwrite(GUEST_LDTR_SELECTOR, ldt.selector.all); _vmwrite(GUEST_TR_BASE, tr.base); _vmwrite(GUEST_TR_LIMIT, tr.limit); _vmwrite(GUEST_TR_AR_BYTES, tr.ar.all); _vmwrite(GUEST_TR_SELECTOR, tr.selector.all); _vmwrite(GUEST_ES_SELECTOR, es.selector.all); _vmwrite(GUEST_ES_LIMIT, es.limit); _vmwrite(GUEST_ES_AR_BYTES, es.ar.all); _vmwrite(GUEST_ES_BASE, es.base); _vmwrite(GUEST_CS_SELECTOR, cs.selector.all); _vmwrite(GUEST_CS_LIMIT, cs.limit); _vmwrite(GUEST_CS_AR_BYTES, cs.ar.all); _vmwrite(GUEST_CS_BASE, cs.base); _vmwrite(GUEST_SS_SELECTOR, ss.selector.all); _vmwrite(GUEST_SS_LIMIT, ss.limit); _vmwrite(GUEST_SS_AR_BYTES, ss.ar.all); _vmwrite(GUEST_SS_BASE, ss.base); _vmwrite(GUEST_DS_SELECTOR, ds.selector.all); _vmwrite(GUEST_DS_LIMIT, ds.limit); _vmwrite(GUEST_DS_AR_BYTES, ds.ar.all); _vmwrite(GUEST_DS_BASE, ds.base); _vmwrite(GUEST_FS_SELECTOR, fs.selector.all); _vmwrite(GUEST_FS_LIMIT, fs.limit); _vmwrite(GUEST_FS_AR_BYTES, fs.ar.all); _vmwrite(GUEST_FS_BASE, fs.base); _vmwrite(GUEST_GS_SELECTOR, gs.selector.all); _vmwrite(GUEST_GS_LIMIT, gs.limit); _vmwrite(GUEST_GS_AR_BYTES, gs.ar.all); _vmwrite(GUEST_GS_BASE, gs.base); _vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(MSR_IA32_DEBUGCTL)); _vmwrite(GUEST_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS)); _vmwrite(GUEST_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP)); _vmwrite(GUEST_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP)); _vmwrite(VMCS_LINK_POINTER, 0xffffffffffffffff); /* VMCS link pointer (64 bits). This field is included for future expansion. Software should set this field to FFFFFFFF_FFFFFFFFH to avoid VM-entry failures (see Section 22.3.1.5)*/ // init host state area _vmwrite(HOST_CR0, __readcr0()); _vmwrite(HOST_CR3, __readcr3()); _vmwrite(HOST_CR4, __readcr4()); _vmwrite(HOST_RSP, hypervisor_stack_bottom); _vmwrite(HOST_ES_SELECTOR, es.selector.all & ~7); _vmwrite(HOST_DS_SELECTOR, ds.selector.all & ~7); _vmwrite(HOST_CS_SELECTOR, cs.selector.all & ~7); _vmwrite(HOST_SS_SELECTOR, ss.selector.all & ~7); _vmwrite(HOST_FS_SELECTOR, fs.selector.all & ~7); _vmwrite(HOST_GS_SELECTOR, gs.selector.all & ~7); _vmwrite(HOST_TR_SELECTOR, tr.selector.all & ~7); _vmwrite(HOST_FS_BASE, fs.base); _vmwrite(HOST_GS_BASE, gs.base); _vmwrite(HOST_TR_BASE, tr.base); _vmwrite(HOST_GDTR_BASE, GetGdtBase()); _vmwrite(HOST_IDTR_BASE, GetIdtBase()); _vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP)); _vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP)); _vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS)); // init VM-EXECUTION CONTROL FIELDS ULONG64 vmx_pinbased_ctls_msr = __readmsr(MSR_IA32_VMX_PINBASED_CTLS); ULONG64 vmx_procbased_ctls_msr = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS); ULONG64 vmx_exit_ctls_msr = __readmsr(MSR_IA32_VMX_EXIT_CTLS); ULONG64 vmx_entry_ctls_msr = __readmsr(MSR_IA32_VMX_ENTRY_CTLS); ULONG32 vmx_pinbased_ctls = (ULONG32)vmx_pinbased_ctls_msr;// & (ULONG32)(vmx_pinbased_ctls_msr >> 32); ULONG32 vmx_procbased_ctls = (ULONG32)vmx_procbased_ctls_msr;// & (ULONG32)(vmx_procbased_ctls_msr >> 32); ULONG32 vmx_exit_ctls = (ULONG32)vmx_exit_ctls_msr; ULONG32 vmx_entry_ctls = (ULONG32)vmx_entry_ctls_msr; vmx_exit_ctls |= EXIT_CTRLS_HOST_ADDR_SPACE_SIZE; vmx_entry_ctls |= ENTRY_CTRLS_IA32E_MODE_GUEST; _vmwrite(PIN_BASED_VM_EXEC_CONTROL, vmx_pinbased_ctls); _vmwrite(CPU_BASED_VM_EXEC_CONTROL1, vmx_procbased_ctls); _vmwrite(VM_EXIT_CONTROLS, vmx_exit_ctls); _vmwrite(VM_ENTRY_CONTROLS, vmx_entry_ctls); _vmwrite(CR0_GUEST_HOST_MASK, 0xffffffffffffffff); _vmwrite(CR4_GUEST_HOST_MASK, 0xffffffffffffffff); _vmwrite(CR0_READ_SHADOW, __readcr0()); _vmwrite(CR4_READ_SHADOW, __readcr4()); после инициализации вызываю процедуру subvert(); Код (Text): subvert PROC push_all mov rdx, offset __vmlaunch_done mov rcx, GUESR_RIP call _vmwrite mov rdx, rsp mov rcx, GUESR_RSP call _vmwrite mov rdx, offset vmx_on_exit mov rcx, HOST_RIP call _vmwrite cli pushfq pop rdx mov rcx, GUEST_RFLAGS call _vmwrite vmlaunch ALIGN 16 __vmlaunch_done: jc __vmlaunch_cflag jz __vmlaunch_zflag mov qword ptr [rsp], 0 __vmlaunch_ret: pop_all sti ret __vmlaunch_cflag: mov qword ptr [rsp], 1 ; rax == 1 -- cflag jmp __vmlaunch_ret __vmlaunch_zflag: mov qword ptr [rsp], 2 ; rax == 2 -- zflag jmp __vmlaunch_ret subvert ENDP vmx_on_exit PROC push_all mov ax, cs and ax, 3 ; cpl == 0 ? jz __vm_exit_start swapgs __vm_exit_start: sub rsp, 20h call vmx_exit_dispatcher add rsp, 20h mov ax, cs and ax, 3 ; cpl == 0 ? jz __vm_exit_end swapgs __vm_exit_end: mov rcx, GUESR_RIP call _vmread mov rdx, rax mov rcx, VM_EXIT_INSTRUCTION_LEN call _vmread add rdx, rax mov rcx, GUESR_RIP call _vmwrite pop_all vmresume vmx_on_exit ENDP ну и как бы после выполнения сабвёрта комп виснет намертво. без бсода. никак не соображу где копать...
k3internal А чем там можно ладить? Только удалённая отладка по кабелю.. Софтварные эмуляторы вряд ли уже умеют эмулировать виртуализацию.
а всё таки дельные идеи какие-то будут? читаю исходники ксена и мне всё больше кажется что я неправильно инициализирую сегменты...
Synth 1. у тебя MP-система? 2. во всех следующих случаях затирается последний сохраненный (push_all) регистр 3. зачем эти инструкции? CPL будет равен нулю в любом случае
1. система МР. я это учитываю, процессоры переключаю отдельно. код доставки на процессор для простоты опустил. 2. mov qword ptr [rsp] - я же там в комментариях написал - rax. последний сохранённый регистр, через который обратно передаётся код результата выполнения vmlaunch. 3. да, тут я немного просчитался, поскольку переносил код с работающего SVMа. тут надо по другому... но тем не менее без этого кода тоже не работает. господа, если у вас всё получилось, то выложите хоть кто-то код своей инициализации...
Synth при выполнении vmlaunch возможно 3 варианта действий 1. на первом этапе проверок VMCS произошла ошибка => переход на __vmlaunch_done и завершение функции с ошибкой 2. vmlaunch завершился успешно => переход в VMX non-root 3. на втором этапе проверок VMCS произошла ошибка => загрузка host-состояния и переход на vmx_on_exit в первом случае зависания быть не может по определению в третьем случае поле VM-Exit Reason в VMCS содержит код ошибки, а VM-Exit Qualification - описание ошибки при этом IF в RFLAGS сброшен поэтому, если где-то в vmx_exit_dispatcher, возможно потенциальное зацикливание, возможно и зависание аналогичная ситуация и во-втором случае, если осуществляется VM-Exit
1. так и происходит если преднамеренно не инициализировать какое-то важное поле. 3. сейчас в vmx_exit_dispatcher просто вызывается KeBugCheck, никакого зацикливания быть не может. скорее всего сюда мы никогда не заходим, поскольку виснем намертво. МП учтён сто процентов. ибо SVM от AMD мне удалось запустить на многопроцессорной машине. работает идеально. все vmxon есть, всё работает. проверено. попробую ещё раз попросить код... если не хотите светить - скиньте в личку. мне просто надо сравнить и я сам пойму в чём у меня ошибка.
rei3er А вот насчёт МР - где-то видел пример vmx, который поставлялся в виде отдельного драйвера под каждый процессор. Мне непонятна работа системы в промежутке между переходом первого ядра и всех остальных. Возможна ли нормальная работа системы (ОС), когда одно ядро переключили в VMX-on состояние (или даже перешли в vmx non-root), а остальные ядра работают как есть?
не проверял, но думаю да, работать будет в этом случае, однако, любая генерация виртуализируемым CPU неперехватываемого гипервизором IPI (когда для целевого CPU не включено расширение VMX) может привести к потере контроля над гостевой ОС или детекту гипервизора после включения VMX CPU находится в режиме VMX root при этом нет никакой активной VM т. е вообще проблемы нету после того, как код включения VMX отработает на каждом CPU, можно запускать какую-либо VM
стоп. а вы уверены в том что сначала надо сделать vmxon на каждом процессоре, а уже потом переводить кого-то в виртуальный режим? на мой взгляд это не обязательно. например SVM прекрасно работает только на одном ядре. никаких нарушений в стабильности работы винды в таком режиме я не выявил. но это мы отвлеклись от темы. дельные советы по поводу того, почему у меня может виснуть? посмотрите, так ли у вас инициализируются сегментные селекторы?