#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); } struct sigact_status { unsigned int sigaction:1; __sighandler_t old_handler; void (*old_sigaction) (int, siginfo_t *, void *); }; static struct sigact_status o_sigact[SIGUNUSED]; static void temp_sig_handler(int signum) { /* !!! must restore gs firstly */ check_and_restore_gs(); if (signum < SIGHUP || signum >= SIGUNUSED) { fprintf(stderr, "Invalid signal %x in temp_sig_handler\n", signum); abort(); } if ( !o_sigact[signum].sigaction && o_sigact[signum].old_handler) o_sigact[signum].old_handler(signum); else { fprintf(stderr, "Invalid signal in temp_sig_handler: " "signal %x sa_info %s!!\n", signum, o_sigact[signum].sigaction ? "set":"not set" ); abort(); } } static void temp_sig_sigaction(int signum, siginfo_t *info, void *ucontext) { /* !!! must restore gs firstly */ check_and_restore_gs(); if (signum < SIGHUP || signum >= SIGUNUSED) { fprintf(stderr, "Invalid signal %x in temp_sig_sigaction\n", signum); abort(); } if ( o_sigact[signum].sigaction && o_sigact[signum].old_sigaction ) o_sigact[signum].old_sigaction(signum, info, ucontext); else { fprintf(stderr, "Invalid signal in temp_sig_sigaction: " "signal %x sa_info %s!!\n", signum, o_sigact[signum].sigaction ? "set":"not set" ); abort(); } } static int sig_taken = 0; static int take_signal_handler(void) { int i; if (gs_need_restore == KVM_GS_RESTORE_NO) return 0; if (sig_taken) return 0; memset(o_sigact, 0, sizeof(o_sigact)); /* SIGHUP is 1 in POSIX */ for (i = SIGHUP; i < SIGUNUSED; i++) { int sigret; struct sigaction act, old_act; sigret = sigaction(i, NULL, &old_act); if (sigret) continue; /* We don't need take the handler for default or ignore signals */ if ( !(old_act.sa_flags & SA_SIGINFO) && ((old_act.sa_handler == SIG_IGN ) || (old_act.sa_handler == SIG_DFL))) continue; memcpy(&act, &old_act, sizeof(struct sigaction)); if (old_act.sa_flags & SA_SIGINFO) { o_sigact[i].old_sigaction = old_act.sa_sigaction; o_sigact[i].sigaction = 1; act.sa_sigaction = temp_sig_sigaction; } else { o_sigact[i].old_handler = old_act.sa_handler; act.sa_handler = temp_sig_handler; } sigaction(i, &act, NULL); continue; } sig_taken = 1; 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