Some test cases for nested EPT features, including: 1. EPT basic framework tests: read, write and remap. 2. EPT misconfigurations test cases: page permission mieconfiguration and memory type misconfiguration 3. EPT violations test cases: page permission violation and paging structure violation Signed-off-by: Arthur Chunqi Li <yzt356@xxxxxxxxx> --- x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index c1b39f4..a0b9824 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -1,4 +1,36 @@ #include "vmx.h" +#include "processor.h" +#include "vm.h" +#include "msr.h" +#include "fwcfg.h" + +volatile u32 stage; +volatile bool init_fail; +unsigned long *pml4; +u64 eptp; +void *data_page1, *data_page2; + +static inline void set_stage(u32 s) +{ + barrier(); + stage = s; + barrier(); +} + +static inline u32 get_stage() +{ + u32 s; + + barrier(); + s = stage; + barrier(); + return s; +} + +static inline void vmcall() +{ + asm volatile ("vmcall"); +} void basic_init() { @@ -76,6 +108,238 @@ int vmenter_exit_handler() return VMX_TEST_VMEXIT; } +static int setup_ept() +{ + int support_2m; + unsigned long end_of_memory; + + if (!(ept_vpid.val & EPT_CAP_UC) && + !(ept_vpid.val & EPT_CAP_WB)) { + printf("\tEPT paging-structure memory type " + "UC&WB are not supported\n"); + return 1; + } + if (ept_vpid.val & EPT_CAP_UC) + eptp = EPT_MEM_TYPE_UC; + else + eptp = EPT_MEM_TYPE_WB; + if (!(ept_vpid.val & EPT_CAP_PWL4)) { + printf("\tPWL4 is not supported\n"); + return 1; + } + eptp |= (3 << EPTP_PG_WALK_LEN_SHIFT); + pml4 = alloc_page(); + memset(pml4, 0, PAGE_SIZE); + eptp |= virt_to_phys(pml4); + vmcs_write(EPTP, eptp); + support_2m = !!(ept_vpid.val & EPT_CAP_2M_PAGE); + end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); + if (end_of_memory < (1ul << 32)) + end_of_memory = (1ul << 32); + if (setup_ept_range(pml4, 0, end_of_memory, + 0, support_2m, EPT_WA | EPT_RA | EPT_EA)) { + printf("\tSet ept tables failed.\n"); + return 1; + } + return 0; +} + +static void ept_init() +{ + u32 ctrl_cpu[2]; + + init_fail = false; + ctrl_cpu[0] = vmcs_read(CPU_EXEC_CTRL0); + ctrl_cpu[1] = vmcs_read(CPU_EXEC_CTRL1); + ctrl_cpu[0] = (ctrl_cpu[0] | CPU_SECONDARY) + & ctrl_cpu_rev[0].clr; + ctrl_cpu[1] = (ctrl_cpu[1] | CPU_EPT) + & ctrl_cpu_rev[1].clr; + vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]); + vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1] | CPU_EPT); + if (setup_ept()) + init_fail = true; + data_page1 = alloc_page(); + data_page2 = alloc_page(); + memset(data_page1, 0x0, PAGE_SIZE); + memset(data_page2, 0x0, PAGE_SIZE); + *((u32 *)data_page1) = MAGIC_VAL_1; + *((u32 *)data_page2) = MAGIC_VAL_2; + install_ept(pml4, (unsigned long)data_page1, (unsigned long)data_page2, + EPT_RA | EPT_WA | EPT_EA); +} + +static void ept_main() +{ + if (init_fail) + return; + if (!(ctrl_cpu_rev[0].clr & CPU_SECONDARY) + && !(ctrl_cpu_rev[1].clr & CPU_EPT)) { + printf("\tEPT is not supported"); + return; + } + set_stage(0); + if (*((u32 *)data_page2) != MAGIC_VAL_1 && + *((u32 *)data_page1) != MAGIC_VAL_1) + report("EPT basic framework - read", 0); + else { + *((u32 *)data_page2) = MAGIC_VAL_3; + vmcall(); + if (get_stage() == 1) { + if (*((u32 *)data_page1) == MAGIC_VAL_3 && + *((u32 *)data_page2) == MAGIC_VAL_2) + report("EPT basic framework", 1); + else + report("EPT basic framework - remap", 1); + } + } + // Test EPT Misconfigurations + set_stage(1); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() != 2) { + report("EPT misconfigurations", 0); + goto t1; + } + set_stage(2); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() != 3) { + report("EPT misconfigurations", 0); + goto t1; + } + report("EPT misconfigurations", 1); +t1: + // Test EPT violation + set_stage(3); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_1; + if (get_stage() == 4) + report("EPT violation - page permission", 1); + else + report("EPT violation - page permission", 0); + // Violation caused by EPT paging structure + set_stage(4); + vmcall(); + *((u32 *)data_page1) = MAGIC_VAL_2; + if (get_stage() == 5) + report("EPT violation - paging structure", 1); + else + report("EPT violation - paging structure", 0); + return; +} + +static int ept_exit_handler() +{ + u64 guest_rip; + ulong reason; + u32 insn_len; + u32 exit_qual; + static unsigned long data_page1_pte, data_page1_pte_pte; + + guest_rip = vmcs_read(GUEST_RIP); + reason = vmcs_read(EXI_REASON) & 0xff; + insn_len = vmcs_read(EXI_INST_LEN); + exit_qual = vmcs_read(EXI_QUALIFICATION); + switch (reason) { + case VMX_VMCALL: + switch (get_stage()) { + case 0: + if (*((u32 *)data_page1) == MAGIC_VAL_3 && + *((u32 *)data_page2) == MAGIC_VAL_2) { + set_stage(get_stage() + 1); + install_ept(pml4, (unsigned long)data_page2, + (unsigned long)data_page2, + EPT_RA | EPT_WA | EPT_EA); + } else + report("EPT basic framework - write\n", 0); + break; + case 1: + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, EPT_WA); + invept(INVEPT_SINGLE, eptp); + break; + case 2: + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, + EPT_RA | EPT_WA | EPT_EA | + (2 << EPT_MEM_TYPE_SHIFT)); + invept(INVEPT_SINGLE, eptp); + break; + case 3: + data_page1_pte = get_ept_pte(pml4, + (unsigned long)data_page1, 1); + set_ept_pte(pml4, (unsigned long)data_page1, + 1, data_page1_pte & (~EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + case 4: + data_page1_pte = get_ept_pte(pml4, + (unsigned long)data_page1, 2); + data_page1_pte &= PAGE_MASK; + data_page1_pte_pte = get_ept_pte(pml4, data_page1_pte, 2); + set_ept_pte(pml4, data_page1_pte, 2, + data_page1_pte_pte & (~EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + // Should not reach here + default: + printf("ERROR - unknown stage, %d.\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EPT_MISCONFIG: + switch (get_stage()) { + case 1: + case 2: + set_stage(get_stage() + 1); + install_ept(pml4, (unsigned long)data_page1, + (unsigned long)data_page1, + EPT_RA | EPT_WA | EPT_EA); + invept(INVEPT_SINGLE, eptp); + break; + // Should not reach here + default: + printf("ERROR - unknown stage, %d.\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + case VMX_EPT_VIOLATION: + switch(get_stage()) { + case 3: + if (exit_qual == (EPT_VLT_WR | EPT_VLT_LADDR_VLD | + EPT_VLT_PADDR)) + set_stage(get_stage() + 1); + set_ept_pte(pml4, (unsigned long)data_page1, + 1, data_page1_pte | (EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + case 4: + if (exit_qual == (EPT_VLT_RD | EPT_VLT_LADDR_VLD)) + set_stage(get_stage() + 1); + set_ept_pte(pml4, data_page1_pte, 2, + data_page1_pte_pte | (EPT_PRESENT)); + invept(INVEPT_SINGLE, eptp); + break; + default: + // Should not reach here + printf("ERROR : unknown stage, %d\n", get_stage()); + print_vmexit_info(); + return VMX_TEST_VMEXIT; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + default: + printf("Unknown exit reason, %d\n", reason); + print_vmexit_info(); + } + return VMX_TEST_VMEXIT; +} + /* name/init/guest_main/exit_handler/syscall_handler/guest_regs basic_* just implement some basic functions */ struct vmx_test vmx_tests[] = { @@ -83,5 +347,7 @@ struct vmx_test vmx_tests[] = { basic_syscall_handler, {0} }, { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, basic_syscall_handler, {0} }, + { "EPT framework", ept_init, ept_main, ept_exit_handler, + basic_syscall_handler, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, }; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html