Commit fd6ce8f6604359e60283e6d4dfc935ca57c556e5
1 parent
727d01d4
self-modifying code support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@163 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
400 additions
and
123 deletions
cpu-i386.h
@@ -445,16 +445,21 @@ extern unsigned long host_page_mask; | @@ -445,16 +445,21 @@ extern unsigned long host_page_mask; | ||
445 | #define HOST_PAGE_ALIGN(addr) (((addr) + host_page_size - 1) & host_page_mask) | 445 | #define HOST_PAGE_ALIGN(addr) (((addr) + host_page_size - 1) & host_page_mask) |
446 | 446 | ||
447 | /* same as PROT_xxx */ | 447 | /* same as PROT_xxx */ |
448 | -#define PAGE_READ 0x0001 | ||
449 | -#define PAGE_WRITE 0x0002 | ||
450 | -#define PAGE_EXEC 0x0004 | ||
451 | -#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) | ||
452 | -#define PAGE_VALID 0x0008 | 448 | +#define PAGE_READ 0x0001 |
449 | +#define PAGE_WRITE 0x0002 | ||
450 | +#define PAGE_EXEC 0x0004 | ||
451 | +#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) | ||
452 | +#define PAGE_VALID 0x0008 | ||
453 | +/* original state of the write flag (used when tracking self-modifying | ||
454 | + code */ | ||
455 | +#define PAGE_WRITE_ORG 0x0010 | ||
453 | 456 | ||
454 | void page_dump(FILE *f); | 457 | void page_dump(FILE *f); |
455 | int page_get_flags(unsigned long address); | 458 | int page_get_flags(unsigned long address); |
456 | void page_set_flags(unsigned long start, unsigned long end, int flags); | 459 | void page_set_flags(unsigned long start, unsigned long end, int flags); |
460 | +void page_unprotect_range(uint8_t *data, unsigned long data_size); | ||
457 | 461 | ||
462 | +/***************************************************/ | ||
458 | /* internal functions */ | 463 | /* internal functions */ |
459 | 464 | ||
460 | #define GEN_FLAG_CODE32_SHIFT 0 | 465 | #define GEN_FLAG_CODE32_SHIFT 0 |
@@ -468,8 +473,73 @@ void page_set_flags(unsigned long start, unsigned long end, int flags); | @@ -468,8 +473,73 @@ void page_set_flags(unsigned long start, unsigned long end, int flags); | ||
468 | 473 | ||
469 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, | 474 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
470 | int *gen_code_size_ptr, | 475 | int *gen_code_size_ptr, |
471 | - uint8_t *pc_start, uint8_t *cs_base, int flags); | 476 | + uint8_t *pc_start, uint8_t *cs_base, int flags, |
477 | + int *code_size_ptr); | ||
472 | void cpu_x86_tblocks_init(void); | 478 | void cpu_x86_tblocks_init(void); |
473 | void page_init(void); | 479 | void page_init(void); |
480 | +int page_unprotect(unsigned long address); | ||
481 | + | ||
482 | +#define CODE_GEN_MAX_SIZE 65536 | ||
483 | +#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ | ||
484 | + | ||
485 | +#define CODE_GEN_HASH_BITS 15 | ||
486 | +#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) | ||
487 | + | ||
488 | +/* maximum total translate dcode allocated */ | ||
489 | +#define CODE_GEN_BUFFER_SIZE (2048 * 1024) | ||
490 | +//#define CODE_GEN_BUFFER_SIZE (128 * 1024) | ||
491 | + | ||
492 | +typedef struct TranslationBlock { | ||
493 | + unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */ | ||
494 | + unsigned long cs_base; /* CS base for this block */ | ||
495 | + unsigned int flags; /* flags defining in which context the code was generated */ | ||
496 | + uint16_t size; /* size of target code for this block (1 <= | ||
497 | + size <= TARGET_PAGE_SIZE) */ | ||
498 | + uint8_t *tc_ptr; /* pointer to the translated code */ | ||
499 | + struct TranslationBlock *hash_next; /* next matching block */ | ||
500 | + struct TranslationBlock *page_next[2]; /* next blocks in even/odd page */ | ||
501 | +} TranslationBlock; | ||
502 | + | ||
503 | +static inline unsigned int tb_hash_func(unsigned long pc) | ||
504 | +{ | ||
505 | + return pc & (CODE_GEN_HASH_SIZE - 1); | ||
506 | +} | ||
507 | + | ||
508 | +void tb_flush(void); | ||
509 | +TranslationBlock *tb_alloc(unsigned long pc, | ||
510 | + unsigned long size); | ||
511 | + | ||
512 | +extern TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; | ||
513 | + | ||
514 | +extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; | ||
515 | +extern uint8_t *code_gen_ptr; | ||
516 | + | ||
517 | +/* find a translation block in the translation cache. If not found, | ||
518 | + return NULL and the pointer to the last element of the list in pptb */ | ||
519 | +static inline TranslationBlock *tb_find(TranslationBlock ***pptb, | ||
520 | + unsigned long pc, | ||
521 | + unsigned long cs_base, | ||
522 | + unsigned int flags) | ||
523 | +{ | ||
524 | + TranslationBlock **ptb, *tb; | ||
525 | + unsigned int h; | ||
526 | + | ||
527 | + h = tb_hash_func(pc); | ||
528 | + ptb = &tb_hash[h]; | ||
529 | + for(;;) { | ||
530 | + tb = *ptb; | ||
531 | + if (!tb) | ||
532 | + break; | ||
533 | + if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags) | ||
534 | + return tb; | ||
535 | + ptb = &tb->hash_next; | ||
536 | + } | ||
537 | + *pptb = ptb; | ||
538 | + return NULL; | ||
539 | +} | ||
540 | + | ||
541 | +#ifndef offsetof | ||
542 | +#define offsetof(type, field) ((size_t) &((type *)0)->field) | ||
543 | +#endif | ||
474 | 544 | ||
475 | #endif /* CPU_I386_H */ | 545 | #endif /* CPU_I386_H */ |
exec-i386.c
@@ -21,39 +21,10 @@ | @@ -21,39 +21,10 @@ | ||
21 | #include "disas.h" | 21 | #include "disas.h" |
22 | 22 | ||
23 | //#define DEBUG_EXEC | 23 | //#define DEBUG_EXEC |
24 | -#define DEBUG_FLUSH | ||
25 | //#define DEBUG_SIGNAL | 24 | //#define DEBUG_SIGNAL |
26 | 25 | ||
27 | /* main execution loop */ | 26 | /* main execution loop */ |
28 | 27 | ||
29 | -/* maximum total translate dcode allocated */ | ||
30 | -#define CODE_GEN_BUFFER_SIZE (2048 * 1024) | ||
31 | -//#define CODE_GEN_BUFFER_SIZE (128 * 1024) | ||
32 | -#define CODE_GEN_MAX_SIZE 65536 | ||
33 | -#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ | ||
34 | - | ||
35 | -/* threshold to flush the translated code buffer */ | ||
36 | -#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) | ||
37 | - | ||
38 | -#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) | ||
39 | -#define CODE_GEN_HASH_BITS 15 | ||
40 | -#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) | ||
41 | - | ||
42 | -typedef struct TranslationBlock { | ||
43 | - unsigned long pc; /* simulated PC corresponding to this block (EIP + CS base) */ | ||
44 | - unsigned long cs_base; /* CS base for this block */ | ||
45 | - unsigned int flags; /* flags defining in which context the code was generated */ | ||
46 | - uint8_t *tc_ptr; /* pointer to the translated code */ | ||
47 | - struct TranslationBlock *hash_next; /* next matching block */ | ||
48 | -} TranslationBlock; | ||
49 | - | ||
50 | -TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; | ||
51 | -TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; | ||
52 | -int nb_tbs; | ||
53 | - | ||
54 | -uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; | ||
55 | -uint8_t *code_gen_ptr; | ||
56 | - | ||
57 | /* thread support */ | 28 | /* thread support */ |
58 | 29 | ||
59 | #ifdef __powerpc__ | 30 | #ifdef __powerpc__ |
@@ -195,70 +166,6 @@ void raise_exception(int exception_index) | @@ -195,70 +166,6 @@ void raise_exception(int exception_index) | ||
195 | raise_exception_err(exception_index, 0); | 166 | raise_exception_err(exception_index, 0); |
196 | } | 167 | } |
197 | 168 | ||
198 | -void cpu_x86_tblocks_init(void) | ||
199 | -{ | ||
200 | - if (!code_gen_ptr) { | ||
201 | - code_gen_ptr = code_gen_buffer; | ||
202 | - } | ||
203 | -} | ||
204 | - | ||
205 | -/* flush all the translation blocks */ | ||
206 | -static void tb_flush(void) | ||
207 | -{ | ||
208 | - int i; | ||
209 | -#ifdef DEBUG_FLUSH | ||
210 | - printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", | ||
211 | - code_gen_ptr - code_gen_buffer, | ||
212 | - nb_tbs, | ||
213 | - (code_gen_ptr - code_gen_buffer) / nb_tbs); | ||
214 | -#endif | ||
215 | - nb_tbs = 0; | ||
216 | - for(i = 0;i < CODE_GEN_HASH_SIZE; i++) | ||
217 | - tb_hash[i] = NULL; | ||
218 | - code_gen_ptr = code_gen_buffer; | ||
219 | - /* XXX: flush processor icache at this point */ | ||
220 | -} | ||
221 | - | ||
222 | -/* find a translation block in the translation cache. If not found, | ||
223 | - return NULL and the pointer to the last element of the list in pptb */ | ||
224 | -static inline TranslationBlock *tb_find(TranslationBlock ***pptb, | ||
225 | - unsigned long pc, | ||
226 | - unsigned long cs_base, | ||
227 | - unsigned int flags) | ||
228 | -{ | ||
229 | - TranslationBlock **ptb, *tb; | ||
230 | - unsigned int h; | ||
231 | - | ||
232 | - h = pc & (CODE_GEN_HASH_SIZE - 1); | ||
233 | - ptb = &tb_hash[h]; | ||
234 | -#if 0 | ||
235 | - /* XXX: hack to handle 16 bit modyfing code */ | ||
236 | - if (flags & (1 << GEN_FLAG_CODE32_SHIFT)) | ||
237 | -#endif | ||
238 | - for(;;) { | ||
239 | - tb = *ptb; | ||
240 | - if (!tb) | ||
241 | - break; | ||
242 | - if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags) | ||
243 | - return tb; | ||
244 | - ptb = &tb->hash_next; | ||
245 | - } | ||
246 | - *pptb = ptb; | ||
247 | - return NULL; | ||
248 | -} | ||
249 | - | ||
250 | -/* allocate a new translation block. flush the translation buffer if | ||
251 | - too many translation blocks or too much generated code */ | ||
252 | -static inline TranslationBlock *tb_alloc(void) | ||
253 | -{ | ||
254 | - TranslationBlock *tb; | ||
255 | - if (nb_tbs >= CODE_GEN_MAX_BLOCKS || | ||
256 | - (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) | ||
257 | - tb_flush(); | ||
258 | - tb = &tbs[nb_tbs++]; | ||
259 | - return tb; | ||
260 | -} | ||
261 | - | ||
262 | int cpu_x86_exec(CPUX86State *env1) | 169 | int cpu_x86_exec(CPUX86State *env1) |
263 | { | 170 | { |
264 | int saved_T0, saved_T1, saved_A0; | 171 | int saved_T0, saved_T1, saved_A0; |
@@ -287,7 +194,7 @@ int cpu_x86_exec(CPUX86State *env1) | @@ -287,7 +194,7 @@ int cpu_x86_exec(CPUX86State *env1) | ||
287 | #ifdef reg_EDI | 194 | #ifdef reg_EDI |
288 | int saved_EDI; | 195 | int saved_EDI; |
289 | #endif | 196 | #endif |
290 | - int code_gen_size, ret; | 197 | + int code_gen_size, ret, code_size; |
291 | void (*gen_func)(void); | 198 | void (*gen_func)(void); |
292 | TranslationBlock *tb, **ptb; | 199 | TranslationBlock *tb, **ptb; |
293 | uint8_t *tc_ptr, *cs_base, *pc; | 200 | uint8_t *tc_ptr, *cs_base, *pc; |
@@ -390,15 +297,15 @@ int cpu_x86_exec(CPUX86State *env1) | @@ -390,15 +297,15 @@ int cpu_x86_exec(CPUX86State *env1) | ||
390 | cpu_lock(); | 297 | cpu_lock(); |
391 | tc_ptr = code_gen_ptr; | 298 | tc_ptr = code_gen_ptr; |
392 | ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, | 299 | ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, |
393 | - &code_gen_size, pc, cs_base, flags); | 300 | + &code_gen_size, pc, cs_base, flags, |
301 | + &code_size); | ||
394 | /* if invalid instruction, signal it */ | 302 | /* if invalid instruction, signal it */ |
395 | if (ret != 0) { | 303 | if (ret != 0) { |
396 | cpu_unlock(); | 304 | cpu_unlock(); |
397 | raise_exception(EXCP06_ILLOP); | 305 | raise_exception(EXCP06_ILLOP); |
398 | } | 306 | } |
399 | - tb = tb_alloc(); | 307 | + tb = tb_alloc((unsigned long)pc, code_size); |
400 | *ptb = tb; | 308 | *ptb = tb; |
401 | - tb->pc = (unsigned long)pc; | ||
402 | tb->cs_base = (unsigned long)cs_base; | 309 | tb->cs_base = (unsigned long)cs_base; |
403 | tb->flags = flags; | 310 | tb->flags = flags; |
404 | tb->tc_ptr = tc_ptr; | 311 | tb->tc_ptr = tc_ptr; |
@@ -493,15 +400,22 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) | @@ -493,15 +400,22 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) | ||
493 | #include <sys/ucontext.h> | 400 | #include <sys/ucontext.h> |
494 | 401 | ||
495 | /* 'pc' is the host PC at which the exception was raised. 'address' is | 402 | /* 'pc' is the host PC at which the exception was raised. 'address' is |
496 | - the effective address of the memory exception */ | 403 | + the effective address of the memory exception. 'is_write' is 1 if a |
404 | + write caused the exception and otherwise 0'. 'old_set' is the | ||
405 | + signal set which should be restored */ | ||
497 | static inline int handle_cpu_signal(unsigned long pc, | 406 | static inline int handle_cpu_signal(unsigned long pc, |
498 | unsigned long address, | 407 | unsigned long address, |
408 | + int is_write, | ||
499 | sigset_t *old_set) | 409 | sigset_t *old_set) |
500 | { | 410 | { |
501 | -#ifdef DEBUG_SIGNAL | ||
502 | - printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n", | ||
503 | - pc, *(unsigned long *)old_set); | 411 | +#if defined(DEBUG_SIGNAL) |
412 | + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx wr=%d oldset=0x%08lx\n", | ||
413 | + pc, address, is_write, *(unsigned long *)old_set); | ||
504 | #endif | 414 | #endif |
415 | + if (is_write && page_unprotect(address)) { | ||
416 | + sigprocmask(SIG_SETMASK, old_set, NULL); | ||
417 | + return 1; | ||
418 | + } | ||
505 | if (pc >= (unsigned long)code_gen_buffer && | 419 | if (pc >= (unsigned long)code_gen_buffer && |
506 | pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) { | 420 | pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) { |
507 | /* the PC is inside the translated code. It means that we have | 421 | /* the PC is inside the translated code. It means that we have |
@@ -512,8 +426,7 @@ static inline int handle_cpu_signal(unsigned long pc, | @@ -512,8 +426,7 @@ static inline int handle_cpu_signal(unsigned long pc, | ||
512 | /* XXX: need to compute virtual pc position by retranslating | 426 | /* XXX: need to compute virtual pc position by retranslating |
513 | code. The rest of the CPU state should be correct. */ | 427 | code. The rest of the CPU state should be correct. */ |
514 | env->cr2 = address; | 428 | env->cr2 = address; |
515 | - /* XXX: more precise exception code */ | ||
516 | - raise_exception_err(EXCP0E_PAGE, 4); | 429 | + raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1)); |
517 | /* never comes here */ | 430 | /* never comes here */ |
518 | return 1; | 431 | return 1; |
519 | } else { | 432 | } else { |
@@ -531,11 +444,16 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info, | @@ -531,11 +444,16 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info, | ||
531 | 444 | ||
532 | #ifndef REG_EIP | 445 | #ifndef REG_EIP |
533 | /* for glibc 2.1 */ | 446 | /* for glibc 2.1 */ |
534 | -#define REG_EIP EIP | 447 | +#define REG_EIP EIP |
448 | +#define REG_ERR ERR | ||
449 | +#define REG_TRAPNO TRAPNO | ||
535 | #endif | 450 | #endif |
536 | pc = uc->uc_mcontext.gregs[REG_EIP]; | 451 | pc = uc->uc_mcontext.gregs[REG_EIP]; |
537 | pold_set = &uc->uc_sigmask; | 452 | pold_set = &uc->uc_sigmask; |
538 | - return handle_cpu_signal(pc, (unsigned long)info->si_addr, pold_set); | 453 | + return handle_cpu_signal(pc, (unsigned long)info->si_addr, |
454 | + uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ? | ||
455 | + (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, | ||
456 | + pold_set); | ||
539 | #else | 457 | #else |
540 | #warning No CPU specific signal handler: cannot handle target SIGSEGV events | 458 | #warning No CPU specific signal handler: cannot handle target SIGSEGV events |
541 | return 0; | 459 | return 0; |
exec.c
1 | /* | 1 | /* |
2 | - * virtual page mapping | 2 | + * virtual page mapping and translated block handling |
3 | * | 3 | * |
4 | * Copyright (c) 2003 Fabrice Bellard | 4 | * Copyright (c) 2003 Fabrice Bellard |
5 | * | 5 | * |
@@ -24,13 +24,32 @@ | @@ -24,13 +24,32 @@ | ||
24 | #include <errno.h> | 24 | #include <errno.h> |
25 | #include <unistd.h> | 25 | #include <unistd.h> |
26 | #include <inttypes.h> | 26 | #include <inttypes.h> |
27 | +#include <sys/mman.h> | ||
27 | 28 | ||
28 | #include "cpu-i386.h" | 29 | #include "cpu-i386.h" |
29 | 30 | ||
31 | +//#define DEBUG_TB_INVALIDATE | ||
32 | +#define DEBUG_FLUSH | ||
33 | + | ||
34 | +/* make various TB consistency checks */ | ||
35 | +//#define DEBUG_TB_CHECK | ||
36 | + | ||
37 | +/* threshold to flush the translated code buffer */ | ||
38 | +#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) | ||
39 | + | ||
40 | +#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) | ||
41 | + | ||
42 | +TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; | ||
43 | +TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; | ||
44 | +int nb_tbs; | ||
45 | + | ||
46 | +uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; | ||
47 | +uint8_t *code_gen_ptr; | ||
48 | + | ||
30 | /* XXX: pack the flags in the low bits of the pointer ? */ | 49 | /* XXX: pack the flags in the low bits of the pointer ? */ |
31 | typedef struct PageDesc { | 50 | typedef struct PageDesc { |
32 | - struct TranslationBlock *first_tb; | ||
33 | unsigned long flags; | 51 | unsigned long flags; |
52 | + TranslationBlock *first_tb; | ||
34 | } PageDesc; | 53 | } PageDesc; |
35 | 54 | ||
36 | #define L2_BITS 10 | 55 | #define L2_BITS 10 |
@@ -39,6 +58,8 @@ typedef struct PageDesc { | @@ -39,6 +58,8 @@ typedef struct PageDesc { | ||
39 | #define L1_SIZE (1 << L1_BITS) | 58 | #define L1_SIZE (1 << L1_BITS) |
40 | #define L2_SIZE (1 << L2_BITS) | 59 | #define L2_SIZE (1 << L2_BITS) |
41 | 60 | ||
61 | +static void tb_invalidate_page(unsigned long address); | ||
62 | + | ||
42 | unsigned long real_host_page_size; | 63 | unsigned long real_host_page_size; |
43 | unsigned long host_page_bits; | 64 | unsigned long host_page_bits; |
44 | unsigned long host_page_size; | 65 | unsigned long host_page_size; |
@@ -104,36 +125,44 @@ void page_dump(FILE *f) | @@ -104,36 +125,44 @@ void page_dump(FILE *f) | ||
104 | } | 125 | } |
105 | } | 126 | } |
106 | 127 | ||
107 | - | ||
108 | -static inline PageDesc *page_find_alloc(unsigned long address) | 128 | +static inline PageDesc *page_find_alloc(unsigned int index) |
109 | { | 129 | { |
110 | - unsigned int index; | ||
111 | PageDesc **lp, *p; | 130 | PageDesc **lp, *p; |
112 | 131 | ||
113 | - index = address >> TARGET_PAGE_BITS; | ||
114 | lp = &l1_map[index >> L2_BITS]; | 132 | lp = &l1_map[index >> L2_BITS]; |
115 | p = *lp; | 133 | p = *lp; |
116 | if (!p) { | 134 | if (!p) { |
117 | /* allocate if not found */ | 135 | /* allocate if not found */ |
118 | p = malloc(sizeof(PageDesc) * L2_SIZE); | 136 | p = malloc(sizeof(PageDesc) * L2_SIZE); |
119 | - memset(p, 0, sizeof(sizeof(PageDesc) * L2_SIZE)); | 137 | + memset(p, 0, sizeof(PageDesc) * L2_SIZE); |
120 | *lp = p; | 138 | *lp = p; |
121 | } | 139 | } |
122 | return p + (index & (L2_SIZE - 1)); | 140 | return p + (index & (L2_SIZE - 1)); |
123 | } | 141 | } |
124 | 142 | ||
125 | -int page_get_flags(unsigned long address) | 143 | +static inline PageDesc *page_find(unsigned int index) |
126 | { | 144 | { |
127 | - unsigned int index; | ||
128 | PageDesc *p; | 145 | PageDesc *p; |
129 | 146 | ||
130 | - index = address >> TARGET_PAGE_BITS; | ||
131 | p = l1_map[index >> L2_BITS]; | 147 | p = l1_map[index >> L2_BITS]; |
132 | if (!p) | 148 | if (!p) |
133 | return 0; | 149 | return 0; |
134 | - return p[index & (L2_SIZE - 1)].flags; | 150 | + return p + (index & (L2_SIZE - 1)); |
151 | +} | ||
152 | + | ||
153 | +int page_get_flags(unsigned long address) | ||
154 | +{ | ||
155 | + PageDesc *p; | ||
156 | + | ||
157 | + p = page_find(address >> TARGET_PAGE_BITS); | ||
158 | + if (!p) | ||
159 | + return 0; | ||
160 | + return p->flags; | ||
135 | } | 161 | } |
136 | 162 | ||
163 | +/* modify the flags of a page and invalidate the code if | ||
164 | + necessary. The flag PAGE_WRITE_ORG is positionned automatically | ||
165 | + depending on PAGE_WRITE */ | ||
137 | void page_set_flags(unsigned long start, unsigned long end, int flags) | 166 | void page_set_flags(unsigned long start, unsigned long end, int flags) |
138 | { | 167 | { |
139 | PageDesc *p; | 168 | PageDesc *p; |
@@ -141,8 +170,268 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) | @@ -141,8 +170,268 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) | ||
141 | 170 | ||
142 | start = start & TARGET_PAGE_MASK; | 171 | start = start & TARGET_PAGE_MASK; |
143 | end = TARGET_PAGE_ALIGN(end); | 172 | end = TARGET_PAGE_ALIGN(end); |
173 | + if (flags & PAGE_WRITE) | ||
174 | + flags |= PAGE_WRITE_ORG; | ||
144 | for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { | 175 | for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { |
145 | - p = page_find_alloc(addr); | 176 | + p = page_find_alloc(addr >> TARGET_PAGE_BITS); |
177 | + /* if the write protection is set, then we invalidate the code | ||
178 | + inside */ | ||
179 | + if (!(p->flags & PAGE_WRITE) && | ||
180 | + (flags & PAGE_WRITE) && | ||
181 | + p->first_tb) { | ||
182 | + tb_invalidate_page(addr); | ||
183 | + } | ||
146 | p->flags = flags; | 184 | p->flags = flags; |
147 | } | 185 | } |
148 | } | 186 | } |
187 | + | ||
188 | +void cpu_x86_tblocks_init(void) | ||
189 | +{ | ||
190 | + if (!code_gen_ptr) { | ||
191 | + code_gen_ptr = code_gen_buffer; | ||
192 | + } | ||
193 | +} | ||
194 | + | ||
195 | +/* set to NULL all the 'first_tb' fields in all PageDescs */ | ||
196 | +static void page_flush_tb(void) | ||
197 | +{ | ||
198 | + int i, j; | ||
199 | + PageDesc *p; | ||
200 | + | ||
201 | + for(i = 0; i < L1_SIZE; i++) { | ||
202 | + p = l1_map[i]; | ||
203 | + if (p) { | ||
204 | + for(j = 0; j < L2_SIZE; j++) | ||
205 | + p[j].first_tb = NULL; | ||
206 | + } | ||
207 | + } | ||
208 | +} | ||
209 | + | ||
210 | +/* flush all the translation blocks */ | ||
211 | +void tb_flush(void) | ||
212 | +{ | ||
213 | + int i; | ||
214 | +#ifdef DEBUG_FLUSH | ||
215 | + printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", | ||
216 | + code_gen_ptr - code_gen_buffer, | ||
217 | + nb_tbs, | ||
218 | + (code_gen_ptr - code_gen_buffer) / nb_tbs); | ||
219 | +#endif | ||
220 | + nb_tbs = 0; | ||
221 | + for(i = 0;i < CODE_GEN_HASH_SIZE; i++) | ||
222 | + tb_hash[i] = NULL; | ||
223 | + page_flush_tb(); | ||
224 | + code_gen_ptr = code_gen_buffer; | ||
225 | + /* XXX: flush processor icache at this point */ | ||
226 | +} | ||
227 | + | ||
228 | +#ifdef DEBUG_TB_CHECK | ||
229 | + | ||
230 | +static void tb_invalidate_check(unsigned long address) | ||
231 | +{ | ||
232 | + TranslationBlock *tb; | ||
233 | + int i; | ||
234 | + address &= TARGET_PAGE_MASK; | ||
235 | + for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { | ||
236 | + for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { | ||
237 | + if (!(address + TARGET_PAGE_SIZE <= tb->pc || | ||
238 | + address >= tb->pc + tb->size)) { | ||
239 | + printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n", | ||
240 | + address, tb->pc, tb->size); | ||
241 | + } | ||
242 | + } | ||
243 | + } | ||
244 | +} | ||
245 | + | ||
246 | +/* verify that all the pages have correct rights for code */ | ||
247 | +static void tb_page_check(void) | ||
248 | +{ | ||
249 | + TranslationBlock *tb; | ||
250 | + int i, flags1, flags2; | ||
251 | + | ||
252 | + for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { | ||
253 | + for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { | ||
254 | + flags1 = page_get_flags(tb->pc); | ||
255 | + flags2 = page_get_flags(tb->pc + tb->size - 1); | ||
256 | + if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { | ||
257 | + printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", | ||
258 | + tb->pc, tb->size, flags1, flags2); | ||
259 | + } | ||
260 | + } | ||
261 | + } | ||
262 | +} | ||
263 | + | ||
264 | +#endif | ||
265 | + | ||
266 | +/* invalidate one TB */ | ||
267 | +static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb, | ||
268 | + int next_offset) | ||
269 | +{ | ||
270 | + TranslationBlock *tb1; | ||
271 | + for(;;) { | ||
272 | + tb1 = *ptb; | ||
273 | + if (tb1 == tb) { | ||
274 | + *ptb = *(TranslationBlock **)((char *)tb1 + next_offset); | ||
275 | + break; | ||
276 | + } | ||
277 | + ptb = (TranslationBlock **)((char *)tb1 + next_offset); | ||
278 | + } | ||
279 | +} | ||
280 | + | ||
281 | +static inline void tb_invalidate(TranslationBlock *tb, int parity) | ||
282 | +{ | ||
283 | + PageDesc *p; | ||
284 | + unsigned int page_index1, page_index2; | ||
285 | + unsigned int h; | ||
286 | + | ||
287 | + /* remove the TB from the hash list */ | ||
288 | + h = tb_hash_func(tb->pc); | ||
289 | + tb_remove(&tb_hash[h], tb, | ||
290 | + offsetof(TranslationBlock, hash_next)); | ||
291 | + /* remove the TB from the page list */ | ||
292 | + page_index1 = tb->pc >> TARGET_PAGE_BITS; | ||
293 | + if ((page_index1 & 1) == parity) { | ||
294 | + p = page_find(page_index1); | ||
295 | + tb_remove(&p->first_tb, tb, | ||
296 | + offsetof(TranslationBlock, page_next[page_index1 & 1])); | ||
297 | + } | ||
298 | + page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; | ||
299 | + if ((page_index2 & 1) == parity) { | ||
300 | + p = page_find(page_index2); | ||
301 | + tb_remove(&p->first_tb, tb, | ||
302 | + offsetof(TranslationBlock, page_next[page_index2 & 1])); | ||
303 | + } | ||
304 | +} | ||
305 | + | ||
306 | +/* invalidate all TBs which intersect with the target page starting at addr */ | ||
307 | +static void tb_invalidate_page(unsigned long address) | ||
308 | +{ | ||
309 | + TranslationBlock *tb_next, *tb; | ||
310 | + unsigned int page_index; | ||
311 | + int parity1, parity2; | ||
312 | + PageDesc *p; | ||
313 | +#ifdef DEBUG_TB_INVALIDATE | ||
314 | + printf("tb_invalidate_page: %lx\n", address); | ||
315 | +#endif | ||
316 | + | ||
317 | + page_index = address >> TARGET_PAGE_BITS; | ||
318 | + p = page_find(page_index); | ||
319 | + if (!p) | ||
320 | + return; | ||
321 | + tb = p->first_tb; | ||
322 | + parity1 = page_index & 1; | ||
323 | + parity2 = parity1 ^ 1; | ||
324 | + while (tb != NULL) { | ||
325 | + tb_next = tb->page_next[parity1]; | ||
326 | + tb_invalidate(tb, parity2); | ||
327 | + tb = tb_next; | ||
328 | + } | ||
329 | + p->first_tb = NULL; | ||
330 | +} | ||
331 | + | ||
332 | +/* add the tb in the target page and protect it if necessary */ | ||
333 | +static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index) | ||
334 | +{ | ||
335 | + PageDesc *p; | ||
336 | + unsigned long host_start, host_end, addr, page_addr; | ||
337 | + int prot; | ||
338 | + | ||
339 | + p = page_find_alloc(page_index); | ||
340 | + tb->page_next[page_index & 1] = p->first_tb; | ||
341 | + p->first_tb = tb; | ||
342 | + if (p->flags & PAGE_WRITE) { | ||
343 | + /* force the host page as non writable (writes will have a | ||
344 | + page fault + mprotect overhead) */ | ||
345 | + page_addr = (page_index << TARGET_PAGE_BITS); | ||
346 | + host_start = page_addr & host_page_mask; | ||
347 | + host_end = host_start + host_page_size; | ||
348 | + prot = 0; | ||
349 | + for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) | ||
350 | + prot |= page_get_flags(addr); | ||
351 | + mprotect((void *)host_start, host_page_size, | ||
352 | + (prot & PAGE_BITS) & ~PAGE_WRITE); | ||
353 | +#ifdef DEBUG_TB_INVALIDATE | ||
354 | + printf("protecting code page: 0x%08lx\n", | ||
355 | + host_start); | ||
356 | +#endif | ||
357 | + p->flags &= ~PAGE_WRITE; | ||
358 | +#ifdef DEBUG_TB_CHECK | ||
359 | + tb_page_check(); | ||
360 | +#endif | ||
361 | + } | ||
362 | +} | ||
363 | + | ||
364 | +/* Allocate a new translation block. Flush the translation buffer if | ||
365 | + too many translation blocks or too much generated code. */ | ||
366 | +TranslationBlock *tb_alloc(unsigned long pc, | ||
367 | + unsigned long size) | ||
368 | +{ | ||
369 | + TranslationBlock *tb; | ||
370 | + unsigned int page_index1, page_index2; | ||
371 | + | ||
372 | + if (nb_tbs >= CODE_GEN_MAX_BLOCKS || | ||
373 | + (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) | ||
374 | + tb_flush(); | ||
375 | + tb = &tbs[nb_tbs++]; | ||
376 | + tb->pc = pc; | ||
377 | + tb->size = size; | ||
378 | + | ||
379 | + /* add in the page list */ | ||
380 | + page_index1 = pc >> TARGET_PAGE_BITS; | ||
381 | + tb_alloc_page(tb, page_index1); | ||
382 | + page_index2 = (pc + size - 1) >> TARGET_PAGE_BITS; | ||
383 | + if (page_index2 != page_index1) { | ||
384 | + tb_alloc_page(tb, page_index2); | ||
385 | + } | ||
386 | + return tb; | ||
387 | +} | ||
388 | + | ||
389 | +/* called from signal handler: invalidate the code and unprotect the | ||
390 | + page. Return TRUE if the fault was succesfully handled. */ | ||
391 | +int page_unprotect(unsigned long address) | ||
392 | +{ | ||
393 | + unsigned int page_index, prot; | ||
394 | + PageDesc *p; | ||
395 | + unsigned long host_start, host_end, addr; | ||
396 | + | ||
397 | + page_index = address >> TARGET_PAGE_BITS; | ||
398 | + p = page_find(page_index); | ||
399 | + if (!p) | ||
400 | + return 0; | ||
401 | + if ((p->flags & (PAGE_WRITE_ORG | PAGE_WRITE)) == PAGE_WRITE_ORG) { | ||
402 | + /* if the page was really writable, then we change its | ||
403 | + protection back to writable */ | ||
404 | + host_start = address & host_page_mask; | ||
405 | + host_end = host_start + host_page_size; | ||
406 | + prot = 0; | ||
407 | + for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) | ||
408 | + prot |= page_get_flags(addr); | ||
409 | + mprotect((void *)host_start, host_page_size, | ||
410 | + (prot & PAGE_BITS) | PAGE_WRITE); | ||
411 | + p->flags |= PAGE_WRITE; | ||
412 | + | ||
413 | + /* and since the content will be modified, we must invalidate | ||
414 | + the corresponding translated code. */ | ||
415 | + tb_invalidate_page(address); | ||
416 | +#ifdef DEBUG_TB_CHECK | ||
417 | + tb_invalidate_check(address); | ||
418 | +#endif | ||
419 | + return 1; | ||
420 | + } else { | ||
421 | + return 0; | ||
422 | + } | ||
423 | +} | ||
424 | + | ||
425 | +/* call this function when system calls directly modify a memory area */ | ||
426 | +void page_unprotect_range(uint8_t *data, unsigned long data_size) | ||
427 | +{ | ||
428 | + unsigned long start, end, addr; | ||
429 | + | ||
430 | + start = (unsigned long)data; | ||
431 | + end = start + data_size; | ||
432 | + start &= TARGET_PAGE_MASK; | ||
433 | + end = TARGET_PAGE_ALIGN(end); | ||
434 | + for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { | ||
435 | + page_unprotect(addr); | ||
436 | + } | ||
437 | +} |