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 | +} |