KVM: arm64: guest debug, add support for single-step
authorAlex Bennée <alex.bennee@linaro.org>
Tue, 7 Jul 2015 16:29:58 +0000 (17:29 +0100)
committerMarc Zyngier <marc.zyngier@arm.com>
Tue, 21 Jul 2015 11:47:08 +0000 (12:47 +0100)
This adds support for single-stepping the guest. To do this we need to
manipulate the guests PSTATE.SS and MDSCR_EL1.SS bits to trigger
stepping. We take care to preserve MDSCR_EL1 and trap access to it to
ensure we don't affect the apparent state of the guest.

As we have to enable trapping of all software debug exceptions we
suppress the ability of the guest to single-step itself. If we didn't we
would have to deal with the exception arriving while the guest was in
kernelspace when the guest is expecting to single-step userspace. This
is something we don't want to unwind in the kernel. Once the host is no
longer debugging the guest its ability to single-step userspace is
restored.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/debug.c
arch/arm64/kvm/guest.c
arch/arm64/kvm/handle_exit.c

index c90c6a41c448c0410b1f5e0d4efff32dfdb1e103..cfb675466e86c32664810c9e2fcbe636d58fecb9 100644 (file)
@@ -123,6 +123,17 @@ struct kvm_vcpu_arch {
         * here.
         */
 
+       /*
+        * Guest registers we preserve during guest debugging.
+        *
+        * These shadow registers are updated by the kvm_handle_sys_reg
+        * trap handler if the guest accesses or updates them while we
+        * are using guest debug.
+        */
+       struct {
+               u32     mdscr_el1;
+       } guest_debug_preserved;
+
        /* Don't run the guest */
        bool pause;
 
index 8d1bfa4383109bd8335fb6b81f7da370183ff190..d439eb8f3239ecaab16abc29a30d23f03cc9bc01 100644 (file)
 
 #include <linux/kvm_host.h>
 
+#include <asm/debug-monitors.h>
+#include <asm/kvm_asm.h>
 #include <asm/kvm_arm.h>
+#include <asm/kvm_emulate.h>
+
+/* These are the bits of MDSCR_EL1 we may manipulate */
+#define MDSCR_EL1_DEBUG_MASK   (DBG_MDSCR_SS | \
+                               DBG_MDSCR_KDE | \
+                               DBG_MDSCR_MDE)
 
 static DEFINE_PER_CPU(u32, mdcr_el2);
 
+/**
+ * save/restore_guest_debug_regs
+ *
+ * For some debug operations we need to tweak some guest registers. As
+ * a result we need to save the state of those registers before we
+ * make those modifications.
+ *
+ * Guest access to MDSCR_EL1 is trapped by the hypervisor and handled
+ * after we have restored the preserved value to the main context.
+ */
+static void save_guest_debug_regs(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.guest_debug_preserved.mdscr_el1 = vcpu_sys_reg(vcpu, MDSCR_EL1);
+}
+
+static void restore_guest_debug_regs(struct kvm_vcpu *vcpu)
+{
+       vcpu_sys_reg(vcpu, MDSCR_EL1) = vcpu->arch.guest_debug_preserved.mdscr_el1;
+}
+
 /**
  * kvm_arm_init_debug - grab what we need for debug
  *
@@ -38,7 +66,6 @@ void kvm_arm_init_debug(void)
        __this_cpu_write(mdcr_el2, kvm_call_hyp(__kvm_get_mdcr_el2));
 }
 
-
 /**
  * kvm_arm_setup_debug - set up debug related stuff
  *
@@ -73,12 +100,45 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
        if (trap_debug)
                vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
 
-       /* Trap breakpoints? */
-       if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
+       /* Is Guest debugging in effect? */
+       if (vcpu->guest_debug) {
+               /* Route all software debug exceptions to EL2 */
                vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
+
+               /* Save guest debug state */
+               save_guest_debug_regs(vcpu);
+
+               /*
+                * Single Step (ARM ARM D2.12.3 The software step state
+                * machine)
+                *
+                * If we are doing Single Step we need to manipulate
+                * the guest's MDSCR_EL1.SS and PSTATE.SS. Once the
+                * step has occurred the hypervisor will trap the
+                * debug exception and we return to userspace.
+                *
+                * If the guest attempts to single step its userspace
+                * we would have to deal with a trapped exception
+                * while in the guest kernel. Because this would be
+                * hard to unwind we suppress the guest's ability to
+                * do so by masking MDSCR_EL.SS.
+                *
+                * This confuses guest debuggers which use
+                * single-step behind the scenes but everything
+                * returns to normal once the host is no longer
+                * debugging the system.
+                */
+               if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
+                       *vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
+                       vcpu_sys_reg(vcpu, MDSCR_EL1) |= DBG_MDSCR_SS;
+               } else {
+                       vcpu_sys_reg(vcpu, MDSCR_EL1) &= ~DBG_MDSCR_SS;
+               }
+       }
 }
 
 void kvm_arm_clear_debug(struct kvm_vcpu *vcpu)
 {
-       /* Nothing to do yet */
+       if (vcpu->guest_debug)
+               restore_guest_debug_regs(vcpu);
 }
index 22d22c54fd8d158a352fcae958e013e05e0ef158..48de4f4aaa1a567b530e0d2241dff02909dc4b6f 100644 (file)
@@ -332,7 +332,9 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
        return -EINVAL;
 }
 
-#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP)
+#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE |    \
+                           KVM_GUESTDBG_USE_SW_BP | \
+                           KVM_GUESTDBG_SINGLESTEP)
 
 /**
  * kvm_arch_vcpu_ioctl_set_guest_debug - set up guest debugging
index 27f38a9f9ea277daaf48e1e344f4f0f32097ae2a..e9de13ed477e699aa755ee640aacd2219e2392f8 100644 (file)
@@ -103,6 +103,7 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu, struct kvm_run *run)
        run->debug.arch.hsr = hsr;
 
        switch (hsr >> ESR_ELx_EC_SHIFT) {
+       case ESR_ELx_EC_SOFTSTP_LOW:
        case ESR_ELx_EC_BKPT32:
        case ESR_ELx_EC_BRK64:
                break;
@@ -130,6 +131,7 @@ static exit_handle_fn arm_exit_handlers[] = {
        [ESR_ELx_EC_SYS64]      = kvm_handle_sys_reg,
        [ESR_ELx_EC_IABT_LOW]   = kvm_handle_guest_abort,
        [ESR_ELx_EC_DABT_LOW]   = kvm_handle_guest_abort,
+       [ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug,
        [ESR_ELx_EC_BKPT32]     = kvm_handle_guest_debug,
        [ESR_ELx_EC_BRK64]      = kvm_handle_guest_debug,
 };