Message ID | 1378702644-23655-3-git-send-email-yzt356@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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@gmail.com> > --- > 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? 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Sep 9, 2013 at 9:56 PM, Paolo Bonzini <pbonzini@redhat.com> 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@gmail.com> >> --- >> 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Il 09/09/2013 16:11, Arthur Chunqi Li ha scritto: >>> >> +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. No, the two are different. "stage" is written several times in the same function, with no code in the middle: stage++; *p = 1; stage++; To the compiler, the first store is dead. The compiler doesn't know that "*p = 1" traps to the hypervisor. But this is not the case for "init_fail". Paolo -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Sep 9, 2013 at 12:57 PM, Arthur Chunqi Li <yzt356@gmail.com> wrote: > 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@gmail.com> > --- > 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); Hi Paolo, I noticed another possible bug of this patch. Stage 4 of this patch test the scenario that the page of a paging structure is not present, then this will cause EPT violation vmexit with bit 8 of exit_qual unset. My question is: will instruction length be correctly set on this scenario? I got wrong insn_len in "case 4" of VMX_EPT_VIOLATION, which may cause triple fault vmexit. > + 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Il 09/09/2013 17:29, Arthur Chunqi Li ha scritto: > Hi Paolo, > I noticed another possible bug of this patch. Stage 4 of this patch > test the scenario that the page of a paging structure is not present, > then this will cause EPT violation vmexit with bit 8 of exit_qual > unset. My question is: will instruction length be correctly set on > this scenario? I got wrong insn_len in "case 4" of VMX_EPT_VIOLATION, > which may cause triple fault vmexit. It's plausible that the instruction length is wrong, since the processor might be fetching the instruction itself and doesn't know the length. Paolo -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
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} }, };
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@gmail.com> --- x86/vmx_tests.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+)