diff mbox

[kvm-unit-tests,10/18] x86: eventinj: fix inline assembly and make it more robust to new compilers

Message ID 1398698581-17302-11-git-send-email-pbonzini@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Paolo Bonzini April 28, 2014, 3:22 p.m. UTC
Using assembler trampolines instead of && fixes NMI IRET test.

There is another bug, however.  Returns to the same privilege level do
not pop SS:RSP on 32-bit, so the nested NMI underflowed the stack at "s".
This is fixed in the new code.  The new code doesn't set up SS:RSP and
instead leaves some space for the nested NMI handler on the alternate
stack.  The old stack pointer is kept and restored when the nested
handler returns.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 x86/eventinj.c | 64 ++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 42 insertions(+), 22 deletions(-)
diff mbox

Patch

diff --git a/x86/eventinj.c b/x86/eventinj.c
index 2124bdf..1df3a43 100644
--- a/x86/eventinj.c
+++ b/x86/eventinj.c
@@ -59,17 +59,25 @@  static volatile int test_count;
 ulong stack_phys;
 void *stack_va;
 
-static void pf_tss(void)
+void do_pf_tss(void)
 {
-start:
 	printf("PF running\n");
 	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
 		    stack_phys | PTE_PRESENT | PTE_WRITE, 0);
 	invlpg(stack_va);
-	asm volatile ("iret");
-	goto start;
 }
 
+extern void pf_tss(void);
+
+asm (
+        "pf_tss: \n\t"
+        "call do_pf_tss \n\t"
+        "add $"S", %"R "sp\n\t"        // discard error code
+        "iret"W" \n\t"
+        "jmp pf_tss\n\t"
+    );
+
+
 static void of_isr(struct ex_regs *r)
 {
 	printf("OF isr running\n");
@@ -114,39 +122,51 @@  static void nmi_isr(struct ex_regs *r)
 	printf("After nested NMI to self\n");
 }
 
-unsigned long after_iret_addr;
+unsigned long *iret_stack;
 
 static void nested_nmi_iret_isr(struct ex_regs *r)
 {
 	printf("Nested NMI isr running rip=%x\n", r->rip);
 
-	if (r->rip == after_iret_addr)
+	if (r->rip == iret_stack[-3])
 		test_count++;
 }
+
+extern void do_iret(ulong phys_stack, void *virt_stack);
+
+// Return to same privilege level won't pop SS or SP, so
+// save it in RDX while we run on the nested stack
+
+asm("do_iret:"
+#ifdef __x86_64__
+	"mov %rdi, %rax \n\t"		// phys_stack
+	"mov %rsi, %rdx \n\t"		// virt_stack
+#else
+	"mov 4(%esp), %eax \n\t"	// phys_stack
+	"mov 8(%esp), %edx \n\t"	// virt_stack
+#endif
+	"xchg %"R "dx, %"R "sp \n\t"	// point to new stack
+	"pushf"W" \n\t"
+	"mov %cs, %ecx \n\t"
+	"push"W" %"R "cx \n\t"
+	"push"W" $1f \n\t"
+	"outl %eax, $0xe4 \n\t"		// flush page
+	"iret"W" \n\t"
+	"1: xchg %"R "dx, %"R "sp \n\t"	// point to old stack
+	"ret\n\t"
+   );
+
 static void nmi_iret_isr(struct ex_regs *r)
 {
 	unsigned long *s = alloc_page();
 	test_count++;
-	printf("NMI isr running %p stack %p\n", &&after_iret, s);
+	printf("NMI isr running stack %p\n", s);
 	handle_exception(2, nested_nmi_iret_isr);
 	printf("Sending nested NMI to self\n");
 	apic_self_nmi();
 	printf("After nested NMI to self\n");
-	s[4] = read_ss();
-	s[3] = 0; /* rsp */
-	s[2] = read_rflags();
-	s[1] = read_cs();
-	s[0] = after_iret_addr = (unsigned long)&&after_iret;
-	asm ("mov %%" R "sp, %0\n\t"
-	     "mov %1, %%" R "sp\n\t"
-	     "outl %2, $0xe4\n\t" /* flush stack page */
-#ifdef __x86_64__
-	     "iretq\n\t"
-#else
-	     "iretl\n\t"
-#endif
-	     : "=m"(s[3]) : "rm"(&s[0]), "a"((unsigned int)virt_to_phys(s)) : "memory");
-after_iret:
+	iret_stack = &s[128];
+	do_iret(virt_to_phys(s), iret_stack);
 	printf("After iret\n");
 }