Index: kqemu-freebsd.c
@@ -38,6 +38,14 @@
 #else
 #include <machine/npx.h>
 #endif
+#ifdef __x86_64__
+#include <sys/smp.h>
+#include <sys/pcpu.h>
+#include <machine/pcb.h>
+#include <machine/specialreg.h>
+#include <machine/segments.h>
+#include <machine/tss.h>
+#endif
 
 #include "kqemu-kernel.h"
 
@@ -248,6 +256,57 @@
     va_end(ap);
 }
 
+#ifdef __x86_64__
+int kqemu_cpu0gdtfixed;
+int kqemu_gdts_used;
+struct user_segment_descriptor *kqemu_gdts;
+struct region_descriptor kqemu_r_newgdt;
+extern  struct pcpu __pcpu[];
+
+/* called with interrupts disabled */
+void CDECL kqemu_tss_fixup(unsigned long kerngdtbase)
+{
+    int gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
+    unsigned cpuid = PCPU_GET(cpuid);
+    struct user_segment_descriptor *newgdt = gdt;
+
+    if (mp_ncpus <= 1 || kerngdtbase != (unsigned long)gdt)
+	/* UP host or gdt already moved, nothing to do */
+	return;
+    if (cpuid) {
+	/* move gdts of all but first cpu */
+	if (!kqemu_gdts)
+	    /*
+	     * XXX gdt is allocated as
+	     *	struct user_segment_descriptor gdt[NGDT * MAXCPU];
+	     * so it has room for the moved copies; need to allocate at
+	     * kldload (and only free if kqemu_gdts_used is zero) should this
+	     * change in the future
+	     */
+	    kqemu_gdts = &gdt[NGDT];
+	++kqemu_gdts_used;
+	newgdt = &kqemu_gdts[NGDT * (cpuid - 1)];
+	bcopy(gdt, newgdt, NGDT * sizeof(gdt[0]));
+	kqemu_r_newgdt.rd_limit = NGDT * sizeof(gdt[0]) - 1;
+	kqemu_r_newgdt.rd_base = (long) newgdt;
+    } else {
+	if (kqemu_cpu0gdtfixed)
+	    return;
+	++kqemu_cpu0gdtfixed;
+    }
+    gdt_segs[GPROC0_SEL].ssd_base = (long) &common_tss[cpuid];
+    ssdtosyssd(&gdt_segs[GPROC0_SEL],
+       (struct system_segment_descriptor *)&newgdt[GPROC0_SEL]);
+    if (cpuid) {
+	lgdt(&kqemu_r_newgdt);
+	wrmsr(MSR_GSBASE, (u_int64_t)&__pcpu[cpuid]);
+	wrmsr(MSR_KGSBASE, curthread->td_pcb->pcb_gsbase);
+	wrmsr(MSR_FSBASE, curthread->td_pcb->pcb_fsbase);
+    }
+    ltr(gsel_tss);
+}
+#endif
+
 struct kqemu_instance { 
 #if __FreeBSD_version >= 500000
     TAILQ_ENTRY(kqemu_instance) kqemu_ent;
Index: common/kernel.c
@@ -1025,6 +1025,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
@@ -1096,6 +1099,14 @@
             apic_nmi_mask = apic_save_and_disable_nmi(s);
         }
 
+#ifdef __FreeBSD__
+#ifdef __x86_64__
+        spin_lock(&g->lock);
+        asm volatile ("sgdt %0" : : "m" (s->kernel_gdt));
+        kqemu_tss_fixup(s->kernel_gdt.base);
+        spin_unlock(&g->lock);
+#endif
+#endif
         /* load breakpoint registers and avoid setting them if in the
            monitor address space. We suppose that no breakpoints are
            set by the host OS for this process */
Index: kqemu-kernel.h
@@ -48,4 +48,10 @@
 
 void CDECL kqemu_log(const char *fmt, ...);
 
+#ifdef __FreeBSD__
+#ifdef __x86_64__
+void CDECL kqemu_tss_fixup(unsigned long kerngdtbase);
+#endif
+#endif
+
 #endif /* KQEMU_KERNEL_H */
