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@xxxxxxxxxx> Reviewed-by: Marc Orr <marcorr@xxxxxxxxxx> --- lib/x86/msr.h | 3 ++ x86/unittests.cfg | 6 +++ x86/vmx.h | 17 ++++++++ x86/vmx_tests.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) 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), -- 2.20.1.97.g81188d93c3-goog