linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes
@ 2025-09-02 13:08 Alexandru Elisei
  2025-09-02 13:08 ` [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1 Alexandru Elisei
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Alexandru Elisei @ 2025-09-02 13:08 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	linux-arm-kernel, kvmarm, catalin.marinas, will

This is v2 of the patch that zeroes PMSCR_EL1 [1] at boot, hopefully now it's
not as bad.

Picked up another fix - patch #2 ("KVM: arm64: VHE: Save and restore host
MDCR_EL2 value correctly") is new.

[1] https://lore.kernel.org/kvmarm/20241106122654.38234-1-alexandru.elisei@arm.com/

Alexandru Elisei (2):
  KVM: arm64: VHE: Initialize PMSCR_EL1
  KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly

 arch/arm64/kvm/debug.c                  | 20 ++++++++++++++++----
 arch/arm64/kvm/hyp/include/hyp/switch.h |  5 -----
 arch/arm64/kvm/hyp/nvhe/switch.c        |  6 ++++++
 3 files changed, 22 insertions(+), 9 deletions(-)


base-commit: b320789d6883cc00ac78ce83bccbfe7ed58afcf0
-- 
2.51.0



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1
  2025-09-02 13:08 [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Alexandru Elisei
@ 2025-09-02 13:08 ` Alexandru Elisei
  2025-09-04  6:55   ` Oliver Upton
  2025-09-02 13:08 ` [PATCH v2 2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly Alexandru Elisei
  2025-09-05  9:41 ` [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Oliver Upton
  2 siblings, 1 reply; 7+ messages in thread
From: Alexandru Elisei @ 2025-09-02 13:08 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	linux-arm-kernel, kvmarm, catalin.marinas, will

According to the pseudocode for StatisticalProfilingEnabled() from Arm
DDI0487L.b, PMSCR_EL1 controls profiling at EL1 and EL0:

- PMSCR_EL1.E1SPE controls profiling at EL1.
- PMSCR_EL1.E0SPE controls profiling at EL0 if HCR_EL2.TGE=0.

These two fields reset to UNKNOWN values.

When KVM runs in VHE mode and profiling is enabled in the host, before
entering a guest, KVM does not touch any of the SPE registers, leaving the
buffer enabled, and it clears HCR_EL2.TGE. As a result, depending on the
reset value for the E1SPE and E0SPE fields, KVM might unintentionally
profile a guest. Make the behaviour consistent and predictable by clearing
PMSCR_EL1 when KVM initialises the host debug configuration.

Note that this is not a problem for nVHE, because KVM clears
PMSCR_EL1.{E1SPE,E0SPE} before entering the guest.

Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
---

Tested by setting PMSCR_EL1.{E1SPE,E0SPE} in __finalise_el2 (to simulate a
system where the bits reset to 1) and clearing MDCR_EL2.TPMS (so guest
accesses to PMSCR_EL1 are not trapped). I booted the kernel in the model
with FEAT_SPE, but without FEAT_FGT (so I wouldn't have to fiddle with
those traps too).  Then ran a hacked kvm-unit-tests test that reads
PMSCR_EL1.

Without the patch: PMSCR_EL1 = 0x3 - E1SPE and E0SPE are set.
With the patch: PMSCR_EL1 = 0x0- E1SPE and E0SPE are not set.

Also tested by running the series on a cubie a5e (doesn't have FEAT_SPE),
in vhe, nvhe and protected modes.

 arch/arm64/kvm/debug.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index 381382c19fe4..e7ce0d5a622d 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -74,13 +74,19 @@ void kvm_init_host_debug_data(void)
 	*host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
 	*host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);
 
+	if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
+	    !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P)) {
+		if (has_vhe()) {
+			/* Clear E{0,1}SPE, which reset to UNKNOWN values. */
+			write_sysreg_el1(0, SYS_PMSCR);
+		} else {
+			host_data_set_flag(HAS_SPE);
+		}
+	}
+
 	if (has_vhe())
 		return;
 
-	if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
-	    !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P))
-		host_data_set_flag(HAS_SPE);
-
 	/* Check if we have BRBE implemented and available at the host */
 	if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
 		host_data_set_flag(HAS_BRBE);
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly
  2025-09-02 13:08 [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Alexandru Elisei
  2025-09-02 13:08 ` [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1 Alexandru Elisei
@ 2025-09-02 13:08 ` Alexandru Elisei
  2025-09-04  6:55   ` Oliver Upton
  2025-09-05  9:41 ` [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Oliver Upton
  2 siblings, 1 reply; 7+ messages in thread
From: Alexandru Elisei @ 2025-09-02 13:08 UTC (permalink / raw)
  To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
	linux-arm-kernel, kvmarm, catalin.marinas, will

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>
---

Tested on a cubie a5e by adding a few print statements. Without the patch:

[ 1009.416572] kvm_arch_init_vm: MDCR_EL2=0x6
[ 1028.397204] kvm_arch_destroy_vm: MDCR_EL2=0xe66
[ 1049.271878] kvm_arch_init_vm: MDCR_EL2=0xe66
[ 1066.030000] kvm_arch_destroy_vm: MDCR_EL2=0xe66

With the patch:

[  977.814892] kvm_arch_init_vm: MDCR_EL2=0x6
[ 1040.084919] kvm_arch_destroy_vm: MDCR_EL2=0x6
[ 1045.987826] kvm_arch_init_vm: MDCR_EL2=0x6
[ 1128.720366] kvm_arch_destroy_vm: MDCR_EL2=0x6

 arch/arm64/kvm/debug.c                  | 6 ++++++
 arch/arm64/kvm/hyp/include/hyp/switch.h | 5 -----
 arch/arm64/kvm/hyp/nvhe/switch.c        | 6 ++++++
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index e7ce0d5a622d..c81b11c42e4d 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -144,6 +144,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:
 	 *
@@ -190,6 +193,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;
 
diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
index 84ec4e100fbb..b6682202edf3 100644
--- a/arch/arm64/kvm/hyp/include/hyp/switch.h
+++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
@@ -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);
diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c
index ccd575d5f6de..d3b9ec8a7c28 100644
--- a/arch/arm64/kvm/hyp/nvhe/switch.c
+++ b/arch/arm64/kvm/hyp/nvhe/switch.c
@@ -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);
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1
  2025-09-02 13:08 ` [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1 Alexandru Elisei
@ 2025-09-04  6:55   ` Oliver Upton
  2025-09-04  9:30     ` Alexandru Elisei
  0 siblings, 1 reply; 7+ messages in thread
From: Oliver Upton @ 2025-09-04  6:55 UTC (permalink / raw)
  To: Alexandru Elisei
  Cc: maz, joey.gouly, suzuki.poulose, yuzenghui, linux-arm-kernel,
	kvmarm, catalin.marinas, will

On Tue, Sep 02, 2025 at 02:08:32PM +0100, Alexandru Elisei wrote:
> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
> index 381382c19fe4..e7ce0d5a622d 100644
> --- a/arch/arm64/kvm/debug.c
> +++ b/arch/arm64/kvm/debug.c
> @@ -74,13 +74,19 @@ void kvm_init_host_debug_data(void)
>  	*host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
>  	*host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);
>  
> +	if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
> +	    !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P)) {
> +		if (has_vhe()) {
> +			/* Clear E{0,1}SPE, which reset to UNKNOWN values. */
> +			write_sysreg_el1(0, SYS_PMSCR);
> +		} else {
> +			host_data_set_flag(HAS_SPE);
> +		}
> +	}
> +

nit: While this is correct, from a code organization perspective it
doesn't belong here. The rest of this function is concerned with probing
hardware state and initializing the corresponding host data.

I prefer Marc's suggestion which could be wrapped up in a function called
'kvm_debug_init_vhe()' or similar.

Thanks,
Oliver


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly
  2025-09-02 13:08 ` [PATCH v2 2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly Alexandru Elisei
@ 2025-09-04  6:55   ` Oliver Upton
  0 siblings, 0 replies; 7+ messages in thread
From: Oliver Upton @ 2025-09-04  6:55 UTC (permalink / raw)
  To: Alexandru Elisei
  Cc: maz, joey.gouly, suzuki.poulose, yuzenghui, linux-arm-kernel,
	kvmarm, catalin.marinas, will

On Tue, Sep 02, 2025 at 02:08:33PM +0100, Alexandru Elisei wrote:
> 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>

Thanks,
Oliver


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1
  2025-09-04  6:55   ` Oliver Upton
@ 2025-09-04  9:30     ` Alexandru Elisei
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandru Elisei @ 2025-09-04  9:30 UTC (permalink / raw)
  To: Oliver Upton
  Cc: maz, joey.gouly, suzuki.poulose, yuzenghui, linux-arm-kernel,
	kvmarm, catalin.marinas, will

Hi Oliver,

On Wed, Sep 03, 2025 at 11:55:00PM -0700, Oliver Upton wrote:
> On Tue, Sep 02, 2025 at 02:08:32PM +0100, Alexandru Elisei wrote:
> > diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
> > index 381382c19fe4..e7ce0d5a622d 100644
> > --- a/arch/arm64/kvm/debug.c
> > +++ b/arch/arm64/kvm/debug.c
> > @@ -74,13 +74,19 @@ void kvm_init_host_debug_data(void)
> >  	*host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
> >  	*host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);
> >  
> > +	if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
> > +	    !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P)) {
> > +		if (has_vhe()) {
> > +			/* Clear E{0,1}SPE, which reset to UNKNOWN values. */
> > +			write_sysreg_el1(0, SYS_PMSCR);
> > +		} else {
> > +			host_data_set_flag(HAS_SPE);
> > +		}
> > +	}
> > +
> 
> nit: While this is correct, from a code organization perspective it
> doesn't belong here. The rest of this function is concerned with probing
> hardware state and initializing the corresponding host data.

Yes, you're totally right.

> 
> I prefer Marc's suggestion which could be wrapped up in a function called
> 'kvm_debug_init_vhe()' or similar.

I forgot about Marc's suggestion, my bad. Will do that for the next iteration.

Thanks,
Alex

> 
> Thanks,
> Oliver


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes
  2025-09-02 13:08 [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Alexandru Elisei
  2025-09-02 13:08 ` [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1 Alexandru Elisei
  2025-09-02 13:08 ` [PATCH v2 2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly Alexandru Elisei
@ 2025-09-05  9:41 ` Oliver Upton
  2 siblings, 0 replies; 7+ messages in thread
From: Oliver Upton @ 2025-09-05  9:41 UTC (permalink / raw)
  To: maz, joey.gouly, suzuki.poulose, yuzenghui, linux-arm-kernel,
	kvmarm, catalin.marinas, will, Alexandru Elisei
  Cc: Oliver Upton

On Tue, 02 Sep 2025 14:08:31 +0100, Alexandru Elisei wrote:
> This is v2 of the patch that zeroes PMSCR_EL1 [1] at boot, hopefully now it's
> not as bad.
> 
> Picked up another fix - patch #2 ("KVM: arm64: VHE: Save and restore host
> MDCR_EL2 value correctly") is new.
> 
> [1] https://lore.kernel.org/kvmarm/20241106122654.38234-1-alexandru.elisei@arm.com/
> 
> [...]

I squashed the suggestion into the second patch.

Applied to fixes, thanks!

[1/2] KVM: arm64: VHE: Initialize PMSCR_EL1
      https://git.kernel.org/kvmarm/kvmarm/c/e643542c7160
[2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly
      https://git.kernel.org/kvmarm/kvmarm/c/2f67b9a64842

--
Best,
Oliver


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-09-05 14:34 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-02 13:08 [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Alexandru Elisei
2025-09-02 13:08 ` [PATCH v2 1/2] KVM: arm64: VHE: Initialize PMSCR_EL1 Alexandru Elisei
2025-09-04  6:55   ` Oliver Upton
2025-09-04  9:30     ` Alexandru Elisei
2025-09-02 13:08 ` [PATCH v2 2/2] KVM: arm64: VHE: Save and restore host MDCR_EL2 value correctly Alexandru Elisei
2025-09-04  6:55   ` Oliver Upton
2025-09-05  9:41 ` [PATCH v2 0/2] KVM: arm64: VHE: Debug fixes Oliver Upton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).