From ff44f1a3737bfda3cac294b8e92d8ec5ddf3abf5 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 12 Mar 2010 15:20:49 +0100 Subject: [PATCH 1/8] KVM: x86: Add debug register saving and restoring Make use of the new KVM_GET/SET_DEBUGREGS to save/restore the x86 debug registers. Signed-off-by: Jan Kiszka Signed-off-by: Marcelo Tosatti --- kvm-all.c | 11 ++++++++++ kvm.h | 1 + target-i386/kvm.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/kvm-all.c b/kvm-all.c index 2ede4b9ea..d05011547 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -64,6 +64,7 @@ struct KVMState int migration_log; int vcpu_events; int robust_singlestep; + int debugregs; #ifdef KVM_CAP_SET_GUEST_DEBUG struct kvm_sw_breakpoint_head kvm_sw_breakpoints; #endif @@ -664,6 +665,11 @@ int kvm_init(int smp_cpus) kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP); #endif + s->debugregs = 0; +#ifdef KVM_CAP_DEBUGREGS + s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); +#endif + ret = kvm_arch_init(s, smp_cpus); if (ret < 0) goto err; @@ -939,6 +945,11 @@ int kvm_has_robust_singlestep(void) return kvm_state->robust_singlestep; } +int kvm_has_debugregs(void) +{ + return kvm_state->debugregs; +} + void kvm_setup_guest_memory(void *start, size_t size) { if (!kvm_has_sync_mmu()) { diff --git a/kvm.h b/kvm.h index ae87d85f2..70bfbf8ab 100644 --- a/kvm.h +++ b/kvm.h @@ -40,6 +40,7 @@ int kvm_init(int smp_cpus); int kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); int kvm_has_robust_singlestep(void); +int kvm_has_debugregs(void); #ifdef NEED_CPU_H int kvm_init_vcpu(CPUState *env); diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 5513472e8..bb6dafab3 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -874,6 +874,53 @@ static int kvm_guest_debug_workarounds(CPUState *env) return ret; } +static int kvm_put_debugregs(CPUState *env) +{ +#ifdef KVM_CAP_DEBUGREGS + struct kvm_debugregs dbgregs; + int i; + + if (!kvm_has_debugregs()) { + return 0; + } + + for (i = 0; i < 4; i++) { + dbgregs.db[i] = env->dr[i]; + } + dbgregs.dr6 = env->dr[6]; + dbgregs.dr7 = env->dr[7]; + dbgregs.flags = 0; + + return kvm_vcpu_ioctl(env, KVM_SET_DEBUGREGS, &dbgregs); +#else + return 0; +#endif +} + +static int kvm_get_debugregs(CPUState *env) +{ +#ifdef KVM_CAP_DEBUGREGS + struct kvm_debugregs dbgregs; + int i, ret; + + if (!kvm_has_debugregs()) { + return 0; + } + + ret = kvm_vcpu_ioctl(env, KVM_GET_DEBUGREGS, &dbgregs); + if (ret < 0) { + return ret; + } + for (i = 0; i < 4; i++) { + env->dr[i] = dbgregs.db[i]; + } + env->dr[4] = env->dr[6] = dbgregs.dr6; + env->dr[5] = env->dr[7] = dbgregs.dr7; +#endif + + return 0; +} + int kvm_arch_put_registers(CPUState *env, int level) { int ret; @@ -909,6 +956,10 @@ int kvm_arch_put_registers(CPUState *env, int level) if (ret < 0) return ret; + ret = kvm_put_debugregs(env); + if (ret < 0) + return ret; + return 0; } @@ -940,6 +991,10 @@ int kvm_arch_get_registers(CPUState *env) if (ret < 0) return ret; + ret = kvm_get_debugregs(env); + if (ret < 0) + return ret; + return 0; } From b5e5a934174ade5dcfc98a41f21f8a6c4816dd54 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Tue, 23 Mar 2010 13:37:10 -0300 Subject: [PATCH 2/8] target-i386: print EFER in cpu_dump_state Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- target-i386/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target-i386/helper.c b/target-i386/helper.c index 383583510..c9508a816 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -356,6 +356,7 @@ void cpu_dump_state(CPUState *env, FILE *f, cc_op_name); } } + cpu_fprintf(f, "EFER=%016" PRIx64 "\n", env->efer); if (flags & X86_DUMP_FPU) { int fptag; fptag = 0; From 7c80eef899ff0feb1ab6730d4dd3bc3aee046093 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Tue, 23 Mar 2010 13:37:11 -0300 Subject: [PATCH 3/8] kvm: handle internal error Port qemu-kvm's KVM_EXIT_INTERNAL_ERROR handling to upstream. Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- kvm-all.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/kvm-all.c b/kvm-all.c index d05011547..9c8aa7d2b 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -730,6 +730,32 @@ static int kvm_handle_io(uint16_t port, void *data, int direction, int size, return 1; } +#ifdef KVM_CAP_INTERNAL_ERROR_DATA +static void kvm_handle_internal_error(CPUState *env, struct kvm_run *run) +{ + + if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { + int i; + + fprintf(stderr, "KVM internal error. Suberror: %d\n", + run->internal.suberror); + + for (i = 0; i < run->internal.ndata; ++i) { + fprintf(stderr, "extra data[%d]: %"PRIx64"\n", + i, (uint64_t)run->internal.data[i]); + } + } + cpu_dump_state(env, stderr, fprintf, 0); + if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { + fprintf(stderr, "emulation failure\n"); + } + /* FIXME: Should trigger a qmp message to let management know + * something went wrong. + */ + vm_stop(0); +} +#endif + void kvm_flush_coalesced_mmio_buffer(void) { #ifdef KVM_CAP_COALESCED_MMIO @@ -845,6 +871,11 @@ int kvm_cpu_exec(CPUState *env) case KVM_EXIT_EXCEPTION: DPRINTF("kvm_exit_exception\n"); break; +#ifdef KVM_CAP_INTERNAL_ERROR_DATA + case KVM_EXIT_INTERNAL_ERROR: + kvm_handle_internal_error(env, run); + break; +#endif case KVM_EXIT_DEBUG: DPRINTF("kvm_exit_debug\n"); #ifdef KVM_CAP_SET_GUEST_DEBUG From 204204308b011d5fe494970255b318ec444d1d45 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 23 Mar 2010 13:37:12 -0300 Subject: [PATCH 4/8] kvm: allow qemu to set EPT identity mapping address If we use larger BIOS image than current 256KB, we would need move reserved TSS and EPT identity mapping pages. Currently TSS support this, but not EPT. Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- target-i386/kvm.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/target-i386/kvm.c b/target-i386/kvm.c index bb6dafab3..f73b47b97 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -326,6 +326,25 @@ static int kvm_has_msr_star(CPUState *env) return 0; } +static int kvm_init_identity_map_page(KVMState *s) +{ +#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR + int ret; + uint64_t addr = 0xfffbc000; + + if (!kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) { + return 0; + } + + ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &addr); + if (ret < 0) { + fprintf(stderr, "kvm_set_identity_map_addr: %s\n", strerror(ret)); + return ret; + } +#endif + return 0; +} + int kvm_arch_init(KVMState *s, int smp_cpus) { int ret; @@ -353,7 +372,12 @@ int kvm_arch_init(KVMState *s, int smp_cpus) perror("e820_add_entry() table is full"); exit(1); } - return kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, 0xfffbd000); + ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, 0xfffbd000); + if (ret < 0) { + return ret; + } + + return kvm_init_identity_map_page(s); } static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs) From 6164e6d6e86e8f3f0b5ff7ae08b677026cb291c3 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Tue, 23 Mar 2010 13:37:13 -0300 Subject: [PATCH 5/8] kvm_init_vcpu requires global lock held Since it accesses data protected by the lock. Signed-off-by: Avi Kivity --- cpus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpus.c b/cpus.c index 8450ee4ff..2bf87d2d5 100644 --- a/cpus.c +++ b/cpus.c @@ -401,6 +401,7 @@ static void *kvm_cpu_thread_fn(void *arg) { CPUState *env = arg; + qemu_mutex_lock(&qemu_global_mutex); qemu_thread_self(env->thread); if (kvm_enabled()) kvm_init_vcpu(env); @@ -408,7 +409,6 @@ static void *kvm_cpu_thread_fn(void *arg) kvm_block_io_signals(env); /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); env->created = 1; qemu_cond_signal(&qemu_cpu_cond); From cce83b7d625d71b8708c869403d1b8781af6f9b4 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Fri, 23 Apr 2010 14:04:11 -0300 Subject: [PATCH 6/8] vga: fix typo in length passed to kvm_log_stop Signed-off-by: Marcelo Tosatti --- hw/vga.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vga.c b/hw/vga.c index 845dbcc3b..db7211544 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -1618,8 +1618,8 @@ void vga_dirty_log_stop(VGACommonState *s) kvm_log_stop(s->map_addr, s->map_end - s->map_addr); if (kvm_enabled() && s->lfb_vram_mapped) { - kvm_log_stop(isa_mem_base + 0xa0000, 0x80000); - kvm_log_stop(isa_mem_base + 0xa8000, 0x80000); + kvm_log_stop(isa_mem_base + 0xa0000, 0x8000); + kvm_log_stop(isa_mem_base + 0xa8000, 0x8000); } #ifdef CONFIG_BOCHS_VBE From 213acd2ec24f86cdbfe3391c8776987fd6d5e373 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Fri, 23 Apr 2010 14:04:13 -0300 Subject: [PATCH 7/8] introduce leul_to_cpu To be used by next patch. Signed-off-by: Marcelo Tosatti --- bswap.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bswap.h b/bswap.h index aace9b76e..956f3fa0b 100644 --- a/bswap.h +++ b/bswap.h @@ -205,8 +205,10 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) #ifdef HOST_WORDS_BIGENDIAN #define cpu_to_32wu cpu_to_be32wu +#define leul_to_cpu(v) le ## HOST_LONG_BITS ## _to_cpu(v) #else #define cpu_to_32wu cpu_to_le32wu +#define leul_to_cpu(v) (v) #endif #undef le_bswap From 8369e01ce418edb26a6e1f65406cac650563b3da Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Fri, 23 Apr 2010 14:04:14 -0300 Subject: [PATCH 8/8] kvm: port qemu-kvm's bitmap scanning Which is significantly faster. Signed-off-by: Marcelo Tosatti --- kvm-all.c | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index 9c8aa7d2b..6962b2bbe 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -26,6 +26,7 @@ #include "hw/hw.h" #include "gdbstub.h" #include "kvm.h" +#include "bswap.h" /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ #define PAGE_SIZE TARGET_PAGE_SIZE @@ -283,11 +284,41 @@ static int kvm_set_migration_log(int enable) return 0; } -static int test_le_bit(unsigned long nr, unsigned char *addr) +/* get kvm's dirty pages bitmap and update qemu's */ +static int kvm_get_dirty_pages_log_range(unsigned long start_addr, + unsigned long *bitmap, + unsigned long offset, + unsigned long mem_size) { - return (addr[nr >> 3] >> (nr & 7)) & 1; + unsigned int i, j; + unsigned long page_number, addr, addr1, c; + ram_addr_t ram_addr; + unsigned int len = ((mem_size / TARGET_PAGE_SIZE) + HOST_LONG_BITS - 1) / + HOST_LONG_BITS; + + /* + * bitmap-traveling is faster than memory-traveling (for addr...) + * especially when most of the memory is not dirty. + */ + for (i = 0; i < len; i++) { + if (bitmap[i] != 0) { + c = leul_to_cpu(bitmap[i]); + do { + j = ffsl(c) - 1; + c &= ~(1ul << j); + page_number = i * HOST_LONG_BITS + j; + addr1 = page_number * TARGET_PAGE_SIZE; + addr = offset + addr1; + ram_addr = cpu_get_physical_page_desc(addr); + cpu_physical_memory_set_dirty(ram_addr); + } while (c != 0); + } + } + return 0; } +#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) + /** * kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space * This function updates qemu's dirty bitmap using cpu_physical_memory_set_dirty(). @@ -301,8 +332,6 @@ static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, { KVMState *s = kvm_state; unsigned long size, allocated_size = 0; - target_phys_addr_t phys_addr; - ram_addr_t addr; KVMDirtyLog d; KVMSlot *mem; int ret = 0; @@ -314,7 +343,7 @@ static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, break; } - size = ((mem->memory_size >> TARGET_PAGE_BITS) + 7) / 8; + size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), HOST_LONG_BITS) / 8; if (!d.dirty_bitmap) { d.dirty_bitmap = qemu_malloc(size); } else if (size > allocated_size) { @@ -331,17 +360,9 @@ static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, break; } - for (phys_addr = mem->start_addr, addr = mem->phys_offset; - phys_addr < mem->start_addr + mem->memory_size; - phys_addr += TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { - unsigned char *bitmap = (unsigned char *)d.dirty_bitmap; - unsigned nr = (phys_addr - mem->start_addr) >> TARGET_PAGE_BITS; - - if (test_le_bit(nr, bitmap)) { - cpu_physical_memory_set_dirty(addr); - } - } - start_addr = phys_addr; + kvm_get_dirty_pages_log_range(mem->start_addr, d.dirty_bitmap, + mem->start_addr, mem->memory_size); + start_addr = mem->start_addr + mem->memory_size; } qemu_free(d.dirty_bitmap);