KVM: arm64: Clear PSTATE.SS when the Software Step state was Active-pending
authorReiji Watanabe <reijiw@google.com>
Sat, 17 Sep 2022 01:05:58 +0000 (18:05 -0700)
committerMarc Zyngier <maz@kernel.org>
Mon, 19 Sep 2022 09:48:53 +0000 (10:48 +0100)
While userspace enables single-step, if the Software Step state at the
last guest exit was "Active-pending", clear PSTATE.SS on guest entry
to restore the state.

Currently, KVM sets PSTATE.SS to 1 on every guest entry while userspace
enables single-step for the vCPU (with KVM_GUESTDBG_SINGLESTEP).
It means KVM always makes the vCPU's Software Step state
"Active-not-pending" on the guest entry, which lets the VCPU perform
single-step (then Software Step exception is taken). This could cause
extra single-step (without returning to userspace) if the Software Step
state at the last guest exit was "Active-pending" (i.e. the last
exit was triggered by an asynchronous exception after the single-step
is performed, but before the Software Step exception is taken.
See "Figure D2-3 Software step state machine" and "D2.12.7 Behavior
in the active-pending state" in ARM DDI 0487I.a for more info about
this behavior).

Fix this by clearing PSTATE.SS on guest entry if the Software Step state
at the last exit was "Active-pending" so that KVM restore the state (and
the exception is taken before further single-step is performed).

Fixes: 337b99bf7edf ("KVM: arm64: guest debug, add support for single-step")
Signed-off-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220917010600.532642-3-reijiw@google.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 ccf8a14..45e2136 100644 (file)
@@ -536,6 +536,9 @@ struct kvm_vcpu_arch {
 #define IN_WFIT                        __vcpu_single_flag(sflags, BIT(3))
 /* vcpu system registers loaded on physical CPU */
 #define SYSREGS_ON_CPU         __vcpu_single_flag(sflags, BIT(4))
+/* Software step state is Active-pending */
+#define DBG_SS_ACTIVE_PENDING  __vcpu_single_flag(sflags, BIT(5))
+
 
 /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
 #define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) +     \
index 1bd2a1a..56361e5 100644 (file)
@@ -200,7 +200,18 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
                 * debugging the system.
                 */
                if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
-                       *vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
+                       /*
+                        * If the software step state at the last guest exit
+                        * was Active-pending, we don't set DBG_SPSR_SS so
+                        * that the state is maintained (to not run another
+                        * single-step until the pending Software Step
+                        * exception is taken).
+                        */
+                       if (!vcpu_get_flag(vcpu, DBG_SS_ACTIVE_PENDING))
+                               *vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
+                       else
+                               *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
+
                        mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
                        mdscr |= DBG_MDSCR_SS;
                        vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
@@ -274,6 +285,15 @@ void kvm_arm_clear_debug(struct kvm_vcpu *vcpu)
         * Restore the guest's debug registers if we were using them.
         */
        if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
+               if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
+                       if (!(*vcpu_cpsr(vcpu) & DBG_SPSR_SS))
+                               /*
+                                * Mark the vcpu as ACTIVE_PENDING
+                                * until Software Step exception is taken.
+                                */
+                               vcpu_set_flag(vcpu, DBG_SS_ACTIVE_PENDING);
+               }
+
                restore_guest_debug_regs(vcpu);
 
                /*
index f802a3b..2ff13a3 100644 (file)
@@ -937,6 +937,7 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
        } else {
                /* If not enabled clear all flags */
                vcpu->guest_debug = 0;
+               vcpu_clear_flag(vcpu, DBG_SS_ACTIVE_PENDING);
        }
 
 out:
index bbe5b39..e778eef 100644 (file)
@@ -152,8 +152,14 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
        run->debug.arch.hsr_high = upper_32_bits(esr);
        run->flags = KVM_DEBUG_ARCH_HSR_HIGH_VALID;
 
-       if (ESR_ELx_EC(esr) == ESR_ELx_EC_WATCHPT_LOW)
+       switch (ESR_ELx_EC(esr)) {
+       case ESR_ELx_EC_WATCHPT_LOW:
                run->debug.arch.far = vcpu->arch.fault.far_el2;
+               break;
+       case ESR_ELx_EC_SOFTSTP_LOW:
+               vcpu_clear_flag(vcpu, DBG_SS_ACTIVE_PENDING);
+               break;
+       }
 
        return 0;
 }