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
11int initial_gs = INVALID_GS_REG;
12int gs_need_restore = KVM_GS_RESTORE_NODETECTED;
13
14static void restoregs(int gs)
15{
16    asm("movl %0, %%gs"::"r"(gs));
17}
18
19static 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 */
27static 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
35struct sigact_status
36{
37    unsigned int sigaction:1;
38    __sighandler_t old_handler;
39    void (*old_sigaction) (int, siginfo_t *, void *);
40};
41static struct sigact_status o_sigact[SIGUNUSED];
42
43static 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
65static 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
87static int sig_taken = 0;
88
89static 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
136int 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
155int 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 */
167int 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