KVM: arm64: Evaluate debug owner at vcpu_load()
authorOliver Upton <oliver.upton@linux.dev>
Thu, 19 Dec 2024 22:41:03 +0000 (14:41 -0800)
committerMarc Zyngier <maz@kernel.org>
Fri, 20 Dec 2024 09:01:25 +0000 (09:01 +0000)
In preparation for tossing the debug_ptr mess, introduce an enumeration
to track the ownership of the debug registers while in the guest. Update
the owner at vcpu_load() based on whether the host needs to steal the
guest's debug context or if breakpoints/watchpoints are actively in use.

Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20241219224116.3941496-7-oliver.upton@linux.dev
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/arm.c
arch/arm64/kvm/debug.c
arch/arm64/kvm/sys_regs.c

index d196bf0fce524c9befbf33f16c554eeb6d0e4583..f65b30bab0abf63ac980e5022ae359f71e3cfd49 100644 (file)
@@ -756,6 +756,12 @@ struct kvm_vcpu_arch {
        struct kvm_guest_debug_arch vcpu_debug_state;
        struct kvm_guest_debug_arch external_debug_state;
 
+       enum {
+               VCPU_DEBUG_FREE,
+               VCPU_DEBUG_HOST_OWNED,
+               VCPU_DEBUG_GUEST_OWNED,
+       } debug_owner;
+
        /* VGIC state */
        struct vgic_cpu vgic_cpu;
        struct arch_timer_cpu timer_cpu;
@@ -1345,10 +1351,15 @@ void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu);
 void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
 void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
 void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
+void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu);
+void kvm_debug_set_guest_ownership(struct kvm_vcpu *vcpu);
 
 #define kvm_vcpu_os_lock_enabled(vcpu)         \
        (!!(__vcpu_sys_reg(vcpu, OSLSR_EL1) & OSLSR_EL1_OSLK))
 
+#define kvm_host_owns_debug_regs(vcpu)         \
+       ((vcpu)->arch.debug_owner == VCPU_DEBUG_HOST_OWNED)
+
 int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
                               struct kvm_device_attr *attr);
 int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
index 3822774840e1fbcca886729cdf6706b54337da38..a068337da52ab172cad4f9e74178adb437c00b9d 100644 (file)
@@ -598,6 +598,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 
        kvm_vgic_load(vcpu);
        kvm_timer_vcpu_load(vcpu);
+       kvm_vcpu_load_debug(vcpu);
        if (has_vhe())
                kvm_vcpu_load_vhe(vcpu);
        kvm_arch_vcpu_load_fp(vcpu);
index fef03456284bf5ce74cbcb0601c8df123e791785..548016439acaa73afc54508887a2d20ffb294f72 100644 (file)
@@ -317,3 +317,49 @@ void kvm_init_host_debug_data(void)
            !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
                host_data_set_flag(HAS_TRBE);
 }
+
+void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu)
+{
+       u64 mdscr;
+
+       /* Must be called before kvm_vcpu_load_vhe() */
+       KVM_BUG_ON(vcpu_get_flag(vcpu, SYSREGS_ON_CPU), vcpu->kvm);
+
+       /*
+        * Determine which of the possible debug states we're in:
+        *
+        *  - VCPU_DEBUG_HOST_OWNED: KVM has taken ownership of the guest's
+        *    breakpoint/watchpoint registers, or needs to use MDSCR_EL1 to do
+        *    software step or emulate the effects of the OS Lock being enabled.
+        *
+        *  - VCPU_DEBUG_GUEST_OWNED: The guest has debug exceptions enabled, and
+        *    the breakpoint/watchpoint registers need to be loaded eagerly.
+        *
+        *  - VCPU_DEBUG_FREE: Neither of the above apply, no breakpoint/watchpoint
+        *    context needs to be loaded on the CPU.
+        */
+       if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
+               vcpu->arch.debug_owner = VCPU_DEBUG_HOST_OWNED;
+       } else {
+               mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
+
+               if (mdscr & (MDSCR_EL1_KDE | MDSCR_EL1_MDE))
+                       vcpu->arch.debug_owner = VCPU_DEBUG_GUEST_OWNED;
+               else
+                       vcpu->arch.debug_owner = VCPU_DEBUG_FREE;
+       }
+}
+
+/*
+ * Updates ownership of the debug registers after a trapped guest access to a
+ * breakpoint/watchpoint register. Host ownership of the debug registers is of
+ * strictly higher priority, and it is the responsibility of the VMM to emulate
+ * guest debug exceptions in this configuration.
+ */
+void kvm_debug_set_guest_ownership(struct kvm_vcpu *vcpu)
+{
+       if (kvm_host_owns_debug_regs(vcpu))
+               return;
+
+       vcpu->arch.debug_owner = VCPU_DEBUG_GUEST_OWNED;
+}
index e2a5c2918d9e5af9ee8526dce221d6e80c292d03..e45a096be669249e3ab4269c16998ed465ccf410 100644 (file)
@@ -656,6 +656,7 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu,
        if (p->is_write)
                vcpu_set_flag(vcpu, DEBUG_DIRTY);
 
+       kvm_debug_set_guest_ownership(vcpu);
        trace_trap_reg(__func__, r->reg, p->is_write, p->regval);
 
        return true;
@@ -684,6 +685,7 @@ static void reg_to_dbg(struct kvm_vcpu *vcpu,
        val |= (p->regval & (mask >> shift)) << shift;
        *dbg_reg = val;
 
+       kvm_debug_set_guest_ownership(vcpu);
        vcpu_set_flag(vcpu, DEBUG_DIRTY);
 }