target-mips: optimize gen_cl()
[qemu] / kvm-all.c
index 11034df..3cfc7b5 100644 (file)
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -22,6 +22,7 @@
 
 #include "qemu-common.h"
 #include "sysemu.h"
+#include "gdbstub.h"
 #include "kvm.h"
 
 /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */
@@ -56,6 +57,9 @@ struct KVMState
     int fd;
     int vmfd;
     int coalesced_mmio;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+    struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
+#endif
 };
 
 static KVMState *kvm_state;
@@ -220,11 +224,6 @@ void kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_a
     alloc_size = mem->memory_size >> TARGET_PAGE_BITS / sizeof(d.dirty_bitmap);
     d.dirty_bitmap = qemu_mallocz(alloc_size);
 
-    if (d.dirty_bitmap == NULL) {
-        dprintf("Could not allocate dirty bitmap\n");
-        return;
-    }
-
     d.slot = mem->slot;
     dprintf("slot %d, phys_addr %llx, uaddr: %llx\n",
             d.slot, mem->start_addr, mem->phys_offset);
@@ -295,9 +294,10 @@ int kvm_init(int smp_cpus)
         return -EINVAL;
 
     s = qemu_mallocz(sizeof(KVMState));
-    if (s == NULL)
-        return -ENOMEM;
 
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+    TAILQ_INIT(&s->kvm_sw_breakpoints);
+#endif
     for (i = 0; i < ARRAY_SIZE(s->slots); i++)
         s->slots[i].slot = i;
 
@@ -452,7 +452,7 @@ int kvm_cpu_exec(CPUState *env)
     do {
         kvm_arch_pre_run(env, run);
 
-        if ((env->interrupt_request & CPU_INTERRUPT_EXIT)) {
+        if (env->exit_request) {
             dprintf("interrupt exit requested\n");
             ret = 0;
             break;
@@ -511,6 +511,16 @@ int kvm_cpu_exec(CPUState *env)
             break;
         case KVM_EXIT_DEBUG:
             dprintf("kvm_exit_debug\n");
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+            if (kvm_arch_debug(&run->debug.arch)) {
+                gdb_set_stop_cpu(env);
+                vm_stop(EXCP_DEBUG);
+                env->exception_index = EXCP_DEBUG;
+                return 0;
+            }
+            /* re-enter, this exception was guest-internal */
+            ret = 1;
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
             break;
         default:
             dprintf("kvm_arch_handle_exit\n");
@@ -519,8 +529,8 @@ int kvm_cpu_exec(CPUState *env)
         }
     } while (ret > 0);
 
-    if ((env->interrupt_request & CPU_INTERRUPT_EXIT)) {
-        env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
+    if (env->exit_request) {
+        env->exit_request = 0;
         env->exception_index = EXCP_INTERRUPT;
     }
 
@@ -654,12 +664,168 @@ int kvm_vcpu_ioctl(CPUState *env, int type, ...)
 
 int kvm_has_sync_mmu(void)
 {
+#ifdef KVM_CAP_SYNC_MMU
     KVMState *s = kvm_state;
 
-#ifdef KVM_CAP_SYNC_MMU
     if (kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU) > 0)
         return 1;
 #endif
 
     return 0;
 }
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env,
+                                                 target_ulong pc)
+{
+    struct kvm_sw_breakpoint *bp;
+
+    TAILQ_FOREACH(bp, &env->kvm_state->kvm_sw_breakpoints, entry) {
+        if (bp->pc == pc)
+            return bp;
+    }
+    return NULL;
+}
+
+int kvm_sw_breakpoints_active(CPUState *env)
+{
+    return !TAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints);
+}
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+    struct kvm_guest_debug dbg;
+
+    dbg.control = 0;
+    if (env->singlestep_enabled)
+        dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
+
+    kvm_arch_update_guest_debug(env, &dbg);
+    dbg.control |= reinject_trap;
+
+    return kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg);
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+                          target_ulong len, int type)
+{
+    struct kvm_sw_breakpoint *bp;
+    CPUState *env;
+    int err;
+
+    if (type == GDB_BREAKPOINT_SW) {
+        bp = kvm_find_sw_breakpoint(current_env, addr);
+        if (bp) {
+            bp->use_count++;
+            return 0;
+        }
+
+        bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint));
+        if (!bp)
+            return -ENOMEM;
+
+        bp->pc = addr;
+        bp->use_count = 1;
+        err = kvm_arch_insert_sw_breakpoint(current_env, bp);
+        if (err) {
+            free(bp);
+            return err;
+        }
+
+        TAILQ_INSERT_HEAD(&current_env->kvm_state->kvm_sw_breakpoints,
+                          bp, entry);
+    } else {
+        err = kvm_arch_insert_hw_breakpoint(addr, len, type);
+        if (err)
+            return err;
+    }
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        err = kvm_update_guest_debug(env, 0);
+        if (err)
+            return err;
+    }
+    return 0;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+                          target_ulong len, int type)
+{
+    struct kvm_sw_breakpoint *bp;
+    CPUState *env;
+    int err;
+
+    if (type == GDB_BREAKPOINT_SW) {
+        bp = kvm_find_sw_breakpoint(current_env, addr);
+        if (!bp)
+            return -ENOENT;
+
+        if (bp->use_count > 1) {
+            bp->use_count--;
+            return 0;
+        }
+
+        err = kvm_arch_remove_sw_breakpoint(current_env, bp);
+        if (err)
+            return err;
+
+        TAILQ_REMOVE(&current_env->kvm_state->kvm_sw_breakpoints, bp, entry);
+        qemu_free(bp);
+    } else {
+        err = kvm_arch_remove_hw_breakpoint(addr, len, type);
+        if (err)
+            return err;
+    }
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        err = kvm_update_guest_debug(env, 0);
+        if (err)
+            return err;
+    }
+    return 0;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+    struct kvm_sw_breakpoint *bp, *next;
+    KVMState *s = current_env->kvm_state;
+    CPUState *env;
+
+    TAILQ_FOREACH_SAFE(bp, &s->kvm_sw_breakpoints, entry, next) {
+        if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) {
+            /* Try harder to find a CPU that currently sees the breakpoint. */
+            for (env = first_cpu; env != NULL; env = env->next_cpu) {
+                if (kvm_arch_remove_sw_breakpoint(env, bp) == 0)
+                    break;
+            }
+        }
+    }
+    kvm_arch_remove_all_hw_breakpoints();
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu)
+        kvm_update_guest_debug(env, 0);
+}
+
+#else /* !KVM_CAP_SET_GUEST_DEBUG */
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+    return -EINVAL;
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+                          target_ulong len, int type)
+{
+    return -EINVAL;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+                          target_ulong len, int type)
+{
+    return -EINVAL;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+}
+#endif /* !KVM_CAP_SET_GUEST_DEBUG */