[PATCH v3 01/10] qemu-kvm: Add KVM_CAP_X86_ROBUST_SINGLESTEP-awareness

Jan Kiszka
2010-02-24T14:22:25Z

This add-on patch to recent guest debugging refactorings adds the
requested awareness for KVM_CAP_X86_ROBUST_SINGLESTEP to both the
upstream as well as qemu-kvm's own code. Fortunately, code sharing
increased once again.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
kvm-all.c | 12 ++++++++++
kvm.h | 1 +
qemu-kvm-x86.c | 27 +----------------------
qemu-kvm.h | 1 +
target-i386/kvm.c | 60 ++++++++++++++++++++++++++++++----------------------
5 files changed, 51 insertions(+), 50 deletions(-)

diff --git a/kvm-all.c b/kvm-all.c
index bed562f..0776bf5 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -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()) {
diff --git a/kvm.h b/kvm.h
index a78a27f..427dad0 100644
--- a/kvm.h
+++ b/kvm.h
@@ -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);

diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c
index 5af9ce1..5db95f8 100644
--- a/qemu-kvm-x86.c
+++ b/qemu-kvm-x86.c
@@ -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)
diff --git a/qemu-kvm.h b/qemu-kvm.h
index 93b144f..172eba8 100644
--- a/qemu-kvm.h
+++ b/qemu-kvm.h
@@ -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
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 01963e1..cf64c9a 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -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;
}
--
1.6.0.2