From a513fe19ac4896a09c6c338204d76c39e652451f Mon Sep 17 00:00:00 2001 From: bellard Date: Tue, 27 May 2003 23:29:48 +0000 Subject: [PATCH] precise exceptions git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@194 c046a42c-6fe2-441c-8c8c-71466251a162 --- dyngen.c | 4 ++-- exec-i386.c | 60 +++++++++++++++++++++++++++++------------------------ exec-i386.h | 5 ++++- exec.c | 31 +++++++++++++++++++++++++++ exec.h | 10 +++++---- 5 files changed, 76 insertions(+), 34 deletions(-) diff --git a/dyngen.c b/dyngen.c index 96a47c8ed..4ed8b4b8b 100644 --- a/dyngen.c +++ b/dyngen.c @@ -451,7 +451,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, } if (gen_switch == 2) { - fprintf(outfile, "DEF(%s, %d)\n", name + 3, nb_args); + fprintf(outfile, "DEF(%s, %d, %d)\n", name + 3, nb_args, copy_size); } else if (gen_switch == 1) { /* output C code */ @@ -991,7 +991,7 @@ int load_elf(const char *filename, FILE *outfile, int do_print_enum) } if (do_print_enum) { - fprintf(outfile, "DEF(end, 0)\n"); + fprintf(outfile, "DEF(end, 0, 0)\n"); for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { const char *name, *p; name = strtab + sym->st_name; diff --git a/exec-i386.c b/exec-i386.c index 68e0371cc..002f00bdc 100644 --- a/exec-i386.c +++ b/exec-i386.c @@ -39,9 +39,7 @@ void cpu_unlock(void) spin_unlock(&global_cpu_lock); } -/* exception support */ -/* NOTE: not static to force relocation generation by GCC */ -void raise_exception_err(int exception_index, int error_code) +void cpu_loop_exit(void) { /* NOTE: the register at this point must be saved by hand because longjmp restore them */ @@ -76,17 +74,9 @@ void raise_exception_err(int exception_index, int error_code) #ifdef reg_EDI env->regs[R_EDI] = EDI; #endif - env->exception_index = exception_index; - env->error_code = error_code; longjmp(env->jmp_env, 1); } -/* short cut if error_code is 0 or not present */ -void raise_exception(int exception_index) -{ - raise_exception_err(exception_index, 0); -} - int cpu_x86_exec(CPUX86State *env1) { int saved_T0, saved_T1, saved_A0; @@ -115,7 +105,7 @@ int cpu_x86_exec(CPUX86State *env1) #ifdef reg_EDI int saved_EDI; #endif - int code_gen_size, ret, code_size; + int code_gen_size, ret; void (*gen_func)(void); TranslationBlock *tb, **ptb; uint8_t *tc_ptr, *cs_base, *pc; @@ -172,7 +162,8 @@ int cpu_x86_exec(CPUX86State *env1) T0 = 0; /* force lookup of first TB */ for(;;) { if (env->interrupt_request) { - raise_exception(EXCP_INTERRUPT); + env->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(); } #ifdef DEBUG_EXEC if (loglevel) { @@ -226,9 +217,9 @@ int cpu_x86_exec(CPUX86State *env1) } tc_ptr = code_gen_ptr; tb->tc_ptr = tc_ptr; - ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, - &code_gen_size, pc, cs_base, flags, - &code_size, tb); + tb->cs_base = (unsigned long)cs_base; + tb->flags = flags; + ret = cpu_x86_gen_code(tb, CODE_GEN_MAX_SIZE, &code_gen_size); /* if invalid instruction, signal it */ if (ret != 0) { /* NOTE: the tb is allocated but not linked, so we @@ -237,9 +228,6 @@ int cpu_x86_exec(CPUX86State *env1) raise_exception(EXCP06_ILLOP); } *ptb = tb; - tb->size = code_size; - tb->cs_base = (unsigned long)cs_base; - tb->flags = flags; tb->hash_next = NULL; tb_link(tb); code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); @@ -323,7 +311,19 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) saved_env = env; env = s; - load_seg(seg_reg, selector); + if (env->eflags & VM_MASK) { + SegmentCache *sc; + selector &= 0xffff; + sc = &env->seg_cache[seg_reg]; + /* NOTE: in VM86 mode, limit and seg_32bit are never reloaded, + so we must load them here */ + sc->base = (void *)(selector << 4); + sc->limit = 0xffff; + sc->seg_32bit = 0; + env->segs[seg_reg] = selector; + } else { + load_seg(seg_reg, selector, 0); + } env = saved_env; } @@ -346,6 +346,10 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) static inline int handle_cpu_signal(unsigned long pc, unsigned long address, int is_write, sigset_t *old_set) { + TranslationBlock *tb; + int ret; + uint32_t found_pc; + #if defined(DEBUG_SIGNAL) printf("qemu: SIGSEGV pc=0x%08lx address=%08lx wr=%d oldset=0x%08lx\n", pc, address, is_write, *(unsigned long *)old_set); @@ -354,16 +358,18 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, if (is_write && page_unprotect(address)) { return 1; } - if (pc >= (unsigned long)code_gen_buffer && - pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) { + tb = tb_find_pc(pc); + if (tb) { /* 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. */ + ret = cpu_x86_search_pc(tb, &found_pc, pc); + if (ret < 0) + return 0; + env->eip = found_pc - tb->cs_base; env->cr2 = address; + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1)); /* never comes here */ return 1; diff --git a/exec-i386.h b/exec-i386.h index 938680d1e..ba5ea1a9a 100644 --- a/exec-i386.h +++ b/exec-i386.h @@ -217,11 +217,14 @@ typedef struct CCTable { extern CCTable cc_table[]; -void load_seg(int seg_reg, int selector); +void load_seg(int seg_reg, int selector, unsigned cur_eip); void cpu_lock(void); void cpu_unlock(void); +void raise_interrupt(int intno, int is_int, int error_code, + unsigned int next_eip); void raise_exception_err(int exception_index, int error_code); void raise_exception(int exception_index); +void cpu_loop_exit(void); void OPPROTO op_movl_eflags_T0(void); void OPPROTO op_movl_T0_eflags(void); diff --git a/exec.c b/exec.c index 8f332c9ae..2e84f557b 100644 --- a/exec.c +++ b/exec.c @@ -531,3 +531,34 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size) page_unprotect(addr); } } + +/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < + tb[1].tc_ptr. Return NULL if not found */ +TranslationBlock *tb_find_pc(unsigned long tc_ptr) +{ + int m_min, m_max, m; + unsigned long v; + TranslationBlock *tb; + + if (nb_tbs <= 0) + return NULL; + if (tc_ptr < (unsigned long)code_gen_buffer || + tc_ptr >= (unsigned long)code_gen_ptr) + return NULL; + /* binary search (cf Knuth) */ + m_min = 0; + m_max = nb_tbs - 1; + while (m_min <= m_max) { + m = (m_min + m_max) >> 1; + tb = &tbs[m]; + v = (unsigned long)tb->tc_ptr; + if (v == tc_ptr) + return tb; + else if (tc_ptr < v) { + m_max = m - 1; + } else { + m_min = m + 1; + } + } + return &tbs[m_max]; +} diff --git a/exec.h b/exec.h index 29a7ab1fd..18e75e67b 100644 --- a/exec.h +++ b/exec.h @@ -28,10 +28,10 @@ #define GEN_FLAG_IOPL_SHIFT 12 /* same position as eflags */ struct TranslationBlock; -int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, - int *gen_code_size_ptr, - uint8_t *pc_start, uint8_t *cs_base, int flags, - int *code_size_ptr, struct TranslationBlock *tb); +int cpu_x86_gen_code(struct TranslationBlock *tb, + int max_code_size, int *gen_code_size_ptr); +int cpu_x86_search_pc(struct TranslationBlock *tb, + uint32_t *found_pc, unsigned long searched_pc); void cpu_x86_tblocks_init(void); void page_init(void); int page_unprotect(unsigned long address); @@ -161,6 +161,8 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, } } +TranslationBlock *tb_find_pc(unsigned long pc_ptr); + #ifndef offsetof #define offsetof(type, field) ((size_t) &((type *)0)->field) #endif