@@ -400,6 +400,7 @@
#define MSR_IA32_VMX_TRUE_PROC 0x0000048e
#define MSR_IA32_VMX_TRUE_EXIT 0x0000048f
#define MSR_IA32_VMX_TRUE_ENTRY 0x00000490
+#define MSR_IA32_VMX_VMFUNC 0x00000491
#define MSR_IA32_TSCDEADLINE 0x000006e0
@@ -138,11 +138,14 @@ enum Encoding {
TSC_OFFSET_HI = 0x2011ul,
APIC_VIRT_ADDR = 0x2012ul,
APIC_ACCS_ADDR = 0x2014ul,
+ VMFUNC_CTRL = 0x2018ul,
+ VMFUNC_CTRL_HI = 0x2019ul,
EPTP = 0x201aul,
EPTP_HI = 0x201bul,
PMLADDR = 0x200eul,
PMLADDR_HI = 0x200ful,
-
+ EPTP_LIST_ADDR = 0x2024ul,
+ EPTP_LIST_ADDR_HI = 0x2025ul,
/* 64-Bit Readonly Data Field */
INFO_PHYS_ADDR = 0x2400ul,
@@ -394,6 +397,7 @@ enum Ctrl1 {
CPU_URG = 1ul << 7,
CPU_WBINVD = 1ul << 6,
CPU_RDRAND = 1ul << 11,
+ CPU_VMFUNC = 1ul << 13,
CPU_PML = 1ul << 17,
};
@@ -405,6 +409,8 @@ enum Intr_type {
VMX_INTR_TYPE_SOFT_EXCEPTION = 6,
};
+#define EPTP_SWITCHING 0x1
+
/*
* Interruption-information format
*/
@@ -29,6 +29,9 @@ void *data_page1, *data_page2;
void *pml_log;
#define PML_INDEX 512
+void *vpage, *hpage1, *hpage2;
+void *eptp_list;
+
static inline unsigned ffs(unsigned x)
{
int pos = -1;
@@ -1332,6 +1335,148 @@ static int eptad_init()
return r;
}
+static int vmfunc_init()
+{
+ eptp_list = alloc_page();
+ u64 *buf = eptp_list;
+
+ vpage = alloc_page();
+ hpage1 = alloc_page();
+ hpage2 = alloc_page();
+
+ *((u32 *)vpage) = 0x1;
+ *((u32 *)hpage1) = 0x2;
+ *((u32 *)hpage2) = 0x3;
+
+ ept_init_common(false);
+ buf[0] = eptp;
+ install_ept(pml4, (unsigned long)hpage1, (unsigned long)vpage,
+ EPT_RA | EPT_WA | EPT_EA);
+
+ ept_init_common(false);
+ buf[1] = eptp;
+ install_ept(pml4, (unsigned long)hpage2, (unsigned long)vpage,
+ EPT_RA | EPT_WA | EPT_EA);
+
+ if (!(ctrl_cpu_rev[1].clr & CPU_VMFUNC)) {
+ printf("\tVMFUNC is not supported");
+ return VMX_TEST_EXIT;
+ }
+
+ return VMX_TEST_START;
+}
+
+static void vmfunc_ud_handler(struct ex_regs *regs)
+{
+ switch(vmx_get_test_stage()) {
+ case 0:
+ case 3:
+ regs->rip += 3;
+ vmx_inc_test_stage();
+ vmx_inc_test_stage();
+ break;
+ default:
+ printf("Unexpected #UD hit!\n");
+ print_vmexit_info();
+ exit(0);
+ }
+}
+
+static void vmfunc_main()
+{
+ int nr = 0, ept = 0;
+
+ handle_exception(UD_VECTOR, vmfunc_ud_handler);
+ vmx_set_test_stage(0);
+
+ /* Call vmfunc when L1 hasn't enabled it' */
+ asm volatile("vmfunc"
+ :
+ : "a"(nr), "c"(ept)
+ : "memory");
+
+ report("VMFUNC - Disabled VMFUNC in sec controls causes #UD",
+ vmx_get_test_stage() == 2);
+
+ vmcall();
+ /* function > 64 causes #UD */
+ nr = 64;
+ asm volatile("vmfunc"
+ :
+ : "a"(nr), "c"(ept)
+ : "memory");
+ report("VMFUNC - guest call with EAX > 63 causes #UD",
+ vmx_get_test_stage() == 5);
+
+ /* invalid vm function causes vmexit */
+ nr = 1;
+ asm volatile("vmfunc"
+ :
+ : "a"(nr), "c"(ept)
+ : "memory");
+ report("VMFUNC - guest call with non-existant function",
+ vmx_get_test_stage() == 6);
+
+ /* invalid entry from the eptp list causes vmexit */
+ nr = 0;
+ ept = 512;
+ asm volatile("vmfunc"
+ :
+ : "a"(nr), "c"(ept)
+ : "memory");
+ report("VMFUNC - guest call with invalid entry in eptp list",
+ vmx_get_test_stage() == 7);
+
+ /*
+ * vpage points to hpage2 with value 0x3
+ * vmfunc with ecx = 0 should make vpage
+ * point to 0x2
+ */
+ if (*((u32 *)vpage) != 0x3) {
+ printf("Guest address points to unexpected page\n");
+ exit(0);
+ }
+ ept = 0;
+ asm volatile("vmfunc"
+ :
+ : "a"(nr), "c"(ept)
+ : "memory");
+ report("VMFUNC - call to switch ept pointer without an exit",
+ (vmx_get_test_stage() == 7) && (*((u32 *)vpage) == 0x2));
+}
+
+static int vmfunc_exit_handler(void)
+{
+ ulong reason = vmcs_read(EXI_REASON) & 0xff;
+ u64 guest_rip = vmcs_read(GUEST_RIP);
+ u32 insn_len = vmcs_read(EXI_INST_LEN);
+ u32 ctrl_cpu;
+ u64 controls;
+
+ switch (reason) {
+ case VMX_VMCALL:
+ ctrl_cpu = vmcs_read(CPU_EXEC_CTRL1) | CPU_VMFUNC;
+ vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu);
+
+ controls = rdmsr(MSR_IA32_VMX_VMFUNC) & 1;
+ if (!(controls & EPTP_SWITCHING)) {
+ printf("\tEPTP switching is not supported");
+ return VMX_TEST_EXIT;
+ }
+
+ vmcs_write(EPTP_LIST_ADDR, (u64)eptp_list);
+ vmcs_write(VMFUNC_CTRL, controls);
+ case VMX_VMFUNC:
+ vmx_inc_test_stage();
+ vmcs_write(GUEST_RIP, guest_rip + insn_len);
+ return VMX_TEST_RESUME;
+ default:
+ report("Unknown exit reason, %ld", false, reason);
+ print_vmexit_info();
+ }
+ return VMX_TEST_VMEXIT;
+}
+
static int pml_init()
{
u32 ctrl_cpu;
@@ -3173,6 +3318,7 @@ struct vmx_test vmx_tests[] = {
disable_rdtscp_exit_handler, NULL, {0} },
{ "int3", int3_init, int3_guest_main, int3_exit_handler, NULL, {0} },
{ "into", into_init, into_guest_main, into_exit_handler, NULL, {0} },
+ { "vmfunc", vmfunc_init, vmfunc_main, vmfunc_exit_handler, NULL, {0} },
{ "exit_monitor_from_l2_test", NULL, exit_monitor_from_l2_main,
exit_monitor_from_l2_handler, NULL, {0} },
/* Basic V2 tests. */
Besides for the obvious vmfunc call in L2 for eptp switching, other tests are to check for valid error paths when L2 calls vmfunc when L1 hasn't enabled it in the secondary controls, L2 calls vmfunc with an invalid function number or calls it with an invalid field from the eptp list Signed-off-by: Bandan Das <bsd@redhat.com> --- v2: Use eptp instead of pml4 in the eptp_list buffer because That would have the necessary bits set for eptp validity lib/x86/msr.h | 1 + x86/vmx.h | 8 +++- x86/vmx_tests.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-)