blob: 2ca6ab6bd774e6d03deaa7eeca137288bce3adf5 [file] [log] [blame]
#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