diff mbox

[kvm-unit-tests] vmx_tests: Add vmfunc tests

Message ID jpgr2xtkvjr.fsf@linux.bootlegged.copy (mailing list archive)
State New, archived
Headers show

Commit Message

Bandan Das July 6, 2017, 11:14 p.m. UTC
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>
---
 lib/x86/msr.h   |   1 +
 x86/vmx.h       |   8 +++-
 x86/vmx_tests.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+), 1 deletion(-)

Comments

Paolo Bonzini July 7, 2017, 8:46 a.m. UTC | #1
On 07/07/2017 01:14, Bandan Das wrote:
> 
> 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>
> ---
>  lib/x86/msr.h   |   1 +
>  x86/vmx.h       |   8 +++-
>  x86/vmx_tests.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 154 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/x86/msr.h b/lib/x86/msr.h
> index 2c0598c..41f75d9 100644
> --- a/lib/x86/msr.h
> +++ b/lib/x86/msr.h
> @@ -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
>  
> diff --git a/x86/vmx.h b/x86/vmx.h
> index d12b9de..b424674 100644
> --- a/x86/vmx.h
> +++ b/x86/vmx.h
> @@ -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
>   */
> diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
> index 2522d3a..01c58cc 100644
> --- a/x86/vmx_tests.c
> +++ b/x86/vmx_tests.c
> @@ -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] = (u64)pml4;
> +	install_ept(pml4, (unsigned long)hpage1, (unsigned long)vpage,
> +		    EPT_RA | EPT_WA | EPT_EA);
> +
> +	ept_init_common(false);
> +	buf[1] = (u64)pml4;
> +	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. */
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
diff mbox

Patch

diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index 2c0598c..41f75d9 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -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
 
diff --git a/x86/vmx.h b/x86/vmx.h
index d12b9de..b424674 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -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
  */
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 2522d3a..01c58cc 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -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] = (u64)pml4;
+	install_ept(pml4, (unsigned long)hpage1, (unsigned long)vpage,
+		    EPT_RA | EPT_WA | EPT_EA);
+
+	ept_init_common(false);
+	buf[1] = (u64)pml4;
+	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. */