]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly
authorAlexandru Elisei <alexandru.elisei@arm.com>
Tue, 2 Sep 2025 13:08:33 +0000 (14:08 +0100)
committerOliver Upton <oliver.upton@linux.dev>
Wed, 10 Sep 2025 09:56:19 +0000 (02:56 -0700)
Prior to commit 75a5fbaf6623 ("KVM: arm64: Compute MDCR_EL2 at
vcpu_load()"), host MDCR_EL2 was saved correctly:

kvm_arch_vcpu_load()
  kvm_vcpu_load_debug() /* Doesn't touch hardware MDCR_EL2. */
  kvm_vcpu_load_vhe()
    __activate_traps_common()
       /* Saves host MDCR_EL2. */
       *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2)
       /* Writes VCPU MDCR_EL2. */
       write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2)

The MDCR_EL2 value saved previously was restored in
kvm_arch_vcpu_put() -> kvm_vcpu_put_vhe().

After the aforementioned commit, host MDCR_EL2 is never saved:

kvm_arch_vcpu_load()
  kvm_vcpu_load_debug() /* Writes VCPU MDCR_EL2 */
  kvm_vcpu_load_vhe()
    __activate_traps_common()
       /* Saves **VCPU** MDCR_EL2. */
       *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2)
       /* Writes VCPU MDCR_EL2 a second time. */
       write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2)

kvm_arch_vcpu_put() -> kvm_vcpu_put_vhe() then restores the VCPU MDCR_EL2
value. Also VCPU's MDCR_EL2 value gets written to hardware twice now.

Fix this by saving the host MDCR_EL2 in kvm_arch_vcpu_load() before it gets
overwritten by the VCPU's MDCR_EL2 value, and restore it on VCPU put.

Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20250902130833.338216-3-alexandru.elisei@arm.com
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/debug.c
arch/arm64/kvm/hyp/include/hyp/switch.h
arch/arm64/kvm/hyp/nvhe/switch.c

index fee6e882490ab245759c18c99586ccc5079caafa..e027d9c32b0d309240a6861c663c054a1baeefcc 100644 (file)
@@ -145,6 +145,9 @@ void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu)
        /* Must be called before kvm_vcpu_load_vhe() */
        KVM_BUG_ON(vcpu_get_flag(vcpu, SYSREGS_ON_CPU), vcpu->kvm);
 
+       if (has_vhe())
+               *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2);
+
        /*
         * Determine which of the possible debug states we're in:
         *
@@ -191,6 +194,9 @@ void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu)
 
 void kvm_vcpu_put_debug(struct kvm_vcpu *vcpu)
 {
+       if (has_vhe())
+               write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2);
+
        if (likely(!(vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)))
                return;
 
index 84ec4e100fbb98ac915cf1f664ce58108e465962..b6682202edf3c6246f4e93e1ad09a0403eea7ff8 100644 (file)
@@ -431,9 +431,6 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu)
                vcpu_set_flag(vcpu, PMUSERENR_ON_CPU);
        }
 
-       *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2);
-       write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
-
        if (cpus_have_final_cap(ARM64_HAS_HCX)) {
                u64 hcrx = vcpu->arch.hcrx_el2;
                if (is_nested_ctxt(vcpu)) {
@@ -454,8 +451,6 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpu_context *hctxt = host_data_ptr(host_ctxt);
 
-       write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2);
-
        write_sysreg(0, hstr_el2);
        if (system_supports_pmuv3()) {
                write_sysreg(ctxt_sys_reg(hctxt, PMUSERENR_EL0), pmuserenr_el0);
index ccd575d5f6dec98278e07a666ade7ace52ebe5f3..d3b9ec8a7c283435cc771276fe8ee3b52fe6c413 100644 (file)
@@ -50,6 +50,10 @@ extern void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc);
 static void __activate_traps(struct kvm_vcpu *vcpu)
 {
        ___activate_traps(vcpu, vcpu->arch.hcr_el2);
+
+       *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2);
+       write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
+
        __activate_traps_common(vcpu);
        __activate_cptr_traps(vcpu);
 
@@ -93,6 +97,8 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu)
                isb();
        }
 
+       write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2);
+
        __deactivate_traps_common(vcpu);
 
        write_sysreg_hcr(this_cpu_ptr(&kvm_init_params)->hcr_el2);