Commit aa0629734011106231e02fca3c19e9e0e24c02be
1 parent
3a7d929e
kqemu fixes - new API support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1545 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
182 additions
and
0 deletions
kqemu.c
| ... | ... | @@ -40,6 +40,7 @@ |
| 40 | 40 | #ifdef USE_KQEMU |
| 41 | 41 | |
| 42 | 42 | #define DEBUG |
| 43 | +//#define PROFILE | |
| 43 | 44 | |
| 44 | 45 | #include <unistd.h> |
| 45 | 46 | #include <fcntl.h> |
| ... | ... | @@ -49,6 +50,10 @@ |
| 49 | 50 | #ifndef KQEMU_RET_SYSCALL |
| 50 | 51 | #define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */ |
| 51 | 52 | #endif |
| 53 | +#ifndef KQEMU_MAX_RAM_PAGES_TO_UPDATE | |
| 54 | +#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512 | |
| 55 | +#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1) | |
| 56 | +#endif | |
| 52 | 57 | |
| 53 | 58 | #ifdef _WIN32 |
| 54 | 59 | #define KQEMU_DEVICE "\\\\.\\kqemu" |
| ... | ... | @@ -69,6 +74,8 @@ int kqemu_fd = KQEMU_INVALID_FD; |
| 69 | 74 | int kqemu_allowed = 1; |
| 70 | 75 | unsigned long *pages_to_flush; |
| 71 | 76 | unsigned int nb_pages_to_flush; |
| 77 | +unsigned long *ram_pages_to_update; | |
| 78 | +unsigned int nb_ram_pages_to_update; | |
| 72 | 79 | extern uint32_t **l1_phys_map; |
| 73 | 80 | |
| 74 | 81 | #define cpuid(index, eax, ebx, ecx, edx) \ |
| ... | ... | @@ -167,11 +174,19 @@ int kqemu_init(CPUState *env) |
| 167 | 174 | if (!pages_to_flush) |
| 168 | 175 | goto fail; |
| 169 | 176 | |
| 177 | + ram_pages_to_update = qemu_vmalloc(KQEMU_MAX_RAM_PAGES_TO_UPDATE * | |
| 178 | + sizeof(unsigned long)); | |
| 179 | + if (!ram_pages_to_update) | |
| 180 | + goto fail; | |
| 181 | + | |
| 170 | 182 | init.ram_base = phys_ram_base; |
| 171 | 183 | init.ram_size = phys_ram_size; |
| 172 | 184 | init.ram_dirty = phys_ram_dirty; |
| 173 | 185 | init.phys_to_ram_map = l1_phys_map; |
| 174 | 186 | init.pages_to_flush = pages_to_flush; |
| 187 | +#if KQEMU_VERSION >= 0x010200 | |
| 188 | + init.ram_pages_to_update = ram_pages_to_update; | |
| 189 | +#endif | |
| 175 | 190 | #ifdef _WIN32 |
| 176 | 191 | ret = DeviceIoControl(kqemu_fd, KQEMU_INIT, &init, sizeof(init), |
| 177 | 192 | NULL, 0, &temp, NULL) == TRUE ? 0 : -1; |
| ... | ... | @@ -188,6 +203,7 @@ int kqemu_init(CPUState *env) |
| 188 | 203 | kqemu_update_cpuid(env); |
| 189 | 204 | env->kqemu_enabled = 1; |
| 190 | 205 | nb_pages_to_flush = 0; |
| 206 | + nb_ram_pages_to_update = 0; | |
| 191 | 207 | return 0; |
| 192 | 208 | } |
| 193 | 209 | |
| ... | ... | @@ -214,6 +230,19 @@ void kqemu_flush(CPUState *env, int global) |
| 214 | 230 | nb_pages_to_flush = KQEMU_FLUSH_ALL; |
| 215 | 231 | } |
| 216 | 232 | |
| 233 | +void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr) | |
| 234 | +{ | |
| 235 | +#ifdef DEBUG | |
| 236 | + if (loglevel & CPU_LOG_INT) { | |
| 237 | + fprintf(logfile, "kqemu_set_notdirty: addr=%08lx\n", ram_addr); | |
| 238 | + } | |
| 239 | +#endif | |
| 240 | + if (nb_ram_pages_to_update >= KQEMU_MAX_RAM_PAGES_TO_UPDATE) | |
| 241 | + nb_ram_pages_to_update = KQEMU_RAM_PAGES_UPDATE_ALL; | |
| 242 | + else | |
| 243 | + ram_pages_to_update[nb_ram_pages_to_update++] = ram_addr; | |
| 244 | +} | |
| 245 | + | |
| 217 | 246 | struct fpstate { |
| 218 | 247 | uint16_t fpuc; |
| 219 | 248 | uint16_t dummy1; |
| ... | ... | @@ -404,6 +433,103 @@ static int do_syscall(CPUState *env, |
| 404 | 433 | return 2; |
| 405 | 434 | } |
| 406 | 435 | |
| 436 | +#ifdef PROFILE | |
| 437 | + | |
| 438 | +#define PC_REC_SIZE 1 | |
| 439 | +#define PC_REC_HASH_BITS 16 | |
| 440 | +#define PC_REC_HASH_SIZE (1 << PC_REC_HASH_BITS) | |
| 441 | + | |
| 442 | +typedef struct PCRecord { | |
| 443 | + unsigned long pc; | |
| 444 | + int64_t count; | |
| 445 | + struct PCRecord *next; | |
| 446 | +} PCRecord; | |
| 447 | + | |
| 448 | +PCRecord *pc_rec_hash[PC_REC_HASH_SIZE]; | |
| 449 | +int nb_pc_records; | |
| 450 | + | |
| 451 | +void kqemu_record_pc(unsigned long pc) | |
| 452 | +{ | |
| 453 | + unsigned long h; | |
| 454 | + PCRecord **pr, *r; | |
| 455 | + | |
| 456 | + h = pc / PC_REC_SIZE; | |
| 457 | + h = h ^ (h >> PC_REC_HASH_BITS); | |
| 458 | + h &= (PC_REC_HASH_SIZE - 1); | |
| 459 | + pr = &pc_rec_hash[h]; | |
| 460 | + for(;;) { | |
| 461 | + r = *pr; | |
| 462 | + if (r == NULL) | |
| 463 | + break; | |
| 464 | + if (r->pc == pc) { | |
| 465 | + r->count++; | |
| 466 | + return; | |
| 467 | + } | |
| 468 | + pr = &r->next; | |
| 469 | + } | |
| 470 | + r = malloc(sizeof(PCRecord)); | |
| 471 | + r->count = 1; | |
| 472 | + r->pc = pc; | |
| 473 | + r->next = NULL; | |
| 474 | + *pr = r; | |
| 475 | + nb_pc_records++; | |
| 476 | +} | |
| 477 | + | |
| 478 | +int pc_rec_cmp(const void *p1, const void *p2) | |
| 479 | +{ | |
| 480 | + PCRecord *r1 = *(PCRecord **)p1; | |
| 481 | + PCRecord *r2 = *(PCRecord **)p2; | |
| 482 | + if (r1->count < r2->count) | |
| 483 | + return 1; | |
| 484 | + else if (r1->count == r2->count) | |
| 485 | + return 0; | |
| 486 | + else | |
| 487 | + return -1; | |
| 488 | +} | |
| 489 | + | |
| 490 | +void kqemu_record_dump(void) | |
| 491 | +{ | |
| 492 | + PCRecord **pr, *r; | |
| 493 | + int i, h; | |
| 494 | + FILE *f; | |
| 495 | + int64_t total, sum; | |
| 496 | + | |
| 497 | + pr = malloc(sizeof(PCRecord *) * nb_pc_records); | |
| 498 | + i = 0; | |
| 499 | + total = 0; | |
| 500 | + for(h = 0; h < PC_REC_HASH_SIZE; h++) { | |
| 501 | + for(r = pc_rec_hash[h]; r != NULL; r = r->next) { | |
| 502 | + pr[i++] = r; | |
| 503 | + total += r->count; | |
| 504 | + } | |
| 505 | + } | |
| 506 | + qsort(pr, nb_pc_records, sizeof(PCRecord *), pc_rec_cmp); | |
| 507 | + | |
| 508 | + f = fopen("/tmp/kqemu.stats", "w"); | |
| 509 | + if (!f) { | |
| 510 | + perror("/tmp/kqemu.stats"); | |
| 511 | + exit(1); | |
| 512 | + } | |
| 513 | + fprintf(f, "total: %lld\n", total); | |
| 514 | + sum = 0; | |
| 515 | + for(i = 0; i < nb_pc_records; i++) { | |
| 516 | + r = pr[i]; | |
| 517 | + sum += r->count; | |
| 518 | + fprintf(f, "%08lx: %lld %0.2f%% %0.2f%%\n", | |
| 519 | + r->pc, | |
| 520 | + r->count, | |
| 521 | + (double)r->count / (double)total * 100.0, | |
| 522 | + (double)sum / (double)total * 100.0); | |
| 523 | + } | |
| 524 | + fclose(f); | |
| 525 | + free(pr); | |
| 526 | +} | |
| 527 | +#else | |
| 528 | +void kqemu_record_dump(void) | |
| 529 | +{ | |
| 530 | +} | |
| 531 | +#endif | |
| 532 | + | |
| 407 | 533 | int kqemu_cpu_exec(CPUState *env) |
| 408 | 534 | { |
| 409 | 535 | struct kqemu_cpu_state kcpu_state, *kenv = &kcpu_state; |
| ... | ... | @@ -447,6 +573,11 @@ int kqemu_cpu_exec(CPUState *env) |
| 447 | 573 | kenv->cpl = 3; |
| 448 | 574 | kenv->nb_pages_to_flush = nb_pages_to_flush; |
| 449 | 575 | nb_pages_to_flush = 0; |
| 576 | +#if KQEMU_VERSION >= 0x010200 | |
| 577 | + kenv->user_only = 1; | |
| 578 | + kenv->nb_ram_pages_to_update = nb_ram_pages_to_update; | |
| 579 | +#endif | |
| 580 | + nb_ram_pages_to_update = 0; | |
| 450 | 581 | |
| 451 | 582 | if (!(kenv->cr0 & CR0_TS_MASK)) { |
| 452 | 583 | if (env->cpuid_features & CPUID_FXSR) |
| ... | ... | @@ -494,6 +625,49 @@ int kqemu_cpu_exec(CPUState *env) |
| 494 | 625 | env->cr[2] = kenv->cr2; |
| 495 | 626 | env->dr[6] = kenv->dr6; |
| 496 | 627 | |
| 628 | +#if KQEMU_VERSION >= 0x010200 | |
| 629 | + if (kenv->nb_ram_pages_to_update > 0) { | |
| 630 | + cpu_tlb_update_dirty(env); | |
| 631 | + } | |
| 632 | +#endif | |
| 633 | + | |
| 634 | + /* restore the hidden flags */ | |
| 635 | + { | |
| 636 | + unsigned int new_hflags; | |
| 637 | +#ifdef TARGET_X86_64 | |
| 638 | + if ((env->hflags & HF_LMA_MASK) && | |
| 639 | + (env->segs[R_CS].flags & DESC_L_MASK)) { | |
| 640 | + /* long mode */ | |
| 641 | + new_hflags = HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; | |
| 642 | + } else | |
| 643 | +#endif | |
| 644 | + { | |
| 645 | + /* legacy / compatibility case */ | |
| 646 | + new_hflags = (env->segs[R_CS].flags & DESC_B_MASK) | |
| 647 | + >> (DESC_B_SHIFT - HF_CS32_SHIFT); | |
| 648 | + new_hflags |= (env->segs[R_SS].flags & DESC_B_MASK) | |
| 649 | + >> (DESC_B_SHIFT - HF_SS32_SHIFT); | |
| 650 | + if (!(env->cr[0] & CR0_PE_MASK) || | |
| 651 | + (env->eflags & VM_MASK) || | |
| 652 | + !(env->hflags & HF_CS32_MASK)) { | |
| 653 | + /* XXX: try to avoid this test. The problem comes from the | |
| 654 | + fact that is real mode or vm86 mode we only modify the | |
| 655 | + 'base' and 'selector' fields of the segment cache to go | |
| 656 | + faster. A solution may be to force addseg to one in | |
| 657 | + translate-i386.c. */ | |
| 658 | + new_hflags |= HF_ADDSEG_MASK; | |
| 659 | + } else { | |
| 660 | + new_hflags |= ((env->segs[R_DS].base | | |
| 661 | + env->segs[R_ES].base | | |
| 662 | + env->segs[R_SS].base) != 0) << | |
| 663 | + HF_ADDSEG_SHIFT; | |
| 664 | + } | |
| 665 | + } | |
| 666 | + env->hflags = (env->hflags & | |
| 667 | + ~(HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)) | | |
| 668 | + new_hflags; | |
| 669 | + } | |
| 670 | + | |
| 497 | 671 | #ifdef DEBUG |
| 498 | 672 | if (loglevel & CPU_LOG_INT) { |
| 499 | 673 | fprintf(logfile, "kqemu: kqemu_cpu_exec: ret=0x%x\n", ret); |
| ... | ... | @@ -537,6 +711,14 @@ int kqemu_cpu_exec(CPUState *env) |
| 537 | 711 | #endif |
| 538 | 712 | return 0; |
| 539 | 713 | } else if (ret == KQEMU_RET_SOFTMMU) { |
| 714 | +#ifdef PROFILE | |
| 715 | + kqemu_record_pc(env->eip + env->segs[R_CS].base); | |
| 716 | +#endif | |
| 717 | +#ifdef DEBUG | |
| 718 | + if (loglevel & CPU_LOG_INT) { | |
| 719 | + cpu_dump_state(env, logfile, fprintf, 0); | |
| 720 | + } | |
| 721 | +#endif | |
| 540 | 722 | return 2; |
| 541 | 723 | } else { |
| 542 | 724 | cpu_dump_state(env, stderr, fprintf, 0); | ... | ... |