aboutsummaryrefslogtreecommitdiffstats
path: root/target-i386
diff options
context:
space:
mode:
authorJun Nakajima <jun.nakajima@intel.com>2011-05-27 18:24:21 -0700
committerJun Nakajima <jun.nakajima@intel.com>2011-05-27 18:24:21 -0700
commitbb0140b925cb2adce03ebc0885067ea3bfd19a20 (patch)
tree1e5cb53ab8a3cf8da339498754e1a7eda80cb039 /target-i386
parentbd03068d5d287ec638bc834d04f85b4e49404db5 (diff)
downloadexternal_qemu-bb0140b925cb2adce03ebc0885067ea3bfd19a20.zip
external_qemu-bb0140b925cb2adce03ebc0885067ea3bfd19a20.tar.gz
external_qemu-bb0140b925cb2adce03ebc0885067ea3bfd19a20.tar.bz2
x86: Workaorund for the KVM GS_BASE MSR save/restore issue.
In some versions of the kvm module in Ubuntu, the host's GS_BASE MSR is not save/restored correctly when running guests on 64-bit hosts if the qemu/emulator is 32-bit. This patch implements a workaround in the emulator code. Change-Id: If3ebe3cb49f377c5d0547c75f6ac3a3ceacdc375 Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com> Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/kvm-gs-restore.c144
-rw-r--r--target-i386/kvm-gs-restore.h12
-rw-r--r--target-i386/kvm.c21
3 files changed, 177 insertions, 0 deletions
diff --git a/target-i386/kvm-gs-restore.c b/target-i386/kvm-gs-restore.c
new file mode 100644
index 0000000..a0ff565
--- /dev/null
+++ b/target-i386/kvm-gs-restore.c
@@ -0,0 +1,144 @@
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "qemu-common.h"
+
+#ifdef CONFIG_KVM_GS_RESTORE
+
+#define INVALID_GS_REG 0xffff
+#define KVM_GS_RESTORE_NODETECTED 0x1
+#define KVM_GS_RESTORE_NO 0x2
+#define KVM_GS_RESTORE_YES 0x3
+int initial_gs = INVALID_GS_REG;
+int gs_need_restore = KVM_GS_RESTORE_NODETECTED;
+
+static void restoregs(int gs)
+{
+ asm("movl %0, %%gs"::"r"(gs));
+}
+
+static unsigned int _getgs()
+{
+ unsigned int gs = 0;
+ asm("movl %%gs,%0" :"=r"(gs):);
+ return gs;
+}
+
+/* No fprintf or any system call before the gs is restored successfully */
+static void check_and_restore_gs(void)
+{
+ if (gs_need_restore == KVM_GS_RESTORE_NO)
+ return;
+
+ restoregs(initial_gs);
+}
+
+static struct sigaction old_alarm_act, old_gio_act, old_pipe_act,old_usr1_act, old_chld_act;
+static void temp_sig_handler(int host_signum)
+{
+ /* !!! must restore gs firstly */
+ check_and_restore_gs();
+ switch (host_signum)
+ {
+ case SIGALRM:
+ old_alarm_act.sa_handler(host_signum);
+ break;
+
+ case SIGIO:
+ old_gio_act.sa_handler(host_signum);
+ break;
+
+ case SIGUSR1:
+ old_usr1_act.sa_handler(host_signum);
+ break;
+
+ case SIGPIPE:
+ old_pipe_act.sa_handler(host_signum);
+ break;
+
+ case SIGCHLD:
+ old_chld_act.sa_handler(host_signum);
+ break;
+
+ default:
+ fprintf(stderr, "Not take signal %x!!\n", host_signum);
+ break;
+ }
+}
+
+static int sig_taken = 0;
+static int take_signal_handler(void)
+{
+ struct sigaction act;
+ int ret;
+
+ if (gs_need_restore == KVM_GS_RESTORE_NO)
+ return 0;
+ if (sig_taken)
+ return 0;
+
+ sig_taken = 1;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = temp_sig_handler;
+ /* Did we missed any other signal ? */
+ sigaction(SIGALRM, &act, &old_alarm_act);
+ sigaction(SIGIO, &act, &old_gio_act);
+ sigaction(SIGUSR1, &act, &old_usr1_act);
+ sigaction(SIGPIPE, &act, &old_pipe_act);
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, &old_chld_act);
+ return 1;
+}
+
+int gs_base_pre_run(void)
+{
+ if (unlikely(initial_gs == INVALID_GS_REG) )
+ {
+ initial_gs = _getgs();
+ /*
+ * As 2.6.35-28 lucid will get correct gs but clobbered GS_BASE
+ * we have to always re-write the gs base
+ */
+ if (initial_gs == 0x0)
+ gs_need_restore = KVM_GS_RESTORE_NO;
+ else
+ gs_need_restore = KVM_GS_RESTORE_YES;
+ }
+
+ take_signal_handler();
+ return 0;
+}
+
+int gs_base_post_run(void)
+{
+ check_and_restore_gs();
+ return 0;
+}
+
+/*
+ * ioctl may update errno, which is in thread local storage and
+ * requires gs register, we have to provide our own ioctl
+ * XXX should "call %%gs:$0x10" be replaced with call to vsyscall
+ * page, which is more generic and clean?
+ */
+int no_gs_ioctl(int fd, int type, void *arg)
+{
+ int ret=0;
+
+ asm(
+ "movl %3, %%edx;\n"
+ "movl %2, %%ecx;\n"
+ "movl %1, %%ebx;\n"
+ "movl $0x36, %%eax;\n"
+ "call *%%gs:0x10;\n"
+ "movl %%eax, %0\n"
+ : "=m"(ret)
+ :"m"(fd),"m"(type),"m"(arg)
+ :"%edx","%ecx","%eax","%ebx"
+ );
+
+ return ret;
+}
+
+#endif
+
diff --git a/target-i386/kvm-gs-restore.h b/target-i386/kvm-gs-restore.h
new file mode 100644
index 0000000..0947b33
--- /dev/null
+++ b/target-i386/kvm-gs-restore.h
@@ -0,0 +1,12 @@
+#ifndef _KVM_GS_RESTORE_H
+#define _KVM_GS_RESTORE_H
+
+#ifdef CONFIG_KVM_GS_RESTORE
+int no_gs_ioctl(int fd, int type, void *arg);
+int gs_base_post_run(void);
+int gs_base_pre_run(void);
+extern int gs_need_restore;
+#define KVM_GS_RESTORE_NO 0x2
+#endif
+
+#endif
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 4b135b1..07efac0 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -26,6 +26,10 @@
#include "cpu.h"
#include "gdbstub.h"
+#ifdef CONFIG_KVM_GS_RESTORE
+#include "kvm-gs-restore.h"
+#endif
+
//#define DEBUG_KVM
#ifdef DEBUG_KVM
@@ -690,6 +694,16 @@ int kvm_arch_get_registers(CPUState *env)
return 0;
}
+int kvm_arch_vcpu_run(CPUState *env)
+{
+#ifdef CONFIG_KVM_GS_RESTORE
+ if (gs_need_restore != KVM_GS_RESTORE_NO)
+ return no_gs_ioctl(env->kvm_fd, KVM_RUN, 0);
+ else
+#endif
+ return kvm_vcpu_ioctl(env, KVM_RUN, 0);
+}
+
int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
{
/* Try to inject an interrupt if the guest can accept it */
@@ -721,11 +735,18 @@ int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
dprintf("setting tpr\n");
run->cr8 = cpu_get_apic_tpr(env);
+#ifdef CONFIG_KVM_GS_RESTORE
+ gs_base_pre_run();
+#endif
+
return 0;
}
int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
{
+#ifdef CONFIG_KVM_GS_RESTORE
+ gs_base_post_run();
+#endif
if (run->if_flag)
env->eflags |= IF_MASK;
else