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); | ... | ... |