Loading cpu-i386.h +76 −6 Original line number Diff line number Diff line Loading @@ -450,11 +450,16 @@ extern unsigned long host_page_mask; #define PAGE_EXEC 0x0004 #define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) #define PAGE_VALID 0x0008 /* original state of the write flag (used when tracking self-modifying code */ #define PAGE_WRITE_ORG 0x0010 void page_dump(FILE *f); int page_get_flags(unsigned long address); void page_set_flags(unsigned long start, unsigned long end, int flags); void page_unprotect_range(uint8_t *data, unsigned long data_size); /***************************************************/ /* internal functions */ #define GEN_FLAG_CODE32_SHIFT 0 Loading @@ -468,8 +473,73 @@ void page_set_flags(unsigned long start, unsigned long end, int flags); 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); uint8_t *pc_start, uint8_t *cs_base, int flags, int *code_size_ptr); void cpu_x86_tblocks_init(void); void page_init(void); int page_unprotect(unsigned long address); #define CODE_GEN_MAX_SIZE 65536 #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ #define CODE_GEN_HASH_BITS 15 #define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) /* maximum total translate dcode allocated */ #define CODE_GEN_BUFFER_SIZE (2048 * 1024) //#define CODE_GEN_BUFFER_SIZE (128 * 1024) typedef struct TranslationBlock { unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */ unsigned long cs_base; /* CS base for this block */ unsigned int flags; /* flags defining in which context the code was generated */ uint16_t size; /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ uint8_t *tc_ptr; /* pointer to the translated code */ struct TranslationBlock *hash_next; /* next matching block */ struct TranslationBlock *page_next[2]; /* next blocks in even/odd page */ } TranslationBlock; static inline unsigned int tb_hash_func(unsigned long pc) { return pc & (CODE_GEN_HASH_SIZE - 1); } void tb_flush(void); TranslationBlock *tb_alloc(unsigned long pc, unsigned long size); extern TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; extern uint8_t *code_gen_ptr; /* find a translation block in the translation cache. If not found, 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; h = tb_hash_func(pc); ptb = &tb_hash[h]; for(;;) { tb = *ptb; if (!tb) break; if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags) return tb; ptb = &tb->hash_next; } *pptb = ptb; return NULL; } #ifndef offsetof #define offsetof(type, field) ((size_t) &((type *)0)->field) #endif #endif /* CPU_I386_H */ exec-i386.c +23 −105 Original line number Diff line number Diff line Loading @@ -21,39 +21,10 @@ #include "disas.h" //#define DEBUG_EXEC #define DEBUG_FLUSH //#define DEBUG_SIGNAL /* main execution loop */ /* maximum total translate dcode allocated */ #define CODE_GEN_BUFFER_SIZE (2048 * 1024) //#define CODE_GEN_BUFFER_SIZE (128 * 1024) #define CODE_GEN_MAX_SIZE 65536 #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ /* threshold to flush the translated code buffer */ #define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) #define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) #define CODE_GEN_HASH_BITS 15 #define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) typedef struct TranslationBlock { unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */ unsigned long cs_base; /* CS base for this block */ unsigned int flags; /* flags defining in which context the code was generated */ uint8_t *tc_ptr; /* pointer to the translated code */ struct TranslationBlock *hash_next; /* next matching block */ } TranslationBlock; TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; int nb_tbs; uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; uint8_t *code_gen_ptr; /* thread support */ #ifdef __powerpc__ Loading Loading @@ -195,70 +166,6 @@ void raise_exception(int exception_index) raise_exception_err(exception_index, 0); } void cpu_x86_tblocks_init(void) { if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; } } /* flush all the translation blocks */ static void tb_flush(void) { int i; #ifdef DEBUG_FLUSH printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", code_gen_ptr - code_gen_buffer, nb_tbs, (code_gen_ptr - code_gen_buffer) / nb_tbs); #endif nb_tbs = 0; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) tb_hash[i] = NULL; code_gen_ptr = code_gen_buffer; /* XXX: flush processor icache at this point */ } /* find a translation block in the translation cache. If not found, 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; h = pc & (CODE_GEN_HASH_SIZE - 1); ptb = &tb_hash[h]; #if 0 /* XXX: hack to handle 16 bit modyfing code */ if (flags & (1 << GEN_FLAG_CODE32_SHIFT)) #endif for(;;) { tb = *ptb; if (!tb) break; if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags) 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++]; return tb; } int cpu_x86_exec(CPUX86State *env1) { int saved_T0, saved_T1, saved_A0; Loading Loading @@ -287,7 +194,7 @@ int cpu_x86_exec(CPUX86State *env1) #ifdef reg_EDI int saved_EDI; #endif int code_gen_size, ret; int code_gen_size, ret, code_size; void (*gen_func)(void); TranslationBlock *tb, **ptb; uint8_t *tc_ptr, *cs_base, *pc; Loading Loading @@ -390,15 +297,15 @@ int cpu_x86_exec(CPUX86State *env1) cpu_lock(); tc_ptr = code_gen_ptr; ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, &code_gen_size, pc, cs_base, flags); &code_gen_size, pc, cs_base, flags, &code_size); /* if invalid instruction, signal it */ if (ret != 0) { cpu_unlock(); raise_exception(EXCP06_ILLOP); } tb = tb_alloc(); tb = tb_alloc((unsigned long)pc, code_size); *ptb = tb; tb->pc = (unsigned long)pc; tb->cs_base = (unsigned long)cs_base; tb->flags = flags; tb->tc_ptr = tc_ptr; Loading Loading @@ -493,15 +400,22 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) #include <sys/ucontext.h> /* 'pc' is the host PC at which the exception was raised. 'address' is the effective address of the memory exception */ the effective address of the memory exception. 'is_write' is 1 if a write caused the exception and otherwise 0'. 'old_set' is the signal set which should be restored */ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, int is_write, sigset_t *old_set) { #ifdef DEBUG_SIGNAL printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n", pc, *(unsigned long *)old_set); #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); #endif if (is_write && page_unprotect(address)) { sigprocmask(SIG_SETMASK, old_set, NULL); return 1; } 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 Loading @@ -512,8 +426,7 @@ static inline int handle_cpu_signal(unsigned long pc, /* XXX: need to compute virtual pc position by retranslating code. The rest of the CPU state should be correct. */ env->cr2 = address; /* XXX: more precise exception code */ raise_exception_err(EXCP0E_PAGE, 4); raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1)); /* never comes here */ return 1; } else { Loading @@ -532,10 +445,15 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info, #ifndef REG_EIP /* for glibc 2.1 */ #define REG_EIP EIP #define REG_ERR ERR #define REG_TRAPNO TRAPNO #endif pc = uc->uc_mcontext.gregs[REG_EIP]; pold_set = &uc->uc_sigmask; return handle_cpu_signal(pc, (unsigned long)info->si_addr, pold_set); return handle_cpu_signal(pc, (unsigned long)info->si_addr, uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ? (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, pold_set); #else #warning No CPU specific signal handler: cannot handle target SIGSEGV events return 0; Loading exec.c +301 −12 Original line number Diff line number Diff line /* * virtual page mapping * virtual page mapping and translated block handling * * Copyright (c) 2003 Fabrice Bellard * Loading @@ -24,13 +24,32 @@ #include <errno.h> #include <unistd.h> #include <inttypes.h> #include <sys/mman.h> #include "cpu-i386.h" //#define DEBUG_TB_INVALIDATE #define DEBUG_FLUSH /* make various TB consistency checks */ //#define DEBUG_TB_CHECK /* threshold to flush the translated code buffer */ #define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) #define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; int nb_tbs; uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; uint8_t *code_gen_ptr; /* XXX: pack the flags in the low bits of the pointer ? */ typedef struct PageDesc { struct TranslationBlock *first_tb; unsigned long flags; TranslationBlock *first_tb; } PageDesc; #define L2_BITS 10 Loading @@ -39,6 +58,8 @@ typedef struct PageDesc { #define L1_SIZE (1 << L1_BITS) #define L2_SIZE (1 << L2_BITS) static void tb_invalidate_page(unsigned long address); unsigned long real_host_page_size; unsigned long host_page_bits; unsigned long host_page_size; Loading Loading @@ -104,36 +125,44 @@ void page_dump(FILE *f) } } static inline PageDesc *page_find_alloc(unsigned long address) static inline PageDesc *page_find_alloc(unsigned int index) { unsigned int index; PageDesc **lp, *p; index = address >> TARGET_PAGE_BITS; lp = &l1_map[index >> L2_BITS]; p = *lp; if (!p) { /* allocate if not found */ p = malloc(sizeof(PageDesc) * L2_SIZE); memset(p, 0, sizeof(sizeof(PageDesc) * L2_SIZE)); memset(p, 0, sizeof(PageDesc) * L2_SIZE); *lp = p; } return p + (index & (L2_SIZE - 1)); } int page_get_flags(unsigned long address) static inline PageDesc *page_find(unsigned int index) { unsigned int index; PageDesc *p; index = address >> TARGET_PAGE_BITS; p = l1_map[index >> L2_BITS]; if (!p) return 0; return p[index & (L2_SIZE - 1)].flags; return p + (index & (L2_SIZE - 1)); } int page_get_flags(unsigned long address) { PageDesc *p; p = page_find(address >> TARGET_PAGE_BITS); if (!p) return 0; return p->flags; } /* modify the flags of a page and invalidate the code if necessary. The flag PAGE_WRITE_ORG is positionned automatically depending on PAGE_WRITE */ void page_set_flags(unsigned long start, unsigned long end, int flags) { PageDesc *p; Loading @@ -141,8 +170,268 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) start = start & TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); if (flags & PAGE_WRITE) flags |= PAGE_WRITE_ORG; for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { p = page_find_alloc(addr); p = page_find_alloc(addr >> TARGET_PAGE_BITS); /* if the write protection is set, then we invalidate the code inside */ if (!(p->flags & PAGE_WRITE) && (flags & PAGE_WRITE) && p->first_tb) { tb_invalidate_page(addr); } p->flags = flags; } } void cpu_x86_tblocks_init(void) { if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; } } /* set to NULL all the 'first_tb' fields in all PageDescs */ static void page_flush_tb(void) { int i, j; PageDesc *p; for(i = 0; i < L1_SIZE; i++) { p = l1_map[i]; if (p) { for(j = 0; j < L2_SIZE; j++) p[j].first_tb = NULL; } } } /* flush all the translation blocks */ void tb_flush(void) { int i; #ifdef DEBUG_FLUSH printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", code_gen_ptr - code_gen_buffer, nb_tbs, (code_gen_ptr - code_gen_buffer) / nb_tbs); #endif nb_tbs = 0; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) tb_hash[i] = NULL; page_flush_tb(); code_gen_ptr = code_gen_buffer; /* XXX: flush processor icache at this point */ } #ifdef DEBUG_TB_CHECK static void tb_invalidate_check(unsigned long address) { TranslationBlock *tb; int i; address &= TARGET_PAGE_MASK; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { if (!(address + TARGET_PAGE_SIZE <= tb->pc || address >= tb->pc + tb->size)) { printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n", address, tb->pc, tb->size); } } } } /* verify that all the pages have correct rights for code */ static void tb_page_check(void) { TranslationBlock *tb; int i, flags1, flags2; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { flags1 = page_get_flags(tb->pc); flags2 = page_get_flags(tb->pc + tb->size - 1); if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", tb->pc, tb->size, flags1, flags2); } } } } #endif /* invalidate one TB */ static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb, int next_offset) { TranslationBlock *tb1; for(;;) { tb1 = *ptb; if (tb1 == tb) { *ptb = *(TranslationBlock **)((char *)tb1 + next_offset); break; } ptb = (TranslationBlock **)((char *)tb1 + next_offset); } } static inline void tb_invalidate(TranslationBlock *tb, int parity) { PageDesc *p; unsigned int page_index1, page_index2; unsigned int h; /* remove the TB from the hash list */ h = tb_hash_func(tb->pc); tb_remove(&tb_hash[h], tb, offsetof(TranslationBlock, hash_next)); /* remove the TB from the page list */ page_index1 = tb->pc >> TARGET_PAGE_BITS; if ((page_index1 & 1) == parity) { p = page_find(page_index1); tb_remove(&p->first_tb, tb, offsetof(TranslationBlock, page_next[page_index1 & 1])); } page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; if ((page_index2 & 1) == parity) { p = page_find(page_index2); tb_remove(&p->first_tb, tb, offsetof(TranslationBlock, page_next[page_index2 & 1])); } } /* invalidate all TBs which intersect with the target page starting at addr */ static void tb_invalidate_page(unsigned long address) { TranslationBlock *tb_next, *tb; unsigned int page_index; int parity1, parity2; PageDesc *p; #ifdef DEBUG_TB_INVALIDATE printf("tb_invalidate_page: %lx\n", address); #endif page_index = address >> TARGET_PAGE_BITS; p = page_find(page_index); if (!p) return; tb = p->first_tb; parity1 = page_index & 1; parity2 = parity1 ^ 1; while (tb != NULL) { tb_next = tb->page_next[parity1]; tb_invalidate(tb, parity2); tb = tb_next; } p->first_tb = NULL; } /* add the tb in the target page and protect it if necessary */ static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index) { PageDesc *p; unsigned long host_start, host_end, addr, page_addr; int prot; p = page_find_alloc(page_index); tb->page_next[page_index & 1] = p->first_tb; p->first_tb = tb; if (p->flags & PAGE_WRITE) { /* force the host page as non writable (writes will have a page fault + mprotect overhead) */ page_addr = (page_index << TARGET_PAGE_BITS); host_start = page_addr & host_page_mask; host_end = host_start + host_page_size; prot = 0; for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) prot |= page_get_flags(addr); mprotect((void *)host_start, host_page_size, (prot & PAGE_BITS) & ~PAGE_WRITE); #ifdef DEBUG_TB_INVALIDATE printf("protecting code page: 0x%08lx\n", host_start); #endif p->flags &= ~PAGE_WRITE; #ifdef DEBUG_TB_CHECK tb_page_check(); #endif } } /* Allocate a new translation block. Flush the translation buffer if too many translation blocks or too much generated code. */ TranslationBlock *tb_alloc(unsigned long pc, unsigned long size) { TranslationBlock *tb; unsigned int page_index1, page_index2; if (nb_tbs >= CODE_GEN_MAX_BLOCKS || (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) tb_flush(); tb = &tbs[nb_tbs++]; tb->pc = pc; tb->size = size; /* add in the page list */ page_index1 = pc >> TARGET_PAGE_BITS; tb_alloc_page(tb, page_index1); page_index2 = (pc + size - 1) >> TARGET_PAGE_BITS; if (page_index2 != page_index1) { tb_alloc_page(tb, page_index2); } return tb; } /* called from signal handler: invalidate the code and unprotect the page. Return TRUE if the fault was succesfully handled. */ int page_unprotect(unsigned long address) { unsigned int page_index, prot; PageDesc *p; unsigned long host_start, host_end, addr; page_index = address >> TARGET_PAGE_BITS; p = page_find(page_index); if (!p) return 0; if ((p->flags & (PAGE_WRITE_ORG | PAGE_WRITE)) == PAGE_WRITE_ORG) { /* if the page was really writable, then we change its protection back to writable */ host_start = address & host_page_mask; host_end = host_start + host_page_size; prot = 0; for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) prot |= page_get_flags(addr); mprotect((void *)host_start, host_page_size, (prot & PAGE_BITS) | PAGE_WRITE); p->flags |= PAGE_WRITE; /* and since the content will be modified, we must invalidate the corresponding translated code. */ tb_invalidate_page(address); #ifdef DEBUG_TB_CHECK tb_invalidate_check(address); #endif return 1; } else { return 0; } } /* call this function when system calls directly modify a memory area */ void page_unprotect_range(uint8_t *data, unsigned long data_size) { unsigned long start, end, addr; start = (unsigned long)data; end = start + data_size; start &= TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { page_unprotect(addr); } } Loading
cpu-i386.h +76 −6 Original line number Diff line number Diff line Loading @@ -450,11 +450,16 @@ extern unsigned long host_page_mask; #define PAGE_EXEC 0x0004 #define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) #define PAGE_VALID 0x0008 /* original state of the write flag (used when tracking self-modifying code */ #define PAGE_WRITE_ORG 0x0010 void page_dump(FILE *f); int page_get_flags(unsigned long address); void page_set_flags(unsigned long start, unsigned long end, int flags); void page_unprotect_range(uint8_t *data, unsigned long data_size); /***************************************************/ /* internal functions */ #define GEN_FLAG_CODE32_SHIFT 0 Loading @@ -468,8 +473,73 @@ void page_set_flags(unsigned long start, unsigned long end, int flags); 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); uint8_t *pc_start, uint8_t *cs_base, int flags, int *code_size_ptr); void cpu_x86_tblocks_init(void); void page_init(void); int page_unprotect(unsigned long address); #define CODE_GEN_MAX_SIZE 65536 #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ #define CODE_GEN_HASH_BITS 15 #define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) /* maximum total translate dcode allocated */ #define CODE_GEN_BUFFER_SIZE (2048 * 1024) //#define CODE_GEN_BUFFER_SIZE (128 * 1024) typedef struct TranslationBlock { unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */ unsigned long cs_base; /* CS base for this block */ unsigned int flags; /* flags defining in which context the code was generated */ uint16_t size; /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ uint8_t *tc_ptr; /* pointer to the translated code */ struct TranslationBlock *hash_next; /* next matching block */ struct TranslationBlock *page_next[2]; /* next blocks in even/odd page */ } TranslationBlock; static inline unsigned int tb_hash_func(unsigned long pc) { return pc & (CODE_GEN_HASH_SIZE - 1); } void tb_flush(void); TranslationBlock *tb_alloc(unsigned long pc, unsigned long size); extern TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; extern uint8_t *code_gen_ptr; /* find a translation block in the translation cache. If not found, 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; h = tb_hash_func(pc); ptb = &tb_hash[h]; for(;;) { tb = *ptb; if (!tb) break; if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags) return tb; ptb = &tb->hash_next; } *pptb = ptb; return NULL; } #ifndef offsetof #define offsetof(type, field) ((size_t) &((type *)0)->field) #endif #endif /* CPU_I386_H */
exec-i386.c +23 −105 Original line number Diff line number Diff line Loading @@ -21,39 +21,10 @@ #include "disas.h" //#define DEBUG_EXEC #define DEBUG_FLUSH //#define DEBUG_SIGNAL /* main execution loop */ /* maximum total translate dcode allocated */ #define CODE_GEN_BUFFER_SIZE (2048 * 1024) //#define CODE_GEN_BUFFER_SIZE (128 * 1024) #define CODE_GEN_MAX_SIZE 65536 #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ /* threshold to flush the translated code buffer */ #define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) #define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) #define CODE_GEN_HASH_BITS 15 #define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) typedef struct TranslationBlock { unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */ unsigned long cs_base; /* CS base for this block */ unsigned int flags; /* flags defining in which context the code was generated */ uint8_t *tc_ptr; /* pointer to the translated code */ struct TranslationBlock *hash_next; /* next matching block */ } TranslationBlock; TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; int nb_tbs; uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; uint8_t *code_gen_ptr; /* thread support */ #ifdef __powerpc__ Loading Loading @@ -195,70 +166,6 @@ void raise_exception(int exception_index) raise_exception_err(exception_index, 0); } void cpu_x86_tblocks_init(void) { if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; } } /* flush all the translation blocks */ static void tb_flush(void) { int i; #ifdef DEBUG_FLUSH printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", code_gen_ptr - code_gen_buffer, nb_tbs, (code_gen_ptr - code_gen_buffer) / nb_tbs); #endif nb_tbs = 0; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) tb_hash[i] = NULL; code_gen_ptr = code_gen_buffer; /* XXX: flush processor icache at this point */ } /* find a translation block in the translation cache. If not found, 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; h = pc & (CODE_GEN_HASH_SIZE - 1); ptb = &tb_hash[h]; #if 0 /* XXX: hack to handle 16 bit modyfing code */ if (flags & (1 << GEN_FLAG_CODE32_SHIFT)) #endif for(;;) { tb = *ptb; if (!tb) break; if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags) 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++]; return tb; } int cpu_x86_exec(CPUX86State *env1) { int saved_T0, saved_T1, saved_A0; Loading Loading @@ -287,7 +194,7 @@ int cpu_x86_exec(CPUX86State *env1) #ifdef reg_EDI int saved_EDI; #endif int code_gen_size, ret; int code_gen_size, ret, code_size; void (*gen_func)(void); TranslationBlock *tb, **ptb; uint8_t *tc_ptr, *cs_base, *pc; Loading Loading @@ -390,15 +297,15 @@ int cpu_x86_exec(CPUX86State *env1) cpu_lock(); tc_ptr = code_gen_ptr; ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, &code_gen_size, pc, cs_base, flags); &code_gen_size, pc, cs_base, flags, &code_size); /* if invalid instruction, signal it */ if (ret != 0) { cpu_unlock(); raise_exception(EXCP06_ILLOP); } tb = tb_alloc(); tb = tb_alloc((unsigned long)pc, code_size); *ptb = tb; tb->pc = (unsigned long)pc; tb->cs_base = (unsigned long)cs_base; tb->flags = flags; tb->tc_ptr = tc_ptr; Loading Loading @@ -493,15 +400,22 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) #include <sys/ucontext.h> /* 'pc' is the host PC at which the exception was raised. 'address' is the effective address of the memory exception */ the effective address of the memory exception. 'is_write' is 1 if a write caused the exception and otherwise 0'. 'old_set' is the signal set which should be restored */ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, int is_write, sigset_t *old_set) { #ifdef DEBUG_SIGNAL printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n", pc, *(unsigned long *)old_set); #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); #endif if (is_write && page_unprotect(address)) { sigprocmask(SIG_SETMASK, old_set, NULL); return 1; } 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 Loading @@ -512,8 +426,7 @@ static inline int handle_cpu_signal(unsigned long pc, /* XXX: need to compute virtual pc position by retranslating code. The rest of the CPU state should be correct. */ env->cr2 = address; /* XXX: more precise exception code */ raise_exception_err(EXCP0E_PAGE, 4); raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1)); /* never comes here */ return 1; } else { Loading @@ -532,10 +445,15 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info, #ifndef REG_EIP /* for glibc 2.1 */ #define REG_EIP EIP #define REG_ERR ERR #define REG_TRAPNO TRAPNO #endif pc = uc->uc_mcontext.gregs[REG_EIP]; pold_set = &uc->uc_sigmask; return handle_cpu_signal(pc, (unsigned long)info->si_addr, pold_set); return handle_cpu_signal(pc, (unsigned long)info->si_addr, uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ? (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, pold_set); #else #warning No CPU specific signal handler: cannot handle target SIGSEGV events return 0; Loading
exec.c +301 −12 Original line number Diff line number Diff line /* * virtual page mapping * virtual page mapping and translated block handling * * Copyright (c) 2003 Fabrice Bellard * Loading @@ -24,13 +24,32 @@ #include <errno.h> #include <unistd.h> #include <inttypes.h> #include <sys/mman.h> #include "cpu-i386.h" //#define DEBUG_TB_INVALIDATE #define DEBUG_FLUSH /* make various TB consistency checks */ //#define DEBUG_TB_CHECK /* threshold to flush the translated code buffer */ #define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) #define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; int nb_tbs; uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; uint8_t *code_gen_ptr; /* XXX: pack the flags in the low bits of the pointer ? */ typedef struct PageDesc { struct TranslationBlock *first_tb; unsigned long flags; TranslationBlock *first_tb; } PageDesc; #define L2_BITS 10 Loading @@ -39,6 +58,8 @@ typedef struct PageDesc { #define L1_SIZE (1 << L1_BITS) #define L2_SIZE (1 << L2_BITS) static void tb_invalidate_page(unsigned long address); unsigned long real_host_page_size; unsigned long host_page_bits; unsigned long host_page_size; Loading Loading @@ -104,36 +125,44 @@ void page_dump(FILE *f) } } static inline PageDesc *page_find_alloc(unsigned long address) static inline PageDesc *page_find_alloc(unsigned int index) { unsigned int index; PageDesc **lp, *p; index = address >> TARGET_PAGE_BITS; lp = &l1_map[index >> L2_BITS]; p = *lp; if (!p) { /* allocate if not found */ p = malloc(sizeof(PageDesc) * L2_SIZE); memset(p, 0, sizeof(sizeof(PageDesc) * L2_SIZE)); memset(p, 0, sizeof(PageDesc) * L2_SIZE); *lp = p; } return p + (index & (L2_SIZE - 1)); } int page_get_flags(unsigned long address) static inline PageDesc *page_find(unsigned int index) { unsigned int index; PageDesc *p; index = address >> TARGET_PAGE_BITS; p = l1_map[index >> L2_BITS]; if (!p) return 0; return p[index & (L2_SIZE - 1)].flags; return p + (index & (L2_SIZE - 1)); } int page_get_flags(unsigned long address) { PageDesc *p; p = page_find(address >> TARGET_PAGE_BITS); if (!p) return 0; return p->flags; } /* modify the flags of a page and invalidate the code if necessary. The flag PAGE_WRITE_ORG is positionned automatically depending on PAGE_WRITE */ void page_set_flags(unsigned long start, unsigned long end, int flags) { PageDesc *p; Loading @@ -141,8 +170,268 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) start = start & TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); if (flags & PAGE_WRITE) flags |= PAGE_WRITE_ORG; for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { p = page_find_alloc(addr); p = page_find_alloc(addr >> TARGET_PAGE_BITS); /* if the write protection is set, then we invalidate the code inside */ if (!(p->flags & PAGE_WRITE) && (flags & PAGE_WRITE) && p->first_tb) { tb_invalidate_page(addr); } p->flags = flags; } } void cpu_x86_tblocks_init(void) { if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; } } /* set to NULL all the 'first_tb' fields in all PageDescs */ static void page_flush_tb(void) { int i, j; PageDesc *p; for(i = 0; i < L1_SIZE; i++) { p = l1_map[i]; if (p) { for(j = 0; j < L2_SIZE; j++) p[j].first_tb = NULL; } } } /* flush all the translation blocks */ void tb_flush(void) { int i; #ifdef DEBUG_FLUSH printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", code_gen_ptr - code_gen_buffer, nb_tbs, (code_gen_ptr - code_gen_buffer) / nb_tbs); #endif nb_tbs = 0; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) tb_hash[i] = NULL; page_flush_tb(); code_gen_ptr = code_gen_buffer; /* XXX: flush processor icache at this point */ } #ifdef DEBUG_TB_CHECK static void tb_invalidate_check(unsigned long address) { TranslationBlock *tb; int i; address &= TARGET_PAGE_MASK; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { if (!(address + TARGET_PAGE_SIZE <= tb->pc || address >= tb->pc + tb->size)) { printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n", address, tb->pc, tb->size); } } } } /* verify that all the pages have correct rights for code */ static void tb_page_check(void) { TranslationBlock *tb; int i, flags1, flags2; for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { flags1 = page_get_flags(tb->pc); flags2 = page_get_flags(tb->pc + tb->size - 1); if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", tb->pc, tb->size, flags1, flags2); } } } } #endif /* invalidate one TB */ static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb, int next_offset) { TranslationBlock *tb1; for(;;) { tb1 = *ptb; if (tb1 == tb) { *ptb = *(TranslationBlock **)((char *)tb1 + next_offset); break; } ptb = (TranslationBlock **)((char *)tb1 + next_offset); } } static inline void tb_invalidate(TranslationBlock *tb, int parity) { PageDesc *p; unsigned int page_index1, page_index2; unsigned int h; /* remove the TB from the hash list */ h = tb_hash_func(tb->pc); tb_remove(&tb_hash[h], tb, offsetof(TranslationBlock, hash_next)); /* remove the TB from the page list */ page_index1 = tb->pc >> TARGET_PAGE_BITS; if ((page_index1 & 1) == parity) { p = page_find(page_index1); tb_remove(&p->first_tb, tb, offsetof(TranslationBlock, page_next[page_index1 & 1])); } page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; if ((page_index2 & 1) == parity) { p = page_find(page_index2); tb_remove(&p->first_tb, tb, offsetof(TranslationBlock, page_next[page_index2 & 1])); } } /* invalidate all TBs which intersect with the target page starting at addr */ static void tb_invalidate_page(unsigned long address) { TranslationBlock *tb_next, *tb; unsigned int page_index; int parity1, parity2; PageDesc *p; #ifdef DEBUG_TB_INVALIDATE printf("tb_invalidate_page: %lx\n", address); #endif page_index = address >> TARGET_PAGE_BITS; p = page_find(page_index); if (!p) return; tb = p->first_tb; parity1 = page_index & 1; parity2 = parity1 ^ 1; while (tb != NULL) { tb_next = tb->page_next[parity1]; tb_invalidate(tb, parity2); tb = tb_next; } p->first_tb = NULL; } /* add the tb in the target page and protect it if necessary */ static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index) { PageDesc *p; unsigned long host_start, host_end, addr, page_addr; int prot; p = page_find_alloc(page_index); tb->page_next[page_index & 1] = p->first_tb; p->first_tb = tb; if (p->flags & PAGE_WRITE) { /* force the host page as non writable (writes will have a page fault + mprotect overhead) */ page_addr = (page_index << TARGET_PAGE_BITS); host_start = page_addr & host_page_mask; host_end = host_start + host_page_size; prot = 0; for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) prot |= page_get_flags(addr); mprotect((void *)host_start, host_page_size, (prot & PAGE_BITS) & ~PAGE_WRITE); #ifdef DEBUG_TB_INVALIDATE printf("protecting code page: 0x%08lx\n", host_start); #endif p->flags &= ~PAGE_WRITE; #ifdef DEBUG_TB_CHECK tb_page_check(); #endif } } /* Allocate a new translation block. Flush the translation buffer if too many translation blocks or too much generated code. */ TranslationBlock *tb_alloc(unsigned long pc, unsigned long size) { TranslationBlock *tb; unsigned int page_index1, page_index2; if (nb_tbs >= CODE_GEN_MAX_BLOCKS || (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) tb_flush(); tb = &tbs[nb_tbs++]; tb->pc = pc; tb->size = size; /* add in the page list */ page_index1 = pc >> TARGET_PAGE_BITS; tb_alloc_page(tb, page_index1); page_index2 = (pc + size - 1) >> TARGET_PAGE_BITS; if (page_index2 != page_index1) { tb_alloc_page(tb, page_index2); } return tb; } /* called from signal handler: invalidate the code and unprotect the page. Return TRUE if the fault was succesfully handled. */ int page_unprotect(unsigned long address) { unsigned int page_index, prot; PageDesc *p; unsigned long host_start, host_end, addr; page_index = address >> TARGET_PAGE_BITS; p = page_find(page_index); if (!p) return 0; if ((p->flags & (PAGE_WRITE_ORG | PAGE_WRITE)) == PAGE_WRITE_ORG) { /* if the page was really writable, then we change its protection back to writable */ host_start = address & host_page_mask; host_end = host_start + host_page_size; prot = 0; for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) prot |= page_get_flags(addr); mprotect((void *)host_start, host_page_size, (prot & PAGE_BITS) | PAGE_WRITE); p->flags |= PAGE_WRITE; /* and since the content will be modified, we must invalidate the corresponding translated code. */ tb_invalidate_page(address); #ifdef DEBUG_TB_CHECK tb_invalidate_check(address); #endif return 1; } else { return 0; } } /* call this function when system calls directly modify a memory area */ void page_unprotect_range(uint8_t *data, unsigned long data_size) { unsigned long start, end, addr; start = (unsigned long)data; end = start + data_size; start &= TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { page_unprotect(addr); } }