Skip to content

Commit

Permalink
better signal/exception support
Browse files Browse the repository at this point in the history
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@42 c046a42c-6fe2-441c-8c8c-71466251a162
  • Loading branch information
bellard committed Mar 23, 2003
1 parent 66fb976 commit 9de5e44
Show file tree
Hide file tree
Showing 15 changed files with 642 additions and 206 deletions.
8 changes: 3 additions & 5 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
- asynchronous signal interrupt / clear synchronous signal handling
- add eflags restore in emulator
- finish signal handing (fp87 state)
- verify thread support (clone() and various locks)
- optimize translated cache chaining (DLL PLT-like system)
- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit issues)
- finish signal handing (fp87 state, more siginfo conversions)
- verify thread support (clone() and various locks)
- vm86 syscall support
- overrides/16bit for string ops
- more syscalls (in particular all 64 bit ones)
- make it self runnable (use same trick as ld.so : include its own relocator and libc)
- improved 16 bit support
- fix FPU exceptions (in particular: gen_op_fpush not before mem load)
13 changes: 11 additions & 2 deletions cpu-i386.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
#define EXCP11_ALGN 18
#define EXCP12_MCHK 19

#define EXCP_SIGNAL 256 /* async signal */
#define EXCP_INTERRUPT 256 /* async interruption */

enum {
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
Expand Down Expand Up @@ -170,9 +170,10 @@ typedef struct CPUX86State {
/* various CPU modes */
int vm86;

/* exception handling */
/* exception/interrupt handling */
jmp_buf jmp_env;
int exception_index;
int interrupt_request;
} CPUX86State;

/* all CPU memory access use these macros */
Expand Down Expand Up @@ -383,11 +384,19 @@ int cpu_x86_inl(int addr);

CPUX86State *cpu_x86_init(void);
int cpu_x86_exec(CPUX86State *s);
void cpu_x86_interrupt(CPUX86State *s);
void cpu_x86_close(CPUX86State *s);

/* needed to load some predefinied segment registers */
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);

/* you can call these signal handler from you SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
struct siginfo;
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
void *puc);

/* internal functions */

#define GEN_FLAG_CODE32_SHIFT 0
Expand Down
173 changes: 148 additions & 25 deletions exec-i386.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

//#define DEBUG_EXEC
#define DEBUG_FLUSH
//#define DEBUG_SIGNAL

/* main execution loop */

Expand Down Expand Up @@ -98,7 +99,41 @@ void cpu_unlock(void)
global_cpu_lock = 0;
}

#ifdef DEBUG_EXEC
/* exception support */
/* NOTE: not static to force relocation generation by GCC */
void raise_exception(int exception_index)
{
/* NOTE: the register at this point must be saved by hand because
longjmp restore them */
#ifdef reg_EAX
env->regs[R_EAX] = EAX;
#endif
#ifdef reg_ECX
env->regs[R_ECX] = ECX;
#endif
#ifdef reg_EDX
env->regs[R_EDX] = EDX;
#endif
#ifdef reg_EBX
env->regs[R_EBX] = EBX;
#endif
#ifdef reg_ESP
env->regs[R_ESP] = ESP;
#endif
#ifdef reg_EBP
env->regs[R_EBP] = EBP;
#endif
#ifdef reg_ESI
env->regs[R_ESI] = ESI;
#endif
#ifdef reg_EDI
env->regs[R_EDI] = EDI;
#endif
env->exception_index = exception_index;
longjmp(env->jmp_env, 1);
}

#if defined(DEBUG_EXEC)
static const char *cc_op_str[] = {
"DYNAMIC",
"EFLAGS",
Expand Down Expand Up @@ -132,15 +167,16 @@ static const char *cc_op_str[] = {
"SARL",
};

static void cpu_x86_dump_state(void)
static void cpu_x86_dump_state(FILE *f)
{
int eflags;
eflags = cc_table[CC_OP].compute_all();
eflags |= (DF & DIRECTION_FLAG);
fprintf(logfile,
fprintf(f,
"EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n"
"CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n",
"CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n"
"EIP=%08x\n",
env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX],
env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP],
env->cc_src, env->cc_dst, cc_op_str[env->cc_op],
Expand All @@ -150,10 +186,10 @@ static void cpu_x86_dump_state(void)
eflags & CC_Z ? 'Z' : '-',
eflags & CC_A ? 'A' : '-',
eflags & CC_P ? 'P' : '-',
eflags & CC_C ? 'C' : '-'
);
eflags & CC_C ? 'C' : '-',
env->eip);
#if 1
fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
(double)ST0, (double)ST1, (double)ST(2), (double)ST(3));
#endif
}
Expand Down Expand Up @@ -185,10 +221,11 @@ static void tb_flush(void)
}

/* find a translation block in the translation cache. If not found,
allocate a new one */
static inline TranslationBlock *tb_find_and_alloc(unsigned long pc,
unsigned long cs_base,
unsigned int flags)
return NULL and the pointer to the last element of the list in pptb */
static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
unsigned long pc,
unsigned long cs_base,
unsigned int flags)
{
TranslationBlock **ptb, *tb;
unsigned int h;
Expand All @@ -203,16 +240,19 @@ static inline TranslationBlock *tb_find_and_alloc(unsigned long pc,
return tb;
ptb = &tb->hash_next;
}
*pptb = ptb;
return NULL;
}

/* allocate a new translation block. flush the translation buffer if
too many translation blocks or too much generated code */
static inline TranslationBlock *tb_alloc(void)
{
TranslationBlock *tb;
if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
(code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
tb_flush();
tb = &tbs[nb_tbs++];
*ptb = tb;
tb->pc = pc;
tb->cs_base = cs_base;
tb->flags = flags;
tb->tc_ptr = NULL;
tb->hash_next = NULL;
return tb;
}

Expand Down Expand Up @@ -246,7 +286,7 @@ int cpu_x86_exec(CPUX86State *env1)
#endif
int code_gen_size, ret;
void (*gen_func)(void);
TranslationBlock *tb;
TranslationBlock *tb, **ptb;
uint8_t *tc_ptr, *cs_base, *pc;
unsigned int flags;

Expand Down Expand Up @@ -289,12 +329,21 @@ int cpu_x86_exec(CPUX86State *env1)
EDI = env->regs[R_EDI];
#endif

/* put eflags in CPU temporary format */
T0 = env->eflags;
op_movl_eflags_T0();
CC_OP = CC_OP_EFLAGS;
env->interrupt_request = 0;

/* prepare setjmp context for exception handling */
if (setjmp(env->jmp_env) == 0) {
for(;;) {
if (env->interrupt_request) {
raise_exception(EXCP_INTERRUPT);
}
#ifdef DEBUG_EXEC
if (loglevel) {
cpu_x86_dump_state();
cpu_x86_dump_state(logfile);
}
#endif
/* we compute the CPU state. We assume it will not
Expand All @@ -307,28 +356,43 @@ int cpu_x86_exec(CPUX86State *env1)
GEN_FLAG_ADDSEG_SHIFT;
cs_base = env->seg_cache[R_CS].base;
pc = cs_base + env->eip;
tb = tb_find_and_alloc((unsigned long)pc, (unsigned long)cs_base,
flags);
tc_ptr = tb->tc_ptr;
if (!tb->tc_ptr) {
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
flags);
if (!tb) {
/* if no translated code available, then translate it now */
/* XXX: very inefficient: we lock all the cpus when
generating code */
cpu_lock();
tc_ptr = code_gen_ptr;
cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
&code_gen_size, pc, cs_base, flags);
ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
&code_gen_size, pc, cs_base, flags);
/* if invalid instruction, signal it */
if (ret != 0) {
cpu_unlock();
raise_exception(EXCP06_ILLOP);
}
tb = tb_alloc();
*ptb = tb;
tb->pc = (unsigned long)pc;
tb->cs_base = (unsigned long)cs_base;
tb->flags = flags;
tb->tc_ptr = tc_ptr;
tb->hash_next = NULL;
code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
cpu_unlock();
}
/* execute the generated code */
tc_ptr = tb->tc_ptr;
gen_func = (void *)tc_ptr;
gen_func();
}
}
ret = env->exception_index;

/* restore flags in standard format */
op_movl_T0_eflags();
env->eflags = T0;

/* restore global registers */
#ifdef reg_EAX
EAX = saved_EAX;
Expand Down Expand Up @@ -361,6 +425,12 @@ int cpu_x86_exec(CPUX86State *env1)
return ret;
}

void cpu_x86_interrupt(CPUX86State *s)
{
s->interrupt_request = 1;
}


void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
{
CPUX86State *saved_env;
Expand All @@ -370,3 +440,56 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
load_seg(seg_reg, selector);
env = saved_env;
}

#undef EAX
#undef ECX
#undef EDX
#undef EBX
#undef ESP
#undef EBP
#undef ESI
#undef EDI
#undef EIP
#include <signal.h>
#include <sys/ucontext.h>

static inline int handle_cpu_signal(unsigned long pc,
sigset_t *old_set)
{
#ifdef DEBUG_SIGNAL
printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n",
pc, *(unsigned long *)old_set);
#endif
if (pc >= (unsigned long)code_gen_buffer &&
pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) {
/* the PC is inside the translated code. It means that we have
a virtual CPU fault */
/* we restore the process signal mask as the sigreturn should
do it */
sigprocmask(SIG_SETMASK, old_set, NULL);
/* XXX: need to compute virtual pc position by retranslating
code. The rest of the CPU state should be correct. */
raise_exception(EXCP0D_GPF);
/* never comes here */
return 1;
} else {
return 0;
}
}

int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
void *puc)
{
#if defined(__i386__)
struct ucontext *uc = puc;
unsigned long pc;
sigset_t *pold_set;

pc = uc->uc_mcontext.gregs[EIP];
pold_set = &uc->uc_sigmask;
return handle_cpu_signal(pc, pold_set);
#else
#warning No CPU specific signal handler: cannot handle target SIGSEGV events
return 0;
#endif
}
4 changes: 4 additions & 0 deletions exec-i386.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,7 @@ extern CCTable cc_table[];
void load_seg(int seg_reg, int selector);
void cpu_lock(void);
void cpu_unlock(void);
void raise_exception(int exception_index);

void OPPROTO op_movl_eflags_T0(void);
void OPPROTO op_movl_T0_eflags(void);
3 changes: 3 additions & 0 deletions linux-user/elfload.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm,
/* Create enough stack to hold everything. If we don't use
* it for args, we'll use it for something else...
*/
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
we allocate a bigger stack. Need a better solution, for example
by remapping the process stack directly at the right place */
if(x86_stack_size > MAX_ARG_PAGES*X86_PAGE_SIZE) {
if((long)mmap4k((void *)(X86_STACK_TOP-x86_stack_size), x86_stack_size + X86_PAGE_SIZE,
PROT_READ | PROT_WRITE,
Expand Down
6 changes: 5 additions & 1 deletion linux-user/ioctls.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* emulated ioctl list */

IOCTL(TCGETS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCGETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCSETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCSETSF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TCSETSW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize)))
Expand Down Expand Up @@ -199,8 +199,12 @@
IOCTL(SNDCTL_TMR_METRONOME, IOC_W, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_TMR_SELECT, IOC_W, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_TMR_SOURCE, IOC_RW, MK_PTR(TYPE_INT))
#if 0
/* we invalidate these defines because they have a same number as
termios ioctls */
IOCTL(SNDCTL_TMR_START, 0, TYPE_NULL)
IOCTL(SNDCTL_TMR_STOP, 0, TYPE_NULL)
#endif
IOCTL(SNDCTL_TMR_TEMPO, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SNDCTL_TMR_TIMEBASE, IOC_RW, MK_PTR(TYPE_INT))

Expand Down
Loading

0 comments on commit 9de5e44

Please sign in to comment.