@@ -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)
@@ -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
@@ -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
@@ -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),