Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 30 Apr 2008 00:24:58 +0200
From:      Juergen Lock <nox@jelal.kn-bremen.de>
To:        freebsd-emulation@FreeBSD.org, freebsd-amd64@FreeBSD.org
Subject:   seems I finally found what upset kqemu on amd64 SMP...  shared gdt! (please test patch :)
Message-ID:  <20080429222458.GA20855@saturn.kn-bremen.de>

next in thread | raw e-mail | index | archive | help
Yeah, the amd64 kernel reuses the same gdt to setup all cpus, causing
kqemu to end up restoring the interrupt stackpointer (after running
guest code using its own cpu state) from the tss of the last cpu,
regardless which cpu it happened to run on.  And that then causes the last
cpu's (usually) idle thread's stack to get smashed and the host doing
multiple panics...  (Which also explains why pinning qemu onto cpu 1
worked on a 2-way host.)

 Here's the patch I just tested, of course you'd want to disable this
once the gdt is no longer shared, so assuming someone wants to fix this,
please also do an OSVERSION bump...

Index: kqemu-freebsd.c
@@ -34,6 +34,11 @@
 
 #include <machine/vmparam.h>
 #include <machine/stdarg.h>
+#ifdef __x86_64__
+#include <sys/pcpu.h>
+#include <machine/segments.h>
+#include <machine/tss.h>
+#endif
 
 #include "kqemu-kernel.h"
 
@@ -264,6 +269,19 @@
     va_end(ap);
 }
 
+#ifdef __x86_64__
+/* called with interrupts disabled */
+void CDECL kqemu_tss_workaround(void)
+{
+    int gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
+
+    gdt_segs[GPROC0_SEL].ssd_base = (long) &common_tss[PCPU_GET(cpuid)];
+    ssdtosyssd(&gdt_segs[GPROC0_SEL],
+       (struct system_segment_descriptor *)&gdt[GPROC0_SEL]);
+    ltr(gsel_tss);
+}
+#endif
+
 struct kqemu_instance { 
 #if __FreeBSD_version >= 500000
     TAILQ_ENTRY(kqemu_instance) kqemu_ent;
Index: common/kernel.c
@@ -1030,6 +1030,9 @@
 #ifdef __x86_64__
     uint16_t saved_ds, saved_es;
     unsigned long fs_base, gs_base;
+#ifdef __FreeBSD__
+    struct kqemu_global_state *g = s->global_state;
+#endif
 #endif
     
 #ifdef PROFILE
@@ -1197,6 +1200,13 @@
             apic_restore_nmi(s, apic_nmi_mask);
         }
         profile_record(s);
+#ifdef __FreeBSD__
+#ifdef __x86_64__
+        spin_lock(&g->lock);
+        kqemu_tss_workaround();
+        spin_unlock(&g->lock);
+#endif
+#endif
 
         if (s->mon_req == MON_REQ_IRQ) {
             struct kqemu_exception_regs *r;
Index: kqemu-kernel.h
@@ -44,4 +44,10 @@
 
 void CDECL kqemu_log(const char *fmt, ...);
 
+#ifdef __FreeBSD__
+#ifdef __x86_64__
+void CDECL kqemu_tss_workaround(void);
+#endif
+#endif
+
 #endif /* KQEMU_KERNEL_H */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080429222458.GA20855>