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 | 445 | #define HOST_PAGE_ALIGN(addr) (((addr) + host_page_size - 1) & host_page_mask) |
446 | 446 | |
447 | 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 | 457 | void page_dump(FILE *f); |
455 | 458 | int page_get_flags(unsigned long address); |
456 | 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 | 463 | /* internal functions */ |
459 | 464 | |
460 | 465 | #define GEN_FLAG_CODE32_SHIFT 0 |
... | ... | @@ -468,8 +473,73 @@ void page_set_flags(unsigned long start, unsigned long end, int flags); |
468 | 473 | |
469 | 474 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
470 | 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 | 478 | void cpu_x86_tblocks_init(void); |
473 | 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 | 545 | #endif /* CPU_I386_H */ | ... | ... |
exec-i386.c
... | ... | @@ -21,39 +21,10 @@ |
21 | 21 | #include "disas.h" |
22 | 22 | |
23 | 23 | //#define DEBUG_EXEC |
24 | -#define DEBUG_FLUSH | |
25 | 24 | //#define DEBUG_SIGNAL |
26 | 25 | |
27 | 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 | 28 | /* thread support */ |
58 | 29 | |
59 | 30 | #ifdef __powerpc__ |
... | ... | @@ -195,70 +166,6 @@ void raise_exception(int exception_index) |
195 | 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 | 169 | int cpu_x86_exec(CPUX86State *env1) |
263 | 170 | { |
264 | 171 | int saved_T0, saved_T1, saved_A0; |
... | ... | @@ -287,7 +194,7 @@ int cpu_x86_exec(CPUX86State *env1) |
287 | 194 | #ifdef reg_EDI |
288 | 195 | int saved_EDI; |
289 | 196 | #endif |
290 | - int code_gen_size, ret; | |
197 | + int code_gen_size, ret, code_size; | |
291 | 198 | void (*gen_func)(void); |
292 | 199 | TranslationBlock *tb, **ptb; |
293 | 200 | uint8_t *tc_ptr, *cs_base, *pc; |
... | ... | @@ -390,15 +297,15 @@ int cpu_x86_exec(CPUX86State *env1) |
390 | 297 | cpu_lock(); |
391 | 298 | tc_ptr = code_gen_ptr; |
392 | 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 | 302 | /* if invalid instruction, signal it */ |
395 | 303 | if (ret != 0) { |
396 | 304 | cpu_unlock(); |
397 | 305 | raise_exception(EXCP06_ILLOP); |
398 | 306 | } |
399 | - tb = tb_alloc(); | |
307 | + tb = tb_alloc((unsigned long)pc, code_size); | |
400 | 308 | *ptb = tb; |
401 | - tb->pc = (unsigned long)pc; | |
402 | 309 | tb->cs_base = (unsigned long)cs_base; |
403 | 310 | tb->flags = flags; |
404 | 311 | tb->tc_ptr = tc_ptr; |
... | ... | @@ -493,15 +400,22 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) |
493 | 400 | #include <sys/ucontext.h> |
494 | 401 | |
495 | 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 | 406 | static inline int handle_cpu_signal(unsigned long pc, |
498 | 407 | unsigned long address, |
408 | + int is_write, | |
499 | 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 | 414 | #endif |
415 | + if (is_write && page_unprotect(address)) { | |
416 | + sigprocmask(SIG_SETMASK, old_set, NULL); | |
417 | + return 1; | |
418 | + } | |
505 | 419 | if (pc >= (unsigned long)code_gen_buffer && |
506 | 420 | pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) { |
507 | 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 | 426 | /* XXX: need to compute virtual pc position by retranslating |
513 | 427 | code. The rest of the CPU state should be correct. */ |
514 | 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 | 430 | /* never comes here */ |
518 | 431 | return 1; |
519 | 432 | } else { |
... | ... | @@ -531,11 +444,16 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info, |
531 | 444 | |
532 | 445 | #ifndef REG_EIP |
533 | 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 | 450 | #endif |
536 | 451 | pc = uc->uc_mcontext.gregs[REG_EIP]; |
537 | 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 | 457 | #else |
540 | 458 | #warning No CPU specific signal handler: cannot handle target SIGSEGV events |
541 | 459 | return 0; | ... | ... |
exec.c
1 | 1 | /* |
2 | - * virtual page mapping | |
2 | + * virtual page mapping and translated block handling | |
3 | 3 | * |
4 | 4 | * Copyright (c) 2003 Fabrice Bellard |
5 | 5 | * |
... | ... | @@ -24,13 +24,32 @@ |
24 | 24 | #include <errno.h> |
25 | 25 | #include <unistd.h> |
26 | 26 | #include <inttypes.h> |
27 | +#include <sys/mman.h> | |
27 | 28 | |
28 | 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 | 49 | /* XXX: pack the flags in the low bits of the pointer ? */ |
31 | 50 | typedef struct PageDesc { |
32 | - struct TranslationBlock *first_tb; | |
33 | 51 | unsigned long flags; |
52 | + TranslationBlock *first_tb; | |
34 | 53 | } PageDesc; |
35 | 54 | |
36 | 55 | #define L2_BITS 10 |
... | ... | @@ -39,6 +58,8 @@ typedef struct PageDesc { |
39 | 58 | #define L1_SIZE (1 << L1_BITS) |
40 | 59 | #define L2_SIZE (1 << L2_BITS) |
41 | 60 | |
61 | +static void tb_invalidate_page(unsigned long address); | |
62 | + | |
42 | 63 | unsigned long real_host_page_size; |
43 | 64 | unsigned long host_page_bits; |
44 | 65 | unsigned long host_page_size; |
... | ... | @@ -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 | 130 | PageDesc **lp, *p; |
112 | 131 | |
113 | - index = address >> TARGET_PAGE_BITS; | |
114 | 132 | lp = &l1_map[index >> L2_BITS]; |
115 | 133 | p = *lp; |
116 | 134 | if (!p) { |
117 | 135 | /* allocate if not found */ |
118 | 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 | 138 | *lp = p; |
121 | 139 | } |
122 | 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 | 145 | PageDesc *p; |
129 | 146 | |
130 | - index = address >> TARGET_PAGE_BITS; | |
131 | 147 | p = l1_map[index >> L2_BITS]; |
132 | 148 | if (!p) |
133 | 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 | 166 | void page_set_flags(unsigned long start, unsigned long end, int flags) |
138 | 167 | { |
139 | 168 | PageDesc *p; |
... | ... | @@ -141,8 +170,268 @@ void page_set_flags(unsigned long start, unsigned long end, int flags) |
141 | 170 | |
142 | 171 | start = start & TARGET_PAGE_MASK; |
143 | 172 | end = TARGET_PAGE_ALIGN(end); |
173 | + if (flags & PAGE_WRITE) | |
174 | + flags |= PAGE_WRITE_ORG; | |
144 | 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 | 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 | +} | ... | ... |