@@ -65,6 +65,7 @@ struct KVMState
int broken_set_mem_region;
int migration_log;
int vcpu_events;
+ int robust_singlestep;
#ifdef KVM_CAP_SET_GUEST_DEBUG
struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
#endif
@@ -673,6 +674,12 @@ int kvm_init(int smp_cpus)
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
#endif
+ s->robust_singlestep = 0;
+#ifdef KVM_CAP_X86_ROBUST_SINGLESTEP
+ s->robust_singlestep =
+ kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP);
+#endif
+
ret = kvm_arch_init(s, smp_cpus);
if (ret < 0)
goto err;
@@ -932,6 +939,11 @@ int kvm_has_vcpu_events(void)
return kvm_state->vcpu_events;
}
+int kvm_has_robust_singlestep(void)
+{
+ return kvm_state->robust_singlestep;
+}
+
void kvm_setup_guest_memory(void *start, size_t size)
{
if (!kvm_has_sync_mmu()) {
@@ -43,6 +43,7 @@ int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size);
int kvm_has_sync_mmu(void);
int kvm_has_vcpu_events(void);
+int kvm_has_robust_singlestep(void);
int kvm_put_vcpu_events(CPUState *env);
int kvm_get_vcpu_events(CPUState *env);
@@ -922,31 +922,8 @@ void kvm_arch_load_regs(CPUState *env)
if (rc == -1)
perror("kvm_set_msrs FAILED");
- /*
- * Kernels before 2.6.33 (which correlates with !kvm_has_vcpu_events())
- * overwrote flags.TF injected via SET_GUEST_DEBUG while updating GP regs.
- * Work around this by updating the debug state once again if
- * single-stepping is on.
- * Another reason to call kvm_update_guest_debug here is a pending debug
- * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to
- * reinject them via SET_GUEST_DEBUG.
- */
- if (!kvm_has_vcpu_events() &&
- (env->exception_injected != -1 || env->singlestep_enabled)) {
- unsigned long reinject_trap = 0;
-
- if (env->exception_injected == 1) {
- reinject_trap = KVM_GUESTDBG_INJECT_DB;
- } else if (env->exception_injected == 3) {
- reinject_trap = KVM_GUESTDBG_INJECT_BP;
- }
- env->exception_injected = -1;
-
- rc = kvm_update_guest_debug(env, reinject_trap);
- if (rc < 0) {
- perror("kvm_update_guest_debug FAILED");
- }
- }
+ /* must be last */
+ kvm_guest_debug_workarounds(env);
}
void kvm_load_tsc(CPUState *env)
@@ -1027,6 +1027,7 @@ struct KVMState {
int broken_set_mem_region;
int migration_log;
int vcpu_events;
+ int robust_singlestep;
#ifdef KVM_CAP_SET_GUEST_DEBUG
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
#endif
@@ -853,6 +853,37 @@ int kvm_get_vcpu_events(CPUState *env)
return 0;
}
+static int kvm_guest_debug_workarounds(CPUState *env)
+{
+ int ret = 0;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ unsigned long reinject_trap = 0;
+
+ if (!kvm_has_vcpu_events()) {
+ if (env->exception_injected == 1) {
+ reinject_trap = KVM_GUESTDBG_INJECT_DB;
+ } else if (env->exception_injected == 3) {
+ reinject_trap = KVM_GUESTDBG_INJECT_BP;
+ }
+ env->exception_injected = -1;
+ }
+
+ /*
+ * Kernels before KVM_CAP_X86_ROBUST_SINGLESTEP overwrote flags.TF
+ * injected via SET_GUEST_DEBUG while updating GP regs. Work around this
+ * by updating the debug state once again if single-stepping is on.
+ * Another reason to call kvm_update_guest_debug here is a pending debug
+ * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to
+ * reinject them via SET_GUEST_DEBUG.
+ */
+ if (reinject_trap ||
+ (!kvm_has_robust_singlestep() && env->singlestep_enabled)) {
+ ret = kvm_update_guest_debug(env, reinject_trap);
+ }
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
+ return ret;
+}
+
#ifdef KVM_UPSTREAM
int kvm_arch_put_registers(CPUState *env)
{
@@ -882,31 +913,10 @@ int kvm_arch_put_registers(CPUState *env)
if (ret < 0)
return ret;
- /*
- * Kernels before 2.6.33 (which correlates with !kvm_has_vcpu_events())
- * overwrote flags.TF injected via SET_GUEST_DEBUG while updating GP regs.
- * Work around this by updating the debug state once again if
- * single-stepping is on.
- * Another reason to call kvm_update_guest_debug here is a pending debug
- * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to
- * reinject them via SET_GUEST_DEBUG.
- */
- if (!kvm_has_vcpu_events() &&
- (env->exception_injected != -1 || env->singlestep_enabled)) {
- unsigned long reinject_trap = 0;
-
- if (env->exception_injected == 1) {
- reinject_trap = KVM_GUESTDBG_INJECT_DB;
- } else if (env->exception_injected == 3) {
- reinject_trap = KVM_GUESTDBG_INJECT_BP;
- }
- env->exception_injected = -1;
-
- ret = kvm_update_guest_debug(env, reinject_trap);
- if (ret < 0) {
- return ret;
- }
- }
+ /* must be last */
+ ret = kvm_guest_debug_workarounds(env);
+ if (ret < 0)
+ return ret;
return 0;
}