[PATCH kvm-unit-tests 2/4] x86: replace set_exception_return with longjmp-based implementation

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

 



set_exception_return forces exceptions handlers to return to a specific
address instead of returning to the instruction address pushed by the
CPU at the time of the exception. The unit tests apic.c and vmx.c use
this functionality to recover from expected exceptions.

When using set_exception_return one would have to be careful not to modify
the stack (such as by doing a function call) as triggering the exception
will likely jump us past the instructions which undo the stack manipulation
(such as a ret).  This is unnecessarily brittle, and C already has a
mechanism to do non-local returns---setjmp.  Now that libcflat includes
an implementation of setjmp, replace set_exception_return with a wrapper
that takes care of restoring the processor flags as well.

Reported-by: David Matlack <dmatlack@xxxxxxxxxx>
Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
 lib/x86/desc.c | 15 +++++++++++----
 lib/x86/desc.h |  6 +++++-
 x86/apic.c     |  8 ++++----
 x86/vmx.c      | 18 +++++++++---------
 4 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index 4760026..acf29e3 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -1,6 +1,7 @@
 #include "libcflat.h"
 #include "desc.h"
 #include "processor.h"
+#include <setjmp.h>
 
 void set_idt_entry(int vec, void *addr, int dpl)
 {
@@ -315,12 +316,18 @@ void setup_alt_stack(void)
 #endif
 
 static bool exception;
-static void *exception_return;
+static jmp_buf *exception_jmpbuf;
+
+static void exception_handler_longjmp(void)
+{
+	longjmp(*exception_jmpbuf, 1);
+}
 
 static void exception_handler(struct ex_regs *regs)
 {
+	/* longjmp must happen after iret, so do not do it now.  */
 	exception = true;
-	regs->rip = (unsigned long)exception_return;
+	regs->rip = (unsigned long)&exception_handler_longjmp;
 }
 
 bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
@@ -333,7 +340,7 @@ bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
 	return exception;
 }
 
-void set_exception_return(void *addr)
+void __set_exception_jmpbuf(jmp_buf *addr)
 {
-	exception_return = addr;
+	exception_jmpbuf = addr;
 }
diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index 7733151..be52fd4 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -1,6 +1,8 @@
 #ifndef __IDT_TEST__
 #define __IDT_TEST__
 
+#include <setjmp.h>
+
 void setup_idt(void);
 void setup_alt_stack(void);
 
@@ -155,6 +157,8 @@ void handle_exception(u8 v, void (*func)(struct ex_regs *regs));
 
 bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
 			void *data);
-void set_exception_return(void *addr);
+void __set_exception_jmpbuf(jmp_buf *addr);
+#define set_exception_jmpbuf(jmpbuf) \
+	(setjmp(jmpbuf) ? : (__set_exception_jmpbuf(&(jmpbuf)), 0))
 
 #endif
diff --git a/x86/apic.c b/x86/apic.c
index d4eec52..2e04c82 100644
--- a/x86/apic.c
+++ b/x86/apic.c
@@ -63,10 +63,10 @@ static void test_tsc_deadline_timer(void)
 
 static void do_write_apicbase(void *data)
 {
-    set_exception_return(&&resume);
-    wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
-resume:
-    barrier();
+    jmp_buf jmpbuf;
+    if (set_exception_jmpbuf(jmpbuf) == 0) {
+        wrmsr(MSR_IA32_APICBASE, *(u64 *)data);
+    }
 }
 
 void test_enable_x2apic(void)
diff --git a/x86/vmx.c b/x86/vmx.c
index 47593bd..ee9aca8 100644
--- a/x86/vmx.c
+++ b/x86/vmx.c
@@ -619,19 +619,19 @@ static void init_vmx(void)
 
 static void do_vmxon_off(void *data)
 {
-	set_exception_return(&&resume);
-	vmx_on();
-	vmx_off();
-resume:
-	barrier();
+	jmp_buf jmpbuf;
+	if (set_exception_jmpbuf(jmpbuf) == 0) {
+		vmx_on();
+		vmx_off();
+	}
 }
 
 static void do_write_feature_control(void *data)
 {
-	set_exception_return(&&resume);
-	wrmsr(MSR_IA32_FEATURE_CONTROL, 0);
-resume:
-	barrier();
+	jmp_buf jmpbuf;
+	if (set_exception_jmpbuf(jmpbuf) == 0) {
+		wrmsr(MSR_IA32_FEATURE_CONTROL, 0);
+	}
 }
 
 static int test_vmx_feature_control(void)
-- 
2.5.0


--
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