On Mon, Sep 9, 2013 at 9:56 PM, Paolo Bonzini <pbonzini@xxxxxxxxxx> wrote: > Il 09/09/2013 06:57, Arthur Chunqi Li ha scritto: >> 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; > > Why volatile? Because init_fail is only set but not used later in ept_init(), and if I don't add volatile, compiler may optimize the setting to init_fail. This occasion firstly occurred when I write set_stage/get_stage. If one variant is set in a function but not used later, the compiler usually optimizes this setting as redundant assignment and remove it. Arthur > > The patch looks good. > >> +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} }, >> }; >> > -- 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