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 <[email protected]>
---
 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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to