[PATCH kvm-unit-tests] emulator: test for MMX movq raising #MF if pending x87 exceptions exist

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Test that a pending #MF is raised correctly if encountered by the emulator
(and that the host isn't trashed by an unexpected exception).  The approach
here exploits the TLB and so will only work on more modern processors.

Signed-off-by: Avi Kivity <avi@xxxxxxxxxx>
---
 x86/emulator.c |   56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 55 insertions(+), 1 deletions(-)

diff --git a/x86/emulator.c b/x86/emulator.c
index 6590618..33820f4 100644
--- a/x86/emulator.c
+++ b/x86/emulator.c
@@ -2,12 +2,15 @@
 #include "vm.h"
 #include "libcflat.h"
 #include "desc.h"
+#include "types.h"
 
 #define memset __builtin_memset
 #define TESTDEV_IO_PORT 0xe0
 
 int fails, tests;
 
+static int exceptions;
+
 void report(const char *name, int result)
 {
 	++tests;
@@ -645,16 +648,65 @@ static void test_shld_shrd(u32 *mem)
     report("shrd (cl)", *mem == ((0x12345678 >> 3) | (5u << 29)));
 }
 
+static void advance_rip_by_3_and_note_exception(struct ex_regs *regs)
+{
+    ++exceptions;
+    regs->rip += 3;
+}
+
+static void test_mmx_movq_mf(uint64_t *mem, uint8_t *insn_page,
+			     uint8_t *alt_insn_page, void *insn_ram)
+{
+    uint16_t fcw = 0;  // all exceptions unmasked
+    ulong *cr3 = (ulong *)read_cr3();
+
+    write_cr0(read_cr0() & ~6);  // TS, EM
+    // Place a trapping instruction in the page to trigger a VMEXIT
+    insn_page[0] = 0x89; // mov %eax, (%rax)
+    insn_page[1] = 0x00;
+    insn_page[2] = 0x90; // nop
+    insn_page[3] = 0xc3; // ret
+    // Place the instruction we want the hypervisor to see in the alternate page
+    alt_insn_page[0] = 0x0f; // movq %mm0, (%rax)
+    alt_insn_page[1] = 0x7f;
+    alt_insn_page[2] = 0x00;
+    alt_insn_page[3] = 0xc3; // ret
+
+    exceptions = 0;
+    handle_exception(MF_VECTOR, advance_rip_by_3_and_note_exception);
+
+    // Load the code TLB with insn_page, but point the page tables at
+    // alt_insn_page (and keep the data TLB clear, for AMD decode assist).
+    // This will make the CPU trap on the insn_page instruction but the
+    // hypervisor will see alt_insn_page.
+    install_page(cr3, virt_to_phys(insn_page), insn_ram);
+    asm volatile("fninit; fldcw %0" : : "m"(fcw));
+    asm volatile("fldz; fldz; fdivp"); // generate exception
+    invlpg(insn_ram);
+    // Load code TLB
+    asm volatile("call *%0" : : "r"(insn_ram + 3));
+    install_page(cr3, virt_to_phys(alt_insn_page), insn_ram);
+    // Trap, let hypervisor emulate at alt_insn_page
+    asm volatile("call *%0" : : "r"(insn_ram), "a"(mem));
+    // exit MMX mode
+    asm volatile("fnclex; emms");
+    report("movq mmx generates #MF", exceptions == 1);
+    handle_exception(MF_VECTOR, 0);
+}
+
 int main()
 {
 	void *mem;
+	void *insn_page, *alt_insn_page;
 	void *insn_ram;
 	unsigned long t1, t2;
 
 	setup_vm();
 	setup_idt();
 	mem = vmap(IORAM_BASE_PHYS, IORAM_LEN);
-	insn_ram = vmalloc(4096);
+	insn_page = alloc_page();
+	alt_insn_page = alloc_page();
+	insn_ram = vmap(virt_to_phys(insn_page), 4096);
 
 	// test mov reg, r/m and mov r/m, reg
 	t1 = 0x123456789abcdef;
@@ -690,6 +742,8 @@ int main()
 	test_rip_relative(mem, insn_ram);
 	test_shld_shrd(mem);
 
+	test_mmx_movq_mf(mem, insn_page, alt_insn_page, insn_ram);
+
 	printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
 	return fails ? 1 : 0;
 }
-- 
1.7.9

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux