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>