Now it does not return 7 rather it vmexit with code 0x(80…)21 qualification 0 and if I set vmxe qualification = 4 immediately after vmlaunch before any instructions, I read instructions on vmexit the instructions seem correct in memory.
Code: Select all
#pragma pack(push, 1)
typedef struct {
UINT16 limit;
UINT64 base;
} DescriptorTableRegister;
#pragma pack(pop)
// TR (TSS descriptor)
typedef struct {
UINT16 lim, addr0;
UINT8 AddrFlagsEtc[4];
UINT32 Addr2;
UINT32 Zero;
} TSSDESC;
extern UINT64 __GDT;
int KCALL _VmxOn(void* VmxMem);
// writes to both guest and host
#define SEG_DATA_ACCESS 0xC093 // Present, Read/Write, DPL=0, usable
#define SEG_CODE_ACCESS 0xA09B // Present, Exec/Read, DPL=0, 64-bit
#define SEG_LIMIT 0xFFFFFFFF
extern DescriptorTableRegister __GDTR, __IDTR;
extern UINT64 __TSS, __TSSR;
#define IA32_VMX_CR0_FIXED0 0x486
#define IA32_VMX_CR0_FIXED1 0x487
#define IA32_VMX_CR4_FIXED0 0x488
#define IA32_VMX_CR4_FIXED1 0x489
#define vmxwrchk(_gr, _val) {static int __errcode; if((__errcode = __vmx_vmwrite(_gr, _val))) {KConOut(L"VMX Write to %lx Failed with code %d", _gr, __errcode);}}
#define vmcs(_guestreg, _hostreg, val) {vmxwrchk((UINT64)_guestreg, (UINT64)(val)); vmxwrchk((UINT64)(_hostreg), (UINT64)(val));}
#define gvmcs(__guestreg, __val) vmxwrchk((UINT64)(__guestreg), (UINT64)(__val))
int K
Code: Select all
void KCALL VmStart(void* vmxon_region)
{
EnhancedMemClr(vmxon_region, 0x10000);
void* vmcs_region = (char*)vmxon_region + 0x1000;
void* guest_stack = (char*)vmxon_region + 0x2000;
// 1. Read VMX basic info and VMX controls from MSRs
UINT64 vmx_basic = __readmsr(IA32_VMX_BASIC);
UINT64 pinbased_ctls = __readmsr(IA32_VMX_TRUE_PINBASED_CTLS);
UINT64 procbased_ctls = __readmsr(IA32_VMX_TRUE_PROCBASED_CTLS);
UINT64 secondary_ctls = __readmsr(IA32_VMX_SECONDARY_PROCBASED_CTLS);
UINT64 vmexit_ctls = __readmsr(IA32_VMX_TRUE_EXIT_CTLS);
UINT64 vmentry_ctls = __readmsr(IA32_VMX_TRUE_ENTRY_CTLS);
// Helper: must set fixed 1 bits and clear fixed 0 bits from MSR (upper 32 bits = fixed1 mask, lower 32 bits = fixed0 mask)
#define FIX_CTL(val, msr) \
(((val) | (UINT32)((msr) >> 32)) & (UINT32)(msr))
UINT32 pinbased = FIX_CTL(0, pinbased_ctls); // start with 0 or with some bits you want enabled
UINT32 procbased = FIX_CTL(0, procbased_ctls);
UINT32 secondary = FIX_CTL(0, secondary_ctls);
UINT32 vmexit = FIX_CTL(0, vmexit_ctls);
UINT32 vmentry = FIX_CTL(0, vmentry_ctls);
// secondary controls bit in primary processor-based controls (bit 31)
procbased |= (1u << 31);
// Enable IA-32e mode guest (bit 9) in VM-entry controls (for 64-bit guest)
vmentry |= (1u << 9) | (1u << 14);
// Enable VM-exit controls for host address space size (bit 9)
vmexit |= (1u << 9);
// Optionally enable EPT (bit 1) in secondary controls if you want to use it
// secondary |= (1u << 1);
secondary |= (1 << 7);
// 2. Prepare VMXON region (must write VMCS revision ID to first 4 bytes)
*(UINT32*)vmxon_region = (UINT32)(vmx_basic & 0xFFFFFFFF);
// 3. Prepare VMCS region similarly
*(UINT32*)vmcs_region = (UINT32)(vmx_basic & 0xFFFFFFFF);
// 4. Execute VMXON
int vmxon_status = __vmx_on((UINT64*)&vmxon_region);
if (vmxon_status != 0) {
// error
return;
}
KConOut(L"KERNEL PHYS %lx %lx", GetPhysicalAddress((void*)__GDTR.base), *((UINT64*)GetPhysicalAddress((void*)__GDTR.base) + 8));
// 5. Clear VMCS region before use
__vmx_vmclear((UINT64*)&vmcs_region);
// 6. Load VMCS pointer
__vmx_vmptrld((UINT64*)&vmcs_region);
// 7. Write VMX control fields to VMCS
gvmcs(PIN_BASED_VM_EXEC_CONTROL, pinbased);
gvmcs(CPU_BASED_VM_EXEC_CONTROL, procbased);
gvmcs(SECONDARY_VM_EXEC_CONTROL, secondary);
gvmcs(VM_EXIT_CONTROLS, vmexit);
gvmcs(VM_ENTRY_CONTROLS, (1ULL << 9) | (1ULL << 14) | vmentry);
// 8. Setup guest state fields
UINT64 guest_cr0 = (__readmsr(IA32_VMX_CR0_FIXED0)) & __readmsr(IA32_VMX_CR0_FIXED1);
UINT64 guest_cr4 = (__readmsr(IA32_VMX_CR4_FIXED0)) & __readmsr(IA32_VMX_CR4_FIXED1);
gvmcs(GUEST_CR0, (guest_cr0 | (1 << 0)) & ~(1ULL << 31));
gvmcs(GUEST_CR4, guest_cr4);
// gvmcs(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, 0x00000400); // DR7: default, no breakpoints
__vmx_vmwrite(0x681E, 0xFFFF0FF0); // DR6: reserved bits set
__vmx_vmwrite(GUEST_IA32_DEBUGCTL, 0x0); // Disable debug features
gvmcs(GUEST_GDTR_BASE, GetPhysicalAddress((void*)__GDTR.base));
gvmcs(GUEST_GDTR_LIMIT, __GDTR.limit);
gvmcs(GUEST_IDTR_BASE, GetPhysicalAddress((void*)__IDTR.base));
gvmcs(GUEST_IDTR_LIMIT, __IDTR.limit);
gvmcs(GUEST_LDTR_SELECTOR, 0);
gvmcs(GUEST_LDTR_BASE, 0);
gvmcs(GUEST_LDTR_LIMIT, 0);
// gvmcs(GUEST_LDTR_AR_BYTES, 0x10000);
gvmcs(GUEST_LDTR_AR_BYTES, 0x0000);
gvmcs(GUEST_CS_SELECTOR, 0x8);
gvmcs(GUEST_DS_SELECTOR, 0x10);
gvmcs(GUEST_ES_SELECTOR, 0x10);
gvmcs(GUEST_GS_SELECTOR, 0x10);
gvmcs(GUEST_FS_SELECTOR, 0x10);
gvmcs(GUEST_RIP, (UINT64)GetPhysicalAddress((void*)_VmxGuest));
gvmcs(GUEST_RSP, (UINT64)GetPhysicalAddress((void*)guest_stack));
gvmcs(GUEST_RFLAGS, 2);
gvmcs(GUEST_IA32_EFER, 0x500);
// TODO: similarly setup other guest segments (DS, ES, SS, FS, GS, TR, LDTR)
#define SEG_ACCESS_CODE64 0xA09B // exec/read, conforming=0, long=1
#define SEG_ACCESS_DATA 0xC093 // read/write, expand down=0
#define SEG_ACCESS_TSS 0x008B // 64-bit TSS (Available)
// CS
gvmcs(GUEST_CS_BASE, 0);
gvmcs(GUEST_CS_LIMIT, 0xFFFFF );
gvmcs(GUEST_CS_AR_BYTES, SEG_ACCESS_CODE64);
// SS, DS, ES, FS, GS
__vmx_vmwrite(GUEST_SS_SELECTOR, 0x10);
__vmx_vmwrite(GUEST_SS_LIMIT, (UINT64)0xFFFFF );
__vmx_vmwrite(GUEST_SS_BASE, 0);
__vmx_vmwrite(GUEST_SS_AR_BYTES, 0xC093);
gvmcs(GUEST_DS_BASE, 0);
gvmcs(GUEST_DS_LIMIT, 0xFFFFF );
gvmcs(GUEST_DS_AR_BYTES, SEG_ACCESS_DATA);
gvmcs(GUEST_ES_BASE, 0);
gvmcs(GUEST_ES_LIMIT, 0xFFFFF );
gvmcs(GUEST_ES_AR_BYTES, SEG_ACCESS_DATA);
gvmcs(GUEST_FS_BASE, 0);
gvmcs(GUEST_FS_LIMIT, 0xFFFFF );
gvmcs(GUEST_FS_AR_BYTES, SEG_ACCESS_DATA);
gvmcs(GUEST_GS_BASE, 0);
gvmcs(GUEST_GS_LIMIT, 0xFFFFF );
gvmcs(GUEST_GS_AR_BYTES, SEG_ACCESS_DATA);
UINT64 TSS = GetPhysicalAddress((void*)(&__TSS));
TSSDESC* Tssr = (TSSDESC*)((char*)(&__GDT) + 0x28);
KConOut(L"LIM %x", (UINT32)Tssr->lim);
Tssr->lim = 0x67;
Tssr->addr0 = (UINT16)TSS;
Tssr->AddrFlagsEtc[0] = (UINT8)(TSS >> 16);
Tssr->AddrFlagsEtc[1] = 0x8B;
Tssr->AddrFlagsEtc[2] = 0b10000000;
Tssr->AddrFlagsEtc[3] = (UINT8)(TSS >> 24);
Tssr->Addr2 = TSS >> 32;
Tssr->Zero = 0;
gvmcs(GUEST_TR_SELECTOR, 0x28);
gvmcs(GUEST_TR_BASE, (UINT64)TSS); // you must define this
gvmcs(GUEST_TR_LIMIT, 0x67);
gvmcs(GUEST_TR_AR_BYTES, 0x8B);
// 9. Setup host state fields
gvmcs(HOST_CR0, __readcr0());
gvmcs(HOST_CR3, __readcr3());
gvmcs(HOST_CR4, __readcr4());
gvmcs(HOST_CS_SELECTOR, 0x10); // kernel data segment selector
gvmcs(HOST_SS_SELECTOR, 0x10);
gvmcs(HOST_DS_SELECTOR, 0x10);
gvmcs(HOST_ES_SELECTOR, 0x10);
gvmcs(HOST_FS_SELECTOR, 0x10);
gvmcs(HOST_GS_SELECTOR, 0x10);
gvmcs(HOST_TR_SELECTOR, 0x18); // TSS selector
gvmcs(HOST_IA32_EFER, __readmsr(0xC0000080)); // MSR_EFER
gvmcs(HOST_RSP, (UINT64)guest_stack + 0x1008);
gvmcs(HOST_RIP, (UINT64)VmExitHandler);
UINT64 GuestRip;
__vmx_vmread(GUEST_RIP, &GuestRip);
KConOut(L"Launching VM... GUEST_RIP %lx", GuestRip);
// 10. Launch the VM
int vmlaunch_status = __vmx_vmlaunch();
if (vmlaunch_status != 0) {
UINT64 vmerr;
__vmx_vmread(0x4400, &vmerr); // VM-instruction error field
KConOut(L"VMLAUNCH Error %d", vmerr);
// Handle error - print vmerr, halt, etc.
}
// VM launched successfully; guest will run now
}