Home | History | Annotate | Download | only in target-i386
      1 #include <sys/types.h>
      2 #include <sys/ioctl.h>
      3 #include "qemu-common.h"
      4 
      5 #ifdef CONFIG_KVM_GS_RESTORE
      6 
      7 #define INVALID_GS_REG  0xffff
      8 #define KVM_GS_RESTORE_NODETECTED 0x1
      9 #define KVM_GS_RESTORE_NO 0x2
     10 #define KVM_GS_RESTORE_YES 0x3
     11 int initial_gs = INVALID_GS_REG;
     12 int gs_need_restore = KVM_GS_RESTORE_NODETECTED;
     13 
     14 static void restoregs(int gs)
     15 {
     16     asm("movl %0, %%gs"::"r"(gs));
     17 }
     18 
     19 static unsigned int _getgs()
     20 {
     21     unsigned int gs = 0;
     22     asm("movl %%gs,%0" :"=r"(gs):);
     23     return gs;
     24 }
     25 
     26 /* No fprintf or any system call before the gs is restored successfully */
     27 static void check_and_restore_gs(void)
     28 {
     29     if (gs_need_restore == KVM_GS_RESTORE_NO)
     30         return;
     31 
     32     restoregs(initial_gs);
     33 }
     34 
     35 struct sigact_status
     36 {
     37     unsigned int sigaction:1;
     38     __sighandler_t old_handler;
     39     void (*old_sigaction) (int, siginfo_t *, void *);
     40 };
     41 static struct sigact_status o_sigact[SIGUNUSED];
     42 
     43 static void temp_sig_handler(int signum)
     44 {
     45     /* !!! must restore gs firstly */
     46     check_and_restore_gs();
     47 
     48     if (signum < SIGHUP || signum >= SIGUNUSED)
     49     {
     50         fprintf(stderr, "Invalid signal %x in temp_sig_handler\n", signum);
     51         abort();
     52     }
     53 
     54     if ( !o_sigact[signum].sigaction && o_sigact[signum].old_handler)
     55         o_sigact[signum].old_handler(signum);
     56     else
     57     {
     58         fprintf(stderr, "Invalid signal in temp_sig_handler: "
     59              "signal %x sa_info %s!!\n",
     60              signum, o_sigact[signum].sigaction ? "set":"not set" );
     61          abort();
     62     }
     63 }
     64 
     65 static void temp_sig_sigaction(int signum, siginfo_t *info, void *ucontext)
     66 {
     67     /* !!! must restore gs firstly */
     68     check_and_restore_gs();
     69 
     70     if (signum < SIGHUP || signum >= SIGUNUSED)
     71     {
     72         fprintf(stderr, "Invalid signal %x in temp_sig_sigaction\n", signum);
     73         abort();
     74     }
     75 
     76     if ( o_sigact[signum].sigaction && o_sigact[signum].old_sigaction )
     77         o_sigact[signum].old_sigaction(signum, info, ucontext);
     78     else
     79     {
     80         fprintf(stderr, "Invalid signal in temp_sig_sigaction: "
     81              "signal %x sa_info %s!!\n",
     82              signum, o_sigact[signum].sigaction ? "set":"not set" );
     83          abort();
     84     }
     85 }
     86 
     87 static int sig_taken = 0;
     88 
     89 static int take_signal_handler(void)
     90 {
     91     int i;
     92 
     93     if (gs_need_restore == KVM_GS_RESTORE_NO)
     94         return 0;
     95     if (sig_taken)
     96         return 0;
     97 
     98     memset(o_sigact, 0, sizeof(o_sigact));
     99 
    100     /* SIGHUP is 1 in POSIX */
    101     for (i = SIGHUP; i < SIGUNUSED; i++)
    102     {
    103         int sigret;
    104         struct sigaction act, old_act;
    105 
    106         sigret = sigaction(i, NULL, &old_act);
    107         if (sigret)
    108             continue;
    109         /* We don't need take the handler for default or ignore signals */
    110         if ( !(old_act.sa_flags & SA_SIGINFO) &&
    111                ((old_act.sa_handler == SIG_IGN ) ||
    112                 (old_act.sa_handler == SIG_DFL)))
    113             continue;
    114 
    115         memcpy(&act, &old_act, sizeof(struct sigaction));
    116 
    117         if (old_act.sa_flags & SA_SIGINFO)
    118         {
    119             o_sigact[i].old_sigaction = old_act.sa_sigaction;
    120             o_sigact[i].sigaction = 1;
    121             act.sa_sigaction = temp_sig_sigaction;
    122         }
    123         else
    124         {
    125             o_sigact[i].old_handler = old_act.sa_handler;
    126             act.sa_handler = temp_sig_handler;
    127         }
    128 
    129         sigaction(i, &act, NULL);
    130         continue;
    131     }
    132     sig_taken = 1;
    133     return 1;
    134 }
    135 
    136 int gs_base_pre_run(void)
    137 {
    138     if (unlikely(initial_gs == INVALID_GS_REG) )
    139     {
    140         initial_gs = _getgs();
    141         /*
    142          * As 2.6.35-28 lucid will get correct gs but clobbered GS_BASE
    143          * we have to always re-write the gs base
    144          */
    145         if (initial_gs == 0x0)
    146             gs_need_restore = KVM_GS_RESTORE_NO;
    147         else
    148             gs_need_restore = KVM_GS_RESTORE_YES;
    149     }
    150 
    151     take_signal_handler();
    152     return 0;
    153 }
    154 
    155 int gs_base_post_run(void)
    156 {
    157     check_and_restore_gs();
    158     return 0;
    159 }
    160 
    161 /*
    162  * ioctl may update errno, which is in thread local storage and
    163  * requires gs register, we have to provide our own ioctl
    164  * XXX should "call %%gs:$0x10" be replaced with call to vsyscall
    165  * page, which is more generic and clean?
    166  */
    167 int no_gs_ioctl(int fd, int type, void *arg)
    168 {
    169     int ret=0;
    170 
    171     asm(
    172       "movl %3, %%edx;\n"
    173       "movl %2, %%ecx;\n"
    174       "movl %1, %%ebx;\n"
    175       "movl $0x36, %%eax;\n"
    176       "call *%%gs:0x10;\n"
    177       "movl %%eax, %0\n"
    178       : "=m"(ret)
    179       :"m"(fd),"m"(type),"m"(arg)
    180       :"%edx","%ecx","%eax","%ebx"
    181       );
    182 
    183     return ret;
    184 }
    185 
    186 #endif
    187 
    188