summaryrefslogtreecommitdiff
path: root/target-i386
diff options
context:
space:
mode:
authorJan Kiszka <jan.kiszka@siemens.com>2010-03-01 19:10:29 +0100
committerMarcelo Tosatti <mtosatti@redhat.com>2010-03-04 00:29:26 -0300
commitb0b1d69079fcb9453f45aade9e9f6b71422147b0 (patch)
treed49073cd79a4987ded9b57a1b057a6329877160f /target-i386
parentc902760fb25f9c490af01e8f6bccaa8dd71cc224 (diff)
downloadqemu-b0b1d69079fcb9453f45aade9e9f6b71422147b0.tar.gz
KVM: Rework of guest debug state writing
So far we synchronized any dirty VCPU state back into the kernel before updating the guest debug state. This was a tribute to a deficite in x86 kernels before 2.6.33. But as this is an arch-dependent issue, it is better handle in the x86 part of KVM and remove the writeback point for generic code. This also avoids overwriting the flushed state later on if user space decides to change some more registers before resuming the guest. We furthermore need to reinject guest exceptions via the appropriate mechanism. That is KVM_SET_GUEST_DEBUG for older kernels and KVM_SET_VCPU_EVENTS for recent ones. Using both mechanisms at the same time will cause state corruptions. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/kvm.c47
1 files changed, 43 insertions, 4 deletions
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index d2116a7b3f..e0247ea631 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -852,6 +852,37 @@ static 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;
+}
+
int kvm_arch_put_registers(CPUState *env)
{
int ret;
@@ -880,6 +911,11 @@ int kvm_arch_put_registers(CPUState *env)
if (ret < 0)
return ret;
+ /* must be last */
+ ret = kvm_guest_debug_workarounds(env);
+ if (ret < 0)
+ return ret;
+
return 0;
}
@@ -1123,10 +1159,13 @@ int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
} else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc))
handle = 1;
- if (!handle)
- kvm_update_guest_debug(cpu_single_env,
- (arch_info->exception == 1) ?
- KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP);
+ if (!handle) {
+ cpu_synchronize_state(cpu_single_env);
+ assert(cpu_single_env->exception_injected == -1);
+
+ cpu_single_env->exception_injected = arch_info->exception;
+ cpu_single_env->has_error_code = 0;
+ }
return handle;
}