xref: /kvm-unit-tests/x86/vmx.c (revision 1d9284d07a39d36e2a05488787581b0072436958)
19d7eaa29SArthur Chunqi Li #include "libcflat.h"
29d7eaa29SArthur Chunqi Li #include "processor.h"
39d7eaa29SArthur Chunqi Li #include "vm.h"
49d7eaa29SArthur Chunqi Li #include "desc.h"
59d7eaa29SArthur Chunqi Li #include "vmx.h"
69d7eaa29SArthur Chunqi Li #include "msr.h"
79d7eaa29SArthur Chunqi Li #include "smp.h"
89d7eaa29SArthur Chunqi Li #include "io.h"
99d7eaa29SArthur Chunqi Li 
109d7eaa29SArthur Chunqi Li int fails = 0, tests = 0;
119d7eaa29SArthur Chunqi Li u32 *vmxon_region;
129d7eaa29SArthur Chunqi Li struct vmcs *vmcs_root;
139d7eaa29SArthur Chunqi Li u32 vpid_cnt;
149d7eaa29SArthur Chunqi Li void *guest_stack, *guest_syscall_stack;
159d7eaa29SArthur Chunqi Li u32 ctrl_pin, ctrl_enter, ctrl_exit, ctrl_cpu[2];
169d7eaa29SArthur Chunqi Li ulong fix_cr0_set, fix_cr0_clr;
179d7eaa29SArthur Chunqi Li ulong fix_cr4_set, fix_cr4_clr;
189d7eaa29SArthur Chunqi Li struct regs regs;
199d7eaa29SArthur Chunqi Li struct vmx_test *current;
209d7eaa29SArthur Chunqi Li u64 hypercall_field = 0;
219d7eaa29SArthur Chunqi Li bool launched;
22*1d9284d0SArthur Chunqi Li u64 host_rflags;
239d7eaa29SArthur Chunqi Li 
249d7eaa29SArthur Chunqi Li extern u64 gdt64_desc[];
259d7eaa29SArthur Chunqi Li extern u64 idt_descr[];
269d7eaa29SArthur Chunqi Li extern u64 tss_descr[];
279d7eaa29SArthur Chunqi Li extern void *vmx_return;
289d7eaa29SArthur Chunqi Li extern void *entry_sysenter;
299d7eaa29SArthur Chunqi Li extern void *guest_entry;
309d7eaa29SArthur Chunqi Li 
319d7eaa29SArthur Chunqi Li static void report(const char *name, int result)
329d7eaa29SArthur Chunqi Li {
339d7eaa29SArthur Chunqi Li 	++tests;
349d7eaa29SArthur Chunqi Li 	if (result)
359d7eaa29SArthur Chunqi Li 		printf("PASS: %s\n", name);
369d7eaa29SArthur Chunqi Li 	else {
379d7eaa29SArthur Chunqi Li 		printf("FAIL: %s\n", name);
389d7eaa29SArthur Chunqi Li 		++fails;
399d7eaa29SArthur Chunqi Li 	}
409d7eaa29SArthur Chunqi Li }
419d7eaa29SArthur Chunqi Li 
429d7eaa29SArthur Chunqi Li static int make_vmcs_current(struct vmcs *vmcs)
439d7eaa29SArthur Chunqi Li {
449d7eaa29SArthur Chunqi Li 	bool ret;
459d7eaa29SArthur Chunqi Li 
469d7eaa29SArthur Chunqi Li 	asm volatile ("vmptrld %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc");
479d7eaa29SArthur Chunqi Li 	return ret;
489d7eaa29SArthur Chunqi Li }
499d7eaa29SArthur Chunqi Li 
509d7eaa29SArthur Chunqi Li /* entry_sysenter */
519d7eaa29SArthur Chunqi Li asm(
529d7eaa29SArthur Chunqi Li 	".align	4, 0x90\n\t"
539d7eaa29SArthur Chunqi Li 	".globl	entry_sysenter\n\t"
549d7eaa29SArthur Chunqi Li 	"entry_sysenter:\n\t"
559d7eaa29SArthur Chunqi Li 	SAVE_GPR
569d7eaa29SArthur Chunqi Li 	"	and	$0xf, %rax\n\t"
579d7eaa29SArthur Chunqi Li 	"	mov	%rax, %rdi\n\t"
589d7eaa29SArthur Chunqi Li 	"	call	syscall_handler\n\t"
599d7eaa29SArthur Chunqi Li 	LOAD_GPR
609d7eaa29SArthur Chunqi Li 	"	vmresume\n\t"
619d7eaa29SArthur Chunqi Li );
629d7eaa29SArthur Chunqi Li 
639d7eaa29SArthur Chunqi Li static void __attribute__((__used__)) syscall_handler(u64 syscall_no)
649d7eaa29SArthur Chunqi Li {
659d7eaa29SArthur Chunqi Li 	current->syscall_handler(syscall_no);
669d7eaa29SArthur Chunqi Li }
679d7eaa29SArthur Chunqi Li 
689d7eaa29SArthur Chunqi Li static inline int vmx_on()
699d7eaa29SArthur Chunqi Li {
709d7eaa29SArthur Chunqi Li 	bool ret;
719d7eaa29SArthur Chunqi Li 	asm volatile ("vmxon %1; setbe %0\n\t"
729d7eaa29SArthur Chunqi Li 		: "=q"(ret) : "m"(vmxon_region) : "cc");
739d7eaa29SArthur Chunqi Li 	return ret;
749d7eaa29SArthur Chunqi Li }
759d7eaa29SArthur Chunqi Li 
769d7eaa29SArthur Chunqi Li static inline int vmx_off()
779d7eaa29SArthur Chunqi Li {
789d7eaa29SArthur Chunqi Li 	bool ret;
799d7eaa29SArthur Chunqi Li 	asm volatile("vmxoff; setbe %0\n\t"
809d7eaa29SArthur Chunqi Li 		: "=q"(ret) : : "cc");
819d7eaa29SArthur Chunqi Li 	return ret;
829d7eaa29SArthur Chunqi Li }
839d7eaa29SArthur Chunqi Li 
849d7eaa29SArthur Chunqi Li static void print_vmexit_info()
859d7eaa29SArthur Chunqi Li {
869d7eaa29SArthur Chunqi Li 	u64 guest_rip, guest_rsp;
879d7eaa29SArthur Chunqi Li 	ulong reason = vmcs_read(EXI_REASON) & 0xff;
889d7eaa29SArthur Chunqi Li 	ulong exit_qual = vmcs_read(EXI_QUALIFICATION);
899d7eaa29SArthur Chunqi Li 	guest_rip = vmcs_read(GUEST_RIP);
909d7eaa29SArthur Chunqi Li 	guest_rsp = vmcs_read(GUEST_RSP);
919d7eaa29SArthur Chunqi Li 	printf("VMEXIT info:\n");
929d7eaa29SArthur Chunqi Li 	printf("\tvmexit reason = %d\n", reason);
939d7eaa29SArthur Chunqi Li 	printf("\texit qualification = 0x%x\n", exit_qual);
949d7eaa29SArthur Chunqi Li 	printf("\tBit 31 of reason = %x\n", (vmcs_read(EXI_REASON) >> 31) & 1);
959d7eaa29SArthur Chunqi Li 	printf("\tguest_rip = 0x%llx\n", guest_rip);
969d7eaa29SArthur Chunqi Li 	printf("\tRAX=0x%llx    RBX=0x%llx    RCX=0x%llx    RDX=0x%llx\n",
979d7eaa29SArthur Chunqi Li 		regs.rax, regs.rbx, regs.rcx, regs.rdx);
989d7eaa29SArthur Chunqi Li 	printf("\tRSP=0x%llx    RBP=0x%llx    RSI=0x%llx    RDI=0x%llx\n",
999d7eaa29SArthur Chunqi Li 		guest_rsp, regs.rbp, regs.rsi, regs.rdi);
1009d7eaa29SArthur Chunqi Li 	printf("\tR8 =0x%llx    R9 =0x%llx    R10=0x%llx    R11=0x%llx\n",
1019d7eaa29SArthur Chunqi Li 		regs.r8, regs.r9, regs.r10, regs.r11);
1029d7eaa29SArthur Chunqi Li 	printf("\tR12=0x%llx    R13=0x%llx    R14=0x%llx    R15=0x%llx\n",
1039d7eaa29SArthur Chunqi Li 		regs.r12, regs.r13, regs.r14, regs.r15);
1049d7eaa29SArthur Chunqi Li }
1059d7eaa29SArthur Chunqi Li 
1069d7eaa29SArthur Chunqi Li static void test_vmclear(void)
1079d7eaa29SArthur Chunqi Li {
1089d7eaa29SArthur Chunqi Li 	u64 rflags;
1099d7eaa29SArthur Chunqi Li 
1109d7eaa29SArthur Chunqi Li 	rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
1119d7eaa29SArthur Chunqi Li 	write_rflags(rflags);
1129d7eaa29SArthur Chunqi Li 	report("test vmclear", vmcs_clear(vmcs_root) == 0);
1139d7eaa29SArthur Chunqi Li }
1149d7eaa29SArthur Chunqi Li 
1159d7eaa29SArthur Chunqi Li static void test_vmxoff(void)
1169d7eaa29SArthur Chunqi Li {
1179d7eaa29SArthur Chunqi Li 	int ret;
1189d7eaa29SArthur Chunqi Li 	u64 rflags;
1199d7eaa29SArthur Chunqi Li 
1209d7eaa29SArthur Chunqi Li 	rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
1219d7eaa29SArthur Chunqi Li 	write_rflags(rflags);
1229d7eaa29SArthur Chunqi Li 	ret = vmx_off();
1239d7eaa29SArthur Chunqi Li 	report("test vmxoff", !ret);
1249d7eaa29SArthur Chunqi Li }
1259d7eaa29SArthur Chunqi Li 
1269d7eaa29SArthur Chunqi Li static void __attribute__((__used__)) guest_main(void)
1279d7eaa29SArthur Chunqi Li {
1289d7eaa29SArthur Chunqi Li 	current->guest_main();
1299d7eaa29SArthur Chunqi Li }
1309d7eaa29SArthur Chunqi Li 
1319d7eaa29SArthur Chunqi Li /* guest_entry */
1329d7eaa29SArthur Chunqi Li asm(
1339d7eaa29SArthur Chunqi Li 	".align	4, 0x90\n\t"
1349d7eaa29SArthur Chunqi Li 	".globl	entry_guest\n\t"
1359d7eaa29SArthur Chunqi Li 	"guest_entry:\n\t"
1369d7eaa29SArthur Chunqi Li 	"	call guest_main\n\t"
1379d7eaa29SArthur Chunqi Li 	"	mov $1, %edi\n\t"
1389d7eaa29SArthur Chunqi Li 	"	call hypercall\n\t"
1399d7eaa29SArthur Chunqi Li );
1409d7eaa29SArthur Chunqi Li 
1419d7eaa29SArthur Chunqi Li static void init_vmcs_ctrl(void)
1429d7eaa29SArthur Chunqi Li {
1439d7eaa29SArthur Chunqi Li 	/* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */
1449d7eaa29SArthur Chunqi Li 	/* 26.2.1.1 */
1459d7eaa29SArthur Chunqi Li 	vmcs_write(PIN_CONTROLS, ctrl_pin);
1469d7eaa29SArthur Chunqi Li 	/* Disable VMEXIT of IO instruction */
1479d7eaa29SArthur Chunqi Li 	vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]);
1489d7eaa29SArthur Chunqi Li 	if (ctrl_cpu_rev[0].set & CPU_SECONDARY) {
1499d7eaa29SArthur Chunqi Li 		ctrl_cpu[1] |= ctrl_cpu_rev[1].set & ctrl_cpu_rev[1].clr;
1509d7eaa29SArthur Chunqi Li 		vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1]);
1519d7eaa29SArthur Chunqi Li 	}
1529d7eaa29SArthur Chunqi Li 	vmcs_write(CR3_TARGET_COUNT, 0);
1539d7eaa29SArthur Chunqi Li 	vmcs_write(VPID, ++vpid_cnt);
1549d7eaa29SArthur Chunqi Li }
1559d7eaa29SArthur Chunqi Li 
1569d7eaa29SArthur Chunqi Li static void init_vmcs_host(void)
1579d7eaa29SArthur Chunqi Li {
1589d7eaa29SArthur Chunqi Li 	/* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */
1599d7eaa29SArthur Chunqi Li 	/* 26.2.1.2 */
1609d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_EFER, rdmsr(MSR_EFER));
1619d7eaa29SArthur Chunqi Li 
1629d7eaa29SArthur Chunqi Li 	/* 26.2.1.3 */
1639d7eaa29SArthur Chunqi Li 	vmcs_write(ENT_CONTROLS, ctrl_enter);
1649d7eaa29SArthur Chunqi Li 	vmcs_write(EXI_CONTROLS, ctrl_exit);
1659d7eaa29SArthur Chunqi Li 
1669d7eaa29SArthur Chunqi Li 	/* 26.2.2 */
1679d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_CR0, read_cr0());
1689d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_CR3, read_cr3());
1699d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_CR4, read_cr4());
1709d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SYSENTER_EIP, (u64)(&entry_sysenter));
1719d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SYSENTER_CS,  SEL_KERN_CODE_64);
1729d7eaa29SArthur Chunqi Li 
1739d7eaa29SArthur Chunqi Li 	/* 26.2.3 */
1749d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_CS, SEL_KERN_CODE_64);
1759d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_SS, SEL_KERN_DATA_64);
1769d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_DS, SEL_KERN_DATA_64);
1779d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_ES, SEL_KERN_DATA_64);
1789d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_FS, SEL_KERN_DATA_64);
1799d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_GS, SEL_KERN_DATA_64);
1809d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_SEL_TR, SEL_TSS_RUN);
1819d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_BASE_TR,   (u64)tss_descr);
1829d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_BASE_GDTR, (u64)gdt64_desc);
1839d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_BASE_IDTR, (u64)idt_descr);
1849d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_BASE_FS, 0);
1859d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_BASE_GS, 0);
1869d7eaa29SArthur Chunqi Li 
1879d7eaa29SArthur Chunqi Li 	/* Set other vmcs area */
1889d7eaa29SArthur Chunqi Li 	vmcs_write(PF_ERROR_MASK, 0);
1899d7eaa29SArthur Chunqi Li 	vmcs_write(PF_ERROR_MATCH, 0);
1909d7eaa29SArthur Chunqi Li 	vmcs_write(VMCS_LINK_PTR, ~0ul);
1919d7eaa29SArthur Chunqi Li 	vmcs_write(VMCS_LINK_PTR_HI, ~0ul);
1929d7eaa29SArthur Chunqi Li 	vmcs_write(HOST_RIP, (u64)(&vmx_return));
1939d7eaa29SArthur Chunqi Li }
1949d7eaa29SArthur Chunqi Li 
1959d7eaa29SArthur Chunqi Li static void init_vmcs_guest(void)
1969d7eaa29SArthur Chunqi Li {
1979d7eaa29SArthur Chunqi Li 	/* 26.3 CHECKING AND LOADING GUEST STATE */
1989d7eaa29SArthur Chunqi Li 	ulong guest_cr0, guest_cr4, guest_cr3;
1999d7eaa29SArthur Chunqi Li 	/* 26.3.1.1 */
2009d7eaa29SArthur Chunqi Li 	guest_cr0 = read_cr0();
2019d7eaa29SArthur Chunqi Li 	guest_cr4 = read_cr4();
2029d7eaa29SArthur Chunqi Li 	guest_cr3 = read_cr3();
2039d7eaa29SArthur Chunqi Li 	if (ctrl_enter & ENT_GUEST_64) {
2049d7eaa29SArthur Chunqi Li 		guest_cr0 |= X86_CR0_PG;
2059d7eaa29SArthur Chunqi Li 		guest_cr4 |= X86_CR4_PAE;
2069d7eaa29SArthur Chunqi Li 	}
2079d7eaa29SArthur Chunqi Li 	if ((ctrl_enter & ENT_GUEST_64) == 0)
2089d7eaa29SArthur Chunqi Li 		guest_cr4 &= (~X86_CR4_PCIDE);
2099d7eaa29SArthur Chunqi Li 	if (guest_cr0 & X86_CR0_PG)
2109d7eaa29SArthur Chunqi Li 		guest_cr0 |= X86_CR0_PE;
2119d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_CR0, guest_cr0);
2129d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_CR3, guest_cr3);
2139d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_CR4, guest_cr4);
2149d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SYSENTER_CS,  SEL_KERN_CODE_64);
2159d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SYSENTER_ESP,
2169d7eaa29SArthur Chunqi Li 		(u64)(guest_syscall_stack + PAGE_SIZE - 1));
2179d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SYSENTER_EIP, (u64)(&entry_sysenter));
2189d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_DR7, 0);
2199d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_EFER, rdmsr(MSR_EFER));
2209d7eaa29SArthur Chunqi Li 
2219d7eaa29SArthur Chunqi Li 	/* 26.3.1.2 */
2229d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_CS, SEL_KERN_CODE_64);
2239d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_SS, SEL_KERN_DATA_64);
2249d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_DS, SEL_KERN_DATA_64);
2259d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_ES, SEL_KERN_DATA_64);
2269d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_FS, SEL_KERN_DATA_64);
2279d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_GS, SEL_KERN_DATA_64);
2289d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_TR, SEL_TSS_RUN);
2299d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_SEL_LDTR, 0);
2309d7eaa29SArthur Chunqi Li 
2319d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_CS, 0);
2329d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_ES, 0);
2339d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_SS, 0);
2349d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_DS, 0);
2359d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_FS, 0);
2369d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_GS, 0);
2379d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_TR,   (u64)tss_descr);
2389d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_LDTR, 0);
2399d7eaa29SArthur Chunqi Li 
2409d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_CS, 0xFFFFFFFF);
2419d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_DS, 0xFFFFFFFF);
2429d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_ES, 0xFFFFFFFF);
2439d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_SS, 0xFFFFFFFF);
2449d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_FS, 0xFFFFFFFF);
2459d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_GS, 0xFFFFFFFF);
2469d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_LDTR, 0xffff);
2479d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_TR, ((struct descr *)tss_descr)->limit);
2489d7eaa29SArthur Chunqi Li 
2499d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_CS, 0xa09b);
2509d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_DS, 0xc093);
2519d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_ES, 0xc093);
2529d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_FS, 0xc093);
2539d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_GS, 0xc093);
2549d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_SS, 0xc093);
2559d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_LDTR, 0x82);
2569d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_AR_TR, 0x8b);
2579d7eaa29SArthur Chunqi Li 
2589d7eaa29SArthur Chunqi Li 	/* 26.3.1.3 */
2599d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_GDTR, (u64)gdt64_desc);
2609d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_BASE_IDTR, (u64)idt_descr);
2619d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_GDTR,
2629d7eaa29SArthur Chunqi Li 		((struct descr *)gdt64_desc)->limit & 0xffff);
2639d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_LIMIT_IDTR,
2649d7eaa29SArthur Chunqi Li 		((struct descr *)idt_descr)->limit & 0xffff);
2659d7eaa29SArthur Chunqi Li 
2669d7eaa29SArthur Chunqi Li 	/* 26.3.1.4 */
2679d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_RIP, (u64)(&guest_entry));
2689d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_RSP, (u64)(guest_stack + PAGE_SIZE - 1));
2699d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_RFLAGS, 0x2);
2709d7eaa29SArthur Chunqi Li 
2719d7eaa29SArthur Chunqi Li 	/* 26.3.1.5 */
2729d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_ACTV_STATE, 0);
2739d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_INTR_STATE, 0);
2749d7eaa29SArthur Chunqi Li }
2759d7eaa29SArthur Chunqi Li 
2769d7eaa29SArthur Chunqi Li static int init_vmcs(struct vmcs **vmcs)
2779d7eaa29SArthur Chunqi Li {
2789d7eaa29SArthur Chunqi Li 	*vmcs = alloc_page();
2799d7eaa29SArthur Chunqi Li 	memset(*vmcs, 0, PAGE_SIZE);
2809d7eaa29SArthur Chunqi Li 	(*vmcs)->revision_id = basic.revision;
2819d7eaa29SArthur Chunqi Li 	/* vmclear first to init vmcs */
2829d7eaa29SArthur Chunqi Li 	if (vmcs_clear(*vmcs)) {
2839d7eaa29SArthur Chunqi Li 		printf("%s : vmcs_clear error\n", __func__);
2849d7eaa29SArthur Chunqi Li 		return 1;
2859d7eaa29SArthur Chunqi Li 	}
2869d7eaa29SArthur Chunqi Li 
2879d7eaa29SArthur Chunqi Li 	if (make_vmcs_current(*vmcs)) {
2889d7eaa29SArthur Chunqi Li 		printf("%s : make_vmcs_current error\n", __func__);
2899d7eaa29SArthur Chunqi Li 		return 1;
2909d7eaa29SArthur Chunqi Li 	}
2919d7eaa29SArthur Chunqi Li 
2929d7eaa29SArthur Chunqi Li 	/* All settings to pin/exit/enter/cpu
2939d7eaa29SArthur Chunqi Li 	   control fields should be placed here */
2949d7eaa29SArthur Chunqi Li 	ctrl_pin |= PIN_EXTINT | PIN_NMI | PIN_VIRT_NMI;
2959d7eaa29SArthur Chunqi Li 	ctrl_exit = EXI_LOAD_EFER | EXI_HOST_64;
2969d7eaa29SArthur Chunqi Li 	ctrl_enter = (ENT_LOAD_EFER | ENT_GUEST_64);
2979d7eaa29SArthur Chunqi Li 	ctrl_cpu[0] |= CPU_HLT;
2989d7eaa29SArthur Chunqi Li 	/* DIsable IO instruction VMEXIT now */
2999d7eaa29SArthur Chunqi Li 	ctrl_cpu[0] &= (~(CPU_IO | CPU_IO_BITMAP));
3009d7eaa29SArthur Chunqi Li 	ctrl_cpu[1] = 0;
3019d7eaa29SArthur Chunqi Li 
3029d7eaa29SArthur Chunqi Li 	ctrl_pin = (ctrl_pin | ctrl_pin_rev.set) & ctrl_pin_rev.clr;
3039d7eaa29SArthur Chunqi Li 	ctrl_enter = (ctrl_enter | ctrl_enter_rev.set) & ctrl_enter_rev.clr;
3049d7eaa29SArthur Chunqi Li 	ctrl_exit = (ctrl_exit | ctrl_exit_rev.set) & ctrl_exit_rev.clr;
3059d7eaa29SArthur Chunqi Li 	ctrl_cpu[0] = (ctrl_cpu[0] | ctrl_cpu_rev[0].set) & ctrl_cpu_rev[0].clr;
3069d7eaa29SArthur Chunqi Li 
3079d7eaa29SArthur Chunqi Li 	init_vmcs_ctrl();
3089d7eaa29SArthur Chunqi Li 	init_vmcs_host();
3099d7eaa29SArthur Chunqi Li 	init_vmcs_guest();
3109d7eaa29SArthur Chunqi Li 	return 0;
3119d7eaa29SArthur Chunqi Li }
3129d7eaa29SArthur Chunqi Li 
3139d7eaa29SArthur Chunqi Li static void init_vmx(void)
3149d7eaa29SArthur Chunqi Li {
3159d7eaa29SArthur Chunqi Li 	vmxon_region = alloc_page();
3169d7eaa29SArthur Chunqi Li 	memset(vmxon_region, 0, PAGE_SIZE);
3179d7eaa29SArthur Chunqi Li 
3189d7eaa29SArthur Chunqi Li 	fix_cr0_set =  rdmsr(MSR_IA32_VMX_CR0_FIXED0);
3199d7eaa29SArthur Chunqi Li 	fix_cr0_clr =  rdmsr(MSR_IA32_VMX_CR0_FIXED1);
3209d7eaa29SArthur Chunqi Li 	fix_cr4_set =  rdmsr(MSR_IA32_VMX_CR4_FIXED0);
3219d7eaa29SArthur Chunqi Li 	fix_cr4_clr = rdmsr(MSR_IA32_VMX_CR4_FIXED1);
3229d7eaa29SArthur Chunqi Li 	basic.val = rdmsr(MSR_IA32_VMX_BASIC);
3239d7eaa29SArthur Chunqi Li 	ctrl_pin_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PIN
3249d7eaa29SArthur Chunqi Li 			: MSR_IA32_VMX_PINBASED_CTLS);
3259d7eaa29SArthur Chunqi Li 	ctrl_exit_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_EXIT
3269d7eaa29SArthur Chunqi Li 			: MSR_IA32_VMX_EXIT_CTLS);
3279d7eaa29SArthur Chunqi Li 	ctrl_enter_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_ENTRY
3289d7eaa29SArthur Chunqi Li 			: MSR_IA32_VMX_ENTRY_CTLS);
3299d7eaa29SArthur Chunqi Li 	ctrl_cpu_rev[0].val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PROC
3309d7eaa29SArthur Chunqi Li 			: MSR_IA32_VMX_PROCBASED_CTLS);
3319d7eaa29SArthur Chunqi Li 	if (ctrl_cpu_rev[0].set & CPU_SECONDARY)
3329d7eaa29SArthur Chunqi Li 		ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2);
3339d7eaa29SArthur Chunqi Li 	if (ctrl_cpu_rev[1].set & CPU_EPT || ctrl_cpu_rev[1].set & CPU_VPID)
3349d7eaa29SArthur Chunqi Li 		ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP);
3359d7eaa29SArthur Chunqi Li 
3369d7eaa29SArthur Chunqi Li 	write_cr0((read_cr0() & fix_cr0_clr) | fix_cr0_set);
3379d7eaa29SArthur Chunqi Li 	write_cr4((read_cr4() & fix_cr4_clr) | fix_cr4_set | X86_CR4_VMXE);
3389d7eaa29SArthur Chunqi Li 
3399d7eaa29SArthur Chunqi Li 	*vmxon_region = basic.revision;
3409d7eaa29SArthur Chunqi Li 
3419d7eaa29SArthur Chunqi Li 	guest_stack = alloc_page();
3429d7eaa29SArthur Chunqi Li 	memset(guest_stack, 0, PAGE_SIZE);
3439d7eaa29SArthur Chunqi Li 	guest_syscall_stack = alloc_page();
3449d7eaa29SArthur Chunqi Li 	memset(guest_syscall_stack, 0, PAGE_SIZE);
3459d7eaa29SArthur Chunqi Li }
3469d7eaa29SArthur Chunqi Li 
3479d7eaa29SArthur Chunqi Li static int test_vmx_capability(void)
3489d7eaa29SArthur Chunqi Li {
3499d7eaa29SArthur Chunqi Li 	struct cpuid r;
3509d7eaa29SArthur Chunqi Li 	u64 ret1, ret2;
3519d7eaa29SArthur Chunqi Li 	u64 ia32_feature_control;
3529d7eaa29SArthur Chunqi Li 	r = cpuid(1);
3539d7eaa29SArthur Chunqi Li 	ret1 = ((r.c) >> 5) & 1;
3549d7eaa29SArthur Chunqi Li 	ia32_feature_control = rdmsr(MSR_IA32_FEATURE_CONTROL);
3559d7eaa29SArthur Chunqi Li 	ret2 = ((ia32_feature_control & 0x5) == 0x5);
3569d7eaa29SArthur Chunqi Li 	if ((!ret2) && ((ia32_feature_control & 0x1) == 0)) {
3579d7eaa29SArthur Chunqi Li 		wrmsr(MSR_IA32_FEATURE_CONTROL, 0x5);
3589d7eaa29SArthur Chunqi Li 		ia32_feature_control = rdmsr(MSR_IA32_FEATURE_CONTROL);
3599d7eaa29SArthur Chunqi Li 		ret2 = ((ia32_feature_control & 0x5) == 0x5);
3609d7eaa29SArthur Chunqi Li 	}
3619d7eaa29SArthur Chunqi Li 	report("test vmx capability", ret1 & ret2);
3629d7eaa29SArthur Chunqi Li 	return !(ret1 & ret2);
3639d7eaa29SArthur Chunqi Li }
3649d7eaa29SArthur Chunqi Li 
3659d7eaa29SArthur Chunqi Li static int test_vmxon(void)
3669d7eaa29SArthur Chunqi Li {
3679d7eaa29SArthur Chunqi Li 	int ret;
3689d7eaa29SArthur Chunqi Li 	u64 rflags;
3699d7eaa29SArthur Chunqi Li 
3709d7eaa29SArthur Chunqi Li 	rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
3719d7eaa29SArthur Chunqi Li 	write_rflags(rflags);
3729d7eaa29SArthur Chunqi Li 	ret = vmx_on();
3739d7eaa29SArthur Chunqi Li 	report("test vmxon", !ret);
3749d7eaa29SArthur Chunqi Li 	return ret;
3759d7eaa29SArthur Chunqi Li }
3769d7eaa29SArthur Chunqi Li 
3779d7eaa29SArthur Chunqi Li static void test_vmptrld(void)
3789d7eaa29SArthur Chunqi Li {
3799d7eaa29SArthur Chunqi Li 	u64 rflags;
3809d7eaa29SArthur Chunqi Li 	struct vmcs *vmcs;
3819d7eaa29SArthur Chunqi Li 
3829d7eaa29SArthur Chunqi Li 	vmcs = alloc_page();
3839d7eaa29SArthur Chunqi Li 	vmcs->revision_id = basic.revision;
3849d7eaa29SArthur Chunqi Li 	rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
3859d7eaa29SArthur Chunqi Li 	write_rflags(rflags);
3869d7eaa29SArthur Chunqi Li 	report("test vmptrld", make_vmcs_current(vmcs) == 0);
3879d7eaa29SArthur Chunqi Li }
3889d7eaa29SArthur Chunqi Li 
3899d7eaa29SArthur Chunqi Li static void test_vmptrst(void)
3909d7eaa29SArthur Chunqi Li {
3919d7eaa29SArthur Chunqi Li 	u64 rflags;
3929d7eaa29SArthur Chunqi Li 	int ret;
3939d7eaa29SArthur Chunqi Li 	struct vmcs *vmcs1, *vmcs2;
3949d7eaa29SArthur Chunqi Li 
3959d7eaa29SArthur Chunqi Li 	vmcs1 = alloc_page();
3969d7eaa29SArthur Chunqi Li 	memset(vmcs1, 0, PAGE_SIZE);
3979d7eaa29SArthur Chunqi Li 	init_vmcs(&vmcs1);
3989d7eaa29SArthur Chunqi Li 	rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
3999d7eaa29SArthur Chunqi Li 	write_rflags(rflags);
4009d7eaa29SArthur Chunqi Li 	ret = vmcs_save(&vmcs2);
4019d7eaa29SArthur Chunqi Li 	report("test vmptrst", (!ret) && (vmcs1 == vmcs2));
4029d7eaa29SArthur Chunqi Li }
4039d7eaa29SArthur Chunqi Li 
4049d7eaa29SArthur Chunqi Li /* This function can only be called in guest */
4059d7eaa29SArthur Chunqi Li static void __attribute__((__used__)) hypercall(u32 hypercall_no)
4069d7eaa29SArthur Chunqi Li {
4079d7eaa29SArthur Chunqi Li 	u64 val = 0;
4089d7eaa29SArthur Chunqi Li 	val = (hypercall_no & HYPERCALL_MASK) | HYPERCALL_BIT;
4099d7eaa29SArthur Chunqi Li 	hypercall_field = val;
4109d7eaa29SArthur Chunqi Li 	asm volatile("vmcall\n\t");
4119d7eaa29SArthur Chunqi Li }
4129d7eaa29SArthur Chunqi Li 
4139d7eaa29SArthur Chunqi Li static bool is_hypercall()
4149d7eaa29SArthur Chunqi Li {
4159d7eaa29SArthur Chunqi Li 	ulong reason, hyper_bit;
4169d7eaa29SArthur Chunqi Li 
4179d7eaa29SArthur Chunqi Li 	reason = vmcs_read(EXI_REASON) & 0xff;
4189d7eaa29SArthur Chunqi Li 	hyper_bit = hypercall_field & HYPERCALL_BIT;
4199d7eaa29SArthur Chunqi Li 	if (reason == VMX_VMCALL && hyper_bit)
4209d7eaa29SArthur Chunqi Li 		return true;
4219d7eaa29SArthur Chunqi Li 	return false;
4229d7eaa29SArthur Chunqi Li }
4239d7eaa29SArthur Chunqi Li 
4249d7eaa29SArthur Chunqi Li static int handle_hypercall()
4259d7eaa29SArthur Chunqi Li {
4269d7eaa29SArthur Chunqi Li 	ulong hypercall_no;
4279d7eaa29SArthur Chunqi Li 
4289d7eaa29SArthur Chunqi Li 	hypercall_no = hypercall_field & HYPERCALL_MASK;
4299d7eaa29SArthur Chunqi Li 	hypercall_field = 0;
4309d7eaa29SArthur Chunqi Li 	switch (hypercall_no) {
4319d7eaa29SArthur Chunqi Li 	case HYPERCALL_VMEXIT:
4329d7eaa29SArthur Chunqi Li 		return VMX_TEST_VMEXIT;
4339d7eaa29SArthur Chunqi Li 	default:
4349d7eaa29SArthur Chunqi Li 		printf("ERROR : Invalid hypercall number : %d\n", hypercall_no);
4359d7eaa29SArthur Chunqi Li 	}
4369d7eaa29SArthur Chunqi Li 	return VMX_TEST_EXIT;
4379d7eaa29SArthur Chunqi Li }
4389d7eaa29SArthur Chunqi Li 
4399d7eaa29SArthur Chunqi Li static int exit_handler()
4409d7eaa29SArthur Chunqi Li {
4419d7eaa29SArthur Chunqi Li 	int ret;
4429d7eaa29SArthur Chunqi Li 
4439d7eaa29SArthur Chunqi Li 	current->exits++;
444*1d9284d0SArthur Chunqi Li 	regs.rflags = vmcs_read(GUEST_RFLAGS);
4459d7eaa29SArthur Chunqi Li 	current->guest_regs = regs;
4469d7eaa29SArthur Chunqi Li 	if (is_hypercall())
4479d7eaa29SArthur Chunqi Li 		ret = handle_hypercall();
4489d7eaa29SArthur Chunqi Li 	else
4499d7eaa29SArthur Chunqi Li 		ret = current->exit_handler();
4509d7eaa29SArthur Chunqi Li 	regs = current->guest_regs;
451*1d9284d0SArthur Chunqi Li 	vmcs_write(GUEST_RFLAGS, regs.rflags);
4529d7eaa29SArthur Chunqi Li 	switch (ret) {
4539d7eaa29SArthur Chunqi Li 	case VMX_TEST_VMEXIT:
4549d7eaa29SArthur Chunqi Li 	case VMX_TEST_RESUME:
4559d7eaa29SArthur Chunqi Li 		return ret;
4569d7eaa29SArthur Chunqi Li 	case VMX_TEST_EXIT:
4579d7eaa29SArthur Chunqi Li 		break;
4589d7eaa29SArthur Chunqi Li 	default:
4599d7eaa29SArthur Chunqi Li 		printf("ERROR : Invalid exit_handler return val %d.\n"
4609d7eaa29SArthur Chunqi Li 			, ret);
4619d7eaa29SArthur Chunqi Li 	}
4629d7eaa29SArthur Chunqi Li 	print_vmexit_info();
4639d7eaa29SArthur Chunqi Li 	exit(-1);
4649d7eaa29SArthur Chunqi Li 	return 0;
4659d7eaa29SArthur Chunqi Li }
4669d7eaa29SArthur Chunqi Li 
4679d7eaa29SArthur Chunqi Li static int vmx_run()
4689d7eaa29SArthur Chunqi Li {
4699d7eaa29SArthur Chunqi Li 	u32 ret = 0, fail = 0;
4709d7eaa29SArthur Chunqi Li 
4719d7eaa29SArthur Chunqi Li 	while (1) {
4729d7eaa29SArthur Chunqi Li 		asm volatile (
4739d7eaa29SArthur Chunqi Li 			"mov %%rsp, %%rsi\n\t"
4749d7eaa29SArthur Chunqi Li 			"mov %2, %%rdi\n\t"
4759d7eaa29SArthur Chunqi Li 			"vmwrite %%rsi, %%rdi\n\t"
4769d7eaa29SArthur Chunqi Li 
4779d7eaa29SArthur Chunqi Li 			LOAD_GPR_C
4789d7eaa29SArthur Chunqi Li 			"cmpl $0, %1\n\t"
4799d7eaa29SArthur Chunqi Li 			"jne 1f\n\t"
4809d7eaa29SArthur Chunqi Li 			LOAD_RFLAGS
4819d7eaa29SArthur Chunqi Li 			"vmlaunch\n\t"
4829d7eaa29SArthur Chunqi Li 			"jmp 2f\n\t"
4839d7eaa29SArthur Chunqi Li 			"1: "
4849d7eaa29SArthur Chunqi Li 			"vmresume\n\t"
4859d7eaa29SArthur Chunqi Li 			"2: "
4869d7eaa29SArthur Chunqi Li 			"setbe %0\n\t"
4879d7eaa29SArthur Chunqi Li 			"vmx_return:\n\t"
4889d7eaa29SArthur Chunqi Li 			SAVE_GPR_C
4899d7eaa29SArthur Chunqi Li 			SAVE_RFLAGS
4909d7eaa29SArthur Chunqi Li 			: "=m"(fail)
4919d7eaa29SArthur Chunqi Li 			: "m"(launched), "i"(HOST_RSP)
4929d7eaa29SArthur Chunqi Li 			: "rdi", "rsi", "memory", "cc"
4939d7eaa29SArthur Chunqi Li 
4949d7eaa29SArthur Chunqi Li 		);
4959d7eaa29SArthur Chunqi Li 		if (fail)
4969d7eaa29SArthur Chunqi Li 			ret = launched ? VMX_TEST_RESUME_ERR :
4979d7eaa29SArthur Chunqi Li 				VMX_TEST_LAUNCH_ERR;
4989d7eaa29SArthur Chunqi Li 		else {
4999d7eaa29SArthur Chunqi Li 			launched = 1;
5009d7eaa29SArthur Chunqi Li 			ret = exit_handler();
5019d7eaa29SArthur Chunqi Li 		}
5029d7eaa29SArthur Chunqi Li 		if (ret != VMX_TEST_RESUME)
5039d7eaa29SArthur Chunqi Li 			break;
5049d7eaa29SArthur Chunqi Li 	}
5059d7eaa29SArthur Chunqi Li 	launched = 0;
5069d7eaa29SArthur Chunqi Li 	switch (ret) {
5079d7eaa29SArthur Chunqi Li 	case VMX_TEST_VMEXIT:
5089d7eaa29SArthur Chunqi Li 		return 0;
5099d7eaa29SArthur Chunqi Li 	case VMX_TEST_LAUNCH_ERR:
5109d7eaa29SArthur Chunqi Li 		printf("%s : vmlaunch failed.\n", __func__);
511*1d9284d0SArthur Chunqi Li 		if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & X86_EFLAGS_ZF))
512*1d9284d0SArthur Chunqi Li 			|| ((host_rflags & X86_EFLAGS_CF) && (host_rflags & X86_EFLAGS_ZF)))
5139d7eaa29SArthur Chunqi Li 			printf("\tvmlaunch set wrong flags\n");
5149d7eaa29SArthur Chunqi Li 		report("test vmlaunch", 0);
5159d7eaa29SArthur Chunqi Li 		break;
5169d7eaa29SArthur Chunqi Li 	case VMX_TEST_RESUME_ERR:
5179d7eaa29SArthur Chunqi Li 		printf("%s : vmresume failed.\n", __func__);
518*1d9284d0SArthur Chunqi Li 		if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & X86_EFLAGS_ZF))
519*1d9284d0SArthur Chunqi Li 			|| ((host_rflags & X86_EFLAGS_CF) && (host_rflags & X86_EFLAGS_ZF)))
5209d7eaa29SArthur Chunqi Li 			printf("\tvmresume set wrong flags\n");
5219d7eaa29SArthur Chunqi Li 		report("test vmresume", 0);
5229d7eaa29SArthur Chunqi Li 		break;
5239d7eaa29SArthur Chunqi Li 	default:
5249d7eaa29SArthur Chunqi Li 		printf("%s : unhandled ret from exit_handler, ret=%d.\n", __func__, ret);
5259d7eaa29SArthur Chunqi Li 		break;
5269d7eaa29SArthur Chunqi Li 	}
5279d7eaa29SArthur Chunqi Li 	return 1;
5289d7eaa29SArthur Chunqi Li }
5299d7eaa29SArthur Chunqi Li 
5309d7eaa29SArthur Chunqi Li static int test_run(struct vmx_test *test)
5319d7eaa29SArthur Chunqi Li {
5329d7eaa29SArthur Chunqi Li 	if (test->name == NULL)
5339d7eaa29SArthur Chunqi Li 		test->name = "(no name)";
5349d7eaa29SArthur Chunqi Li 	if (vmx_on()) {
5359d7eaa29SArthur Chunqi Li 		printf("%s : vmxon failed.\n", __func__);
5369d7eaa29SArthur Chunqi Li 		return 1;
5379d7eaa29SArthur Chunqi Li 	}
5389d7eaa29SArthur Chunqi Li 	init_vmcs(&(test->vmcs));
5399d7eaa29SArthur Chunqi Li 	/* Directly call test->init is ok here, init_vmcs has done
5409d7eaa29SArthur Chunqi Li 	   vmcs init, vmclear and vmptrld*/
5419d7eaa29SArthur Chunqi Li 	if (test->init)
5429d7eaa29SArthur Chunqi Li 		test->init(test->vmcs);
5439d7eaa29SArthur Chunqi Li 	test->exits = 0;
5449d7eaa29SArthur Chunqi Li 	current = test;
5459d7eaa29SArthur Chunqi Li 	regs = test->guest_regs;
5469d7eaa29SArthur Chunqi Li 	vmcs_write(GUEST_RFLAGS, regs.rflags | 0x2);
5479d7eaa29SArthur Chunqi Li 	launched = 0;
5489d7eaa29SArthur Chunqi Li 	printf("\nTest suite : %s\n", test->name);
5499d7eaa29SArthur Chunqi Li 	vmx_run();
5509d7eaa29SArthur Chunqi Li 	if (vmx_off()) {
5519d7eaa29SArthur Chunqi Li 		printf("%s : vmxoff failed.\n", __func__);
5529d7eaa29SArthur Chunqi Li 		return 1;
5539d7eaa29SArthur Chunqi Li 	}
5549d7eaa29SArthur Chunqi Li 	return 0;
5559d7eaa29SArthur Chunqi Li }
5569d7eaa29SArthur Chunqi Li 
5579d7eaa29SArthur Chunqi Li static void basic_init()
5589d7eaa29SArthur Chunqi Li {
5599d7eaa29SArthur Chunqi Li }
5609d7eaa29SArthur Chunqi Li 
5619d7eaa29SArthur Chunqi Li static void basic_guest_main()
5629d7eaa29SArthur Chunqi Li {
5639d7eaa29SArthur Chunqi Li 	/* Here is null guest_main, print Hello World */
5649d7eaa29SArthur Chunqi Li 	printf("\tHello World, this is null_guest_main!\n");
5659d7eaa29SArthur Chunqi Li }
5669d7eaa29SArthur Chunqi Li 
5679d7eaa29SArthur Chunqi Li static int basic_exit_handler()
5689d7eaa29SArthur Chunqi Li {
5699d7eaa29SArthur Chunqi Li 	u64 guest_rip;
5709d7eaa29SArthur Chunqi Li 	ulong reason;
5719d7eaa29SArthur Chunqi Li 
5729d7eaa29SArthur Chunqi Li 	guest_rip = vmcs_read(GUEST_RIP);
5739d7eaa29SArthur Chunqi Li 	reason = vmcs_read(EXI_REASON) & 0xff;
5749d7eaa29SArthur Chunqi Li 
5759d7eaa29SArthur Chunqi Li 	switch (reason) {
5769d7eaa29SArthur Chunqi Li 	case VMX_VMCALL:
5779d7eaa29SArthur Chunqi Li 		print_vmexit_info();
5789d7eaa29SArthur Chunqi Li 		vmcs_write(GUEST_RIP, guest_rip + 3);
5799d7eaa29SArthur Chunqi Li 		return VMX_TEST_RESUME;
5809d7eaa29SArthur Chunqi Li 	default:
5819d7eaa29SArthur Chunqi Li 		break;
5829d7eaa29SArthur Chunqi Li 	}
5839d7eaa29SArthur Chunqi Li 	printf("ERROR : Unhandled vmx exit.\n");
5849d7eaa29SArthur Chunqi Li 	print_vmexit_info();
5859d7eaa29SArthur Chunqi Li 	return VMX_TEST_EXIT;
5869d7eaa29SArthur Chunqi Li }
5879d7eaa29SArthur Chunqi Li 
5889d7eaa29SArthur Chunqi Li static void basic_syscall_handler(u64 syscall_no)
5899d7eaa29SArthur Chunqi Li {
5909d7eaa29SArthur Chunqi Li }
5919d7eaa29SArthur Chunqi Li 
5929d7eaa29SArthur Chunqi Li static void vmenter_main()
5939d7eaa29SArthur Chunqi Li {
5949d7eaa29SArthur Chunqi Li 	u64 rax;
5959d7eaa29SArthur Chunqi Li 	u64 rsp, resume_rsp;
5969d7eaa29SArthur Chunqi Li 
5979d7eaa29SArthur Chunqi Li 	report("test vmlaunch", 1);
5989d7eaa29SArthur Chunqi Li 
5999d7eaa29SArthur Chunqi Li 	asm volatile(
6009d7eaa29SArthur Chunqi Li 		"mov %%rsp, %0\n\t"
6019d7eaa29SArthur Chunqi Li 		"mov %3, %%rax\n\t"
6029d7eaa29SArthur Chunqi Li 		"vmcall\n\t"
6039d7eaa29SArthur Chunqi Li 		"mov %%rax, %1\n\t"
6049d7eaa29SArthur Chunqi Li 		"mov %%rsp, %2\n\t"
6059d7eaa29SArthur Chunqi Li 		: "=r"(rsp), "=r"(rax), "=r"(resume_rsp)
6069d7eaa29SArthur Chunqi Li 		: "g"(0xABCD));
6079d7eaa29SArthur Chunqi Li 	report("test vmresume", (rax == 0xFFFF) && (rsp == resume_rsp));
6089d7eaa29SArthur Chunqi Li }
6099d7eaa29SArthur Chunqi Li 
6109d7eaa29SArthur Chunqi Li static int vmenter_exit_handler()
6119d7eaa29SArthur Chunqi Li {
6129d7eaa29SArthur Chunqi Li 	u64 guest_rip;
6139d7eaa29SArthur Chunqi Li 	ulong reason;
6149d7eaa29SArthur Chunqi Li 
6159d7eaa29SArthur Chunqi Li 	guest_rip = vmcs_read(GUEST_RIP);
6169d7eaa29SArthur Chunqi Li 	reason = vmcs_read(EXI_REASON) & 0xff;
6179d7eaa29SArthur Chunqi Li 	switch (reason) {
6189d7eaa29SArthur Chunqi Li 	case VMX_VMCALL:
6199d7eaa29SArthur Chunqi Li 		if (current->guest_regs.rax != 0xABCD) {
6209d7eaa29SArthur Chunqi Li 			report("test vmresume", 0);
6219d7eaa29SArthur Chunqi Li 			return VMX_TEST_VMEXIT;
6229d7eaa29SArthur Chunqi Li 		}
6239d7eaa29SArthur Chunqi Li 		current->guest_regs.rax = 0xFFFF;
6249d7eaa29SArthur Chunqi Li 		vmcs_write(GUEST_RIP, guest_rip + 3);
6259d7eaa29SArthur Chunqi Li 		return VMX_TEST_RESUME;
6269d7eaa29SArthur Chunqi Li 	default:
6279d7eaa29SArthur Chunqi Li 		report("test vmresume", 0);
6289d7eaa29SArthur Chunqi Li 		print_vmexit_info();
6299d7eaa29SArthur Chunqi Li 	}
6309d7eaa29SArthur Chunqi Li 	return VMX_TEST_VMEXIT;
6319d7eaa29SArthur Chunqi Li }
6329d7eaa29SArthur Chunqi Li 
6339d7eaa29SArthur Chunqi Li 
6349d7eaa29SArthur Chunqi Li /* name/init/guest_main/exit_handler/syscall_handler/guest_regs
6359d7eaa29SArthur Chunqi Li    basic_* just implement some basic functions */
6369d7eaa29SArthur Chunqi Li static struct vmx_test vmx_tests[] = {
6379d7eaa29SArthur Chunqi Li 	{ "null", basic_init, basic_guest_main, basic_exit_handler,
6389d7eaa29SArthur Chunqi Li 		basic_syscall_handler, {0} },
6399d7eaa29SArthur Chunqi Li 	{ "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
6409d7eaa29SArthur Chunqi Li 		basic_syscall_handler, {0} },
6419d7eaa29SArthur Chunqi Li };
6429d7eaa29SArthur Chunqi Li 
6439d7eaa29SArthur Chunqi Li int main(void)
6449d7eaa29SArthur Chunqi Li {
6459d7eaa29SArthur Chunqi Li 	int i;
6469d7eaa29SArthur Chunqi Li 
6479d7eaa29SArthur Chunqi Li 	setup_vm();
6489d7eaa29SArthur Chunqi Li 	setup_idt();
6499d7eaa29SArthur Chunqi Li 
6509d7eaa29SArthur Chunqi Li 	if (test_vmx_capability() != 0) {
6519d7eaa29SArthur Chunqi Li 		printf("ERROR : vmx not supported, check +vmx option\n");
6529d7eaa29SArthur Chunqi Li 		goto exit;
6539d7eaa29SArthur Chunqi Li 	}
6549d7eaa29SArthur Chunqi Li 	init_vmx();
6559d7eaa29SArthur Chunqi Li 	/* Set basic test ctxt the same as "null" */
6569d7eaa29SArthur Chunqi Li 	current = &vmx_tests[0];
6579d7eaa29SArthur Chunqi Li 	if (test_vmxon() != 0)
6589d7eaa29SArthur Chunqi Li 		goto exit;
6599d7eaa29SArthur Chunqi Li 	test_vmptrld();
6609d7eaa29SArthur Chunqi Li 	test_vmclear();
6619d7eaa29SArthur Chunqi Li 	test_vmptrst();
6629d7eaa29SArthur Chunqi Li 	init_vmcs(&vmcs_root);
6639d7eaa29SArthur Chunqi Li 	if (vmx_run()) {
6649d7eaa29SArthur Chunqi Li 		report("test vmlaunch", 0);
6659d7eaa29SArthur Chunqi Li 		goto exit;
6669d7eaa29SArthur Chunqi Li 	}
6679d7eaa29SArthur Chunqi Li 	test_vmxoff();
6689d7eaa29SArthur Chunqi Li 
6699d7eaa29SArthur Chunqi Li 	for (i = 1; i < ARRAY_SIZE(vmx_tests); ++i) {
6709d7eaa29SArthur Chunqi Li 		if (test_run(&vmx_tests[i]))
6719d7eaa29SArthur Chunqi Li 			goto exit;
6729d7eaa29SArthur Chunqi Li 	}
6739d7eaa29SArthur Chunqi Li 
6749d7eaa29SArthur Chunqi Li exit:
6759d7eaa29SArthur Chunqi Li 	printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
6769d7eaa29SArthur Chunqi Li 	return fails ? 1 : 0;
6779d7eaa29SArthur Chunqi Li }
678