aboutsummaryrefslogtreecommitdiffstats
path: root/target-i386/kvm-gs-restore.c
blob: a0ff565f762c4c8d6ac71f29661c39fb317e0849 (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
#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