diff mbox series

[v7,12/38] KVM: arm64: Add a KVM flag indicating emulating debug regs access is needed

Message ID 20220419065544.3616948-13-reijiw@google.com (mailing list archive)
State New, archived
Headers show
Series KVM: arm64: Make CPU ID registers writable by userspace | expand

Commit Message

Reiji Watanabe April 19, 2022, 6:55 a.m. UTC
Highest numbered breakpoints must be context aware breakpoints
(as specified by Arm ARM).  If the number of non-context aware
breakpoints for the guest is decreased by userspace, simply narrowing
the breakpoints will be problematic because it will lead to
narrowing context aware breakpoints for the guest.

Introduce KVM_ARCH_FLAG_EMULATE_DEBUG_REGS for kvm->arch.flags to
indicate trapping debug reg access is needed, and enable the trapping
when the flag is set.  Set the new flag at the first KVM_RUN if the
number of non-context aware breakpoints for the guest is decreased
by userspace.

No code sets the new flag yet since ID_AA64DFR0_EL1 is not configurable
by userspace.

Signed-off-by: Reiji Watanabe <reijiw@google.com>
---
 arch/arm64/include/asm/kvm_host.h |  3 +++
 arch/arm64/kvm/debug.c            |  7 ++++++-
 arch/arm64/kvm/sys_regs.c         | 35 +++++++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index a43fddd58e68..dbed94e759a8 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -136,6 +136,8 @@  struct kvm_arch {
 	 */
 #define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		3
 #define KVM_ARCH_FLAG_EL1_32BIT				4
+	/* Access to debug registers need to be emulated ? */
+#define KVM_ARCH_FLAG_EMULATE_DEBUG_REGS		5
 
 	unsigned long flags;
 
@@ -786,6 +788,7 @@  long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
 
 void set_default_id_regs(struct kvm *kvm);
 int kvm_set_id_reg_feature(struct kvm *kvm, u32 id, u8 field_shift, u8 fval);
+void kvm_vcpu_breakpoint_config(struct kvm_vcpu *vcpu);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index 4fd5c216c4bb..6eb146d908f8 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -106,10 +106,14 @@  static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
 	 *  (KVM_GUESTDBG_USE_HW is set).
 	 *  - The guest is not using debug (KVM_ARM64_DEBUG_DIRTY is clear).
 	 *  - The guest has enabled the OS Lock (debug exceptions are blocked).
+	 *  - The guest's access to debug registers needs to be emulated
+	 *    (the number of non-context aware breakpoints for the guest
+	 *     is decreased by userspace).
 	 */
 	if ((vcpu->guest_debug & KVM_GUESTDBG_USE_HW) ||
 	    !(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY) ||
-	    kvm_vcpu_os_lock_enabled(vcpu))
+	    kvm_vcpu_os_lock_enabled(vcpu) ||
+	    test_bit(KVM_ARCH_FLAG_EMULATE_DEBUG_REGS, &vcpu->kvm->arch.flags))
 		vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
 
 	trace_kvm_arm_set_dreg32("MDCR_EL2", vcpu->arch.mdcr_el2);
@@ -124,6 +128,7 @@  static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
  */
 void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu)
 {
+	kvm_vcpu_breakpoint_config(vcpu);
 	preempt_disable();
 	kvm_arm_setup_mdcr_el2(vcpu);
 	preempt_enable();
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index b68ae53af792..f4aae4ccffd0 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -844,6 +844,41 @@  static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
 	}
 }
 
+#define AA64DFR0_BRPS(v)	\
+	((u8)cpuid_feature_extract_unsigned_field(v, ID_AA64DFR0_BRPS_SHIFT))
+#define AA64DFR0_CTX_CMPS(v)	\
+	((u8)cpuid_feature_extract_unsigned_field(v, ID_AA64DFR0_CTX_CMPS_SHIFT))
+
+/*
+ * Set KVM_ARCH_FLAG_EMULATE_DEBUG_REGS in the VM flags when the number of
+ * non-context aware breakpoints for the guest is decreased by userspace
+ * (meaning that debug register accesses need to be emulated).
+ */
+void kvm_vcpu_breakpoint_config(struct kvm_vcpu *vcpu)
+{
+	u64 p_val = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
+	u64 v_val = read_id_reg_with_encoding(vcpu, SYS_ID_AA64DFR0_EL1);
+	u8 v_nbpn, p_nbpn;
+	struct kvm *kvm = vcpu->kvm;
+
+	/*
+	 * Check the number of normal (non-context aware) breakpoints
+	 * for the guest and the host.
+	 */
+	v_nbpn = AA64DFR0_BRPS(v_val) - AA64DFR0_CTX_CMPS(v_val);
+	p_nbpn = AA64DFR0_BRPS(p_val) - AA64DFR0_CTX_CMPS(p_val);
+	if (v_nbpn >= p_nbpn)
+		/*
+		 * Nothing to do if the number of normal breakpoints for the
+		 * guest is not decreased by userspace (meaning KVM doesn't
+		 * need to emulate an access of debug registers).
+		 */
+		return;
+
+	if (!test_bit(KVM_ARCH_FLAG_EMULATE_DEBUG_REGS, &kvm->arch.flags))
+		set_bit(KVM_ARCH_FLAG_EMULATE_DEBUG_REGS, &kvm->arch.flags);
+}
+
 /*
  * We want to avoid world-switching all the DBG registers all the
  * time: