aboutsummaryrefslogtreecommitdiffstats
path: root/target-i386/kvm-gs-restore.c
blob: 2ca6ab6bd774e6d03deaa7eeca137288bce3adf5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#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