diff mbox series

[kvm-unit-tests] Add VMX INS/OUTS test

Message ID 20190111001159.106695-1-jmattson@google.com (mailing list archive)
State New, archived
Headers show
Series [kvm-unit-tests] Add VMX INS/OUTS test | expand

Commit Message

Jim Mattson Jan. 11, 2019, 12:11 a.m. UTC
Test that all of the appropriate exit information is provided to L1
for INS and OUTS instructions intercepted in L2.

Signed-off-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Marc Orr <marcorr@google.com>
---
 lib/x86/msr.h     |  3 ++
 x86/unittests.cfg |  6 +++
 x86/vmx.h         | 17 ++++++++
 x86/vmx_tests.c   | 98 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 124 insertions(+)
diff mbox series

Patch

diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index abf0d93bc58d..d0f3e79b2d37 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -405,6 +405,9 @@ 
 #define MSR_IA32_VMX_TRUE_EXIT		0x0000048f
 #define MSR_IA32_VMX_TRUE_ENTRY		0x00000490
 
+/* MSR_IA32_VMX_BASIC bits */
+#define MSR_IA32_VMX_BASIC_INOUT (1ULL << 54)
+
 /* MSR_IA32_VMX_MISC bits */
 #define MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS	(1ULL << 29)
 
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index c744a6d8b163..d7270a542751 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -536,6 +536,12 @@  extra_params = -cpu host,+vmx -m 2560 -append invvpid_test_v2
 arch = x86_64
 groups = vmx
 
+[vmx_ins_outs_test]
+file = vmx.flat
+extra_params = -cpu host,+vmx -m 2560 -append vmx_ins_outs_test
+arch = x86_64
+groups = vmx
+
 [vmx_controls]
 file = vmx.flat
 extra_params = -cpu host,+vmx -m 2560 -append vmx_controls_test
diff --git a/x86/vmx.h b/x86/vmx.h
index ba47455e1593..4bd8827a28df 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -7,6 +7,15 @@ 
 #include "asm/page.h"
 #include "asm/io.h"
 
+enum vmx_segment {
+       VMX_SEG_ES = 0,
+       VMX_SEG_CS = 1,
+       VMX_SEG_SS = 2,
+       VMX_SEG_DS = 3,
+       VMX_SEG_FS = 4,
+       VMX_SEG_GS = 5
+};
+
 struct vmcs_hdr {
 	u32 revision_id:31;
 	u32 shadow_vmcs:1;
@@ -539,6 +548,14 @@  enum vm_instruction_error_number {
 #define VMX_IO_PORT_MASK	0xFFFF0000
 #define VMX_IO_PORT_SHIFT	16
 
+#define VMX_IO_ADDR_SIZE_MASK  0x380
+#define VMX_IO_ADDR_SIZE_SHIFT 7
+#define _VMX_IO_ADDR_16                0
+#define _VMX_IO_ADDR_32                1
+#define _VMX_IO_ADDR_64                2
+#define VMX_IO_SEGMENT_MASK    0x38000
+#define VMX_IO_SEGMENT_SHIFT   15
+
 #define VMX_TEST_START		0
 #define VMX_TEST_VMEXIT		1
 #define VMX_TEST_EXIT		2
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 7a8d6ce5f169..f96fcc4246bf 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -3922,6 +3922,103 @@  static void test_apic_ctls(void)
 	test_posted_intr();
 }
 
+static void ins_outs_test_guest(void)
+{
+	asm ("rep;insb" : : "d" (1));
+	asm ("rep;outsb" : : "d" (2));
+	asm ("insw" : : "d" (3));
+	asm ("fs outsw" : : "d" (4));
+	asm ("addr32 insl" : : "d" (5));
+	asm ("addr32 gs outsl" : : "d" (6));
+}
+
+static void check_io_exit(unsigned size, unsigned in, unsigned string,
+			  unsigned rep, unsigned imm, u16 port,
+			  unsigned addr_size, enum vmx_segment segment)
+{
+	u32 reason = vmcs_read(EXI_REASON);
+	u64 exit_qual = vmcs_read(EXI_QUALIFICATION);
+	u32 inst_info = vmcs_read(EXI_INST_INFO);
+
+	report("Exit reason (%u) is I/O instruction", reason == VMX_IO,
+	       reason);
+	report("Size (%lu) is %u", (exit_qual & VMX_IO_SIZE_MASK) == size,
+	       (exit_qual & VMX_IO_SIZE_MASK), size);
+	report("In (%lu) is %u", (exit_qual & VMX_IO_DIRECTION_MASK) == in,
+	       (exit_qual & VMX_IO_DIRECTION_MASK), in);
+	report("String (%lu) is %u", (exit_qual & VMX_IO_STRING) == string,
+	       (exit_qual & VMX_IO_STRING), string);
+	report("Rep (%lu) is %u", (exit_qual & VMX_IO_REP) == rep,
+	       (exit_qual & VMX_IO_REP), rep);
+	report("Immediate (%lu) is %u",
+	       (exit_qual & VMX_IO_OPRAND_IMM) == imm,
+	       (exit_qual & VMX_IO_OPRAND_IMM), imm);
+	report("Port (%lu) is %u",
+	       ((exit_qual & VMX_IO_PORT_MASK) >> VMX_IO_PORT_SHIFT) == port,
+	       ((exit_qual & VMX_IO_PORT_MASK) >> VMX_IO_PORT_SHIFT), port);
+
+	if (!(exit_qual & VMX_IO_STRING))
+		return;
+
+	if (!(rdmsr(MSR_IA32_VMX_BASIC) & MSR_IA32_VMX_BASIC_INOUT)) {
+		report_skip("VM-exit instruction-information field not defined for INS & OUTS");
+		return;
+	}
+
+	report("Address size (%u) is %u",
+	       ((inst_info & VMX_IO_ADDR_SIZE_MASK) >>
+		VMX_IO_ADDR_SIZE_SHIFT) == addr_size,
+	       ((inst_info & VMX_IO_ADDR_SIZE_MASK) >>
+		VMX_IO_ADDR_SIZE_SHIFT), addr_size);
+
+	if ((exit_qual & VMX_IO_DIRECTION_MASK) == VMX_IO_IN)
+		return;
+
+	report("Segment (%u) is %u",
+	       ((inst_info & VMX_IO_SEGMENT_MASK) >>
+		VMX_IO_SEGMENT_SHIFT) == segment,
+	       ((inst_info & VMX_IO_SEGMENT_MASK) >>
+		VMX_IO_SEGMENT_SHIFT), segment);
+}
+
+static void vmx_ins_outs_test(void)
+{
+	test_set_guest(ins_outs_test_guest);
+	vmcs_set_bits(CPU_EXEC_CTRL0, CPU_IO);
+
+	enter_guest();
+	check_io_exit(_VMX_IO_BYTE, VMX_IO_IN, VMX_IO_STRING, VMX_IO_REP,
+		      0, 1, _VMX_IO_ADDR_64, -1);
+	skip_exit_insn();
+
+	enter_guest();
+	check_io_exit(_VMX_IO_BYTE, VMX_IO_OUT, VMX_IO_STRING, VMX_IO_REP,
+		      0, 2, _VMX_IO_ADDR_64, VMX_SEG_DS);
+	skip_exit_insn();
+
+	enter_guest();
+	check_io_exit(_VMX_IO_WORD, VMX_IO_IN, VMX_IO_STRING, 0,
+		      0, 3, _VMX_IO_ADDR_64, -1);
+	skip_exit_insn();
+
+	enter_guest();
+	check_io_exit(_VMX_IO_WORD, VMX_IO_OUT, VMX_IO_STRING, 0,
+		      0, 4, _VMX_IO_ADDR_64, VMX_SEG_FS);
+	skip_exit_insn();
+
+	enter_guest();
+	check_io_exit(_VMX_IO_LONG, VMX_IO_IN, VMX_IO_STRING, 0,
+		      0, 5, _VMX_IO_ADDR_32, -1);
+	skip_exit_insn();
+
+	enter_guest();
+	check_io_exit(_VMX_IO_LONG, VMX_IO_OUT, VMX_IO_STRING, 0,
+		      0, 6, _VMX_IO_ADDR_32, VMX_SEG_GS);
+	skip_exit_insn();
+
+	enter_guest();
+}
+
 /*
  * If the “enable VPID†VM-execution control is 1, the value of the
  * of the VPID VM-execution control field must not be 0000H.
@@ -5896,6 +5993,7 @@  struct vmx_test vmx_tests[] = {
 	TEST(fixture_test_case2),
 	/* Opcode tests. */
 	TEST(invvpid_test_v2),
+	TEST(vmx_ins_outs_test),
 	/* VM-entry tests */
 	TEST(vmx_controls_test),
 	TEST(vmentry_movss_shadow_test),