diff mbox series

perf/arm_pmuv3: Add PMUv3.9 per counter EL0 access control

Message ID 20241002184326.1105499-1-robh@kernel.org (mailing list archive)
State New, archived
Headers show
Series perf/arm_pmuv3: Add PMUv3.9 per counter EL0 access control | expand

Commit Message

Rob Herring (Arm) Oct. 2, 2024, 6:43 p.m. UTC
Armv8.9/9.4 PMUv3.9 adds per counter EL0 access controls. Per counter
access is enabled with the UEN bit in PMUSERENR_EL1 register. Individual
counters are enabled/disabled in the PMUACR_EL1 register. When UEN is
set, the CR/ER bits control EL0 write access and must be set to disable
write access.

With the access controls, the clearing of unused counters can be
skipped.

KVM also configures PMUSERENR_EL1 in order to trap to EL2. UEN does not
need to be set for it since only PMUv3.5 is exposed to guests.

Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
---
 arch/arm/include/asm/arm_pmuv3.h   |  6 ++++++
 arch/arm64/include/asm/arm_pmuv3.h | 10 ++++++++++
 arch/arm64/tools/sysreg            |  8 ++++++++
 drivers/perf/arm_pmuv3.c           | 29 +++++++++++++++++++----------
 include/linux/perf/arm_pmuv3.h     |  1 +
 5 files changed, 44 insertions(+), 10 deletions(-)

Comments

Will Deacon Oct. 24, 2024, 10:39 a.m. UTC | #1
On Wed, Oct 02, 2024 at 01:43:24PM -0500, Rob Herring (Arm) wrote:
> Armv8.9/9.4 PMUv3.9 adds per counter EL0 access controls. Per counter
> access is enabled with the UEN bit in PMUSERENR_EL1 register. Individual
> counters are enabled/disabled in the PMUACR_EL1 register. When UEN is
> set, the CR/ER bits control EL0 write access and must be set to disable
> write access.
> 
> With the access controls, the clearing of unused counters can be
> skipped.
> 
> KVM also configures PMUSERENR_EL1 in order to trap to EL2. UEN does not
> need to be set for it since only PMUv3.5 is exposed to guests.
> 
> Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
> ---
>  arch/arm/include/asm/arm_pmuv3.h   |  6 ++++++
>  arch/arm64/include/asm/arm_pmuv3.h | 10 ++++++++++
>  arch/arm64/tools/sysreg            |  8 ++++++++
>  drivers/perf/arm_pmuv3.c           | 29 +++++++++++++++++++----------
>  include/linux/perf/arm_pmuv3.h     |  1 +
>  5 files changed, 44 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
> index f63ba8986b24..d242b5e1ca0d 100644
> --- a/arch/arm/include/asm/arm_pmuv3.h
> +++ b/arch/arm/include/asm/arm_pmuv3.h
> @@ -231,6 +231,7 @@ static inline void kvm_vcpu_pmu_resync_el0(void) {}
>  #define ARMV8_PMU_DFR_VER_V3P1      0x4
>  #define ARMV8_PMU_DFR_VER_V3P4      0x5
>  #define ARMV8_PMU_DFR_VER_V3P5      0x6
> +#define ARMV8_PMU_DFR_VER_V3P9      0x9
>  #define ARMV8_PMU_DFR_VER_IMP_DEF   0xF
>  
>  static inline bool pmuv3_implemented(int pmuver)
> @@ -249,6 +250,11 @@ static inline bool is_pmuv3p5(int pmuver)
>  	return pmuver >= ARMV8_PMU_DFR_VER_V3P5;
>  }
>  
> +static inline bool is_pmuv3p9(int pmuver)
> +{
> +	return pmuver >= ARMV8_PMU_DFR_VER_V3P9;
> +}
> +
>  static inline u64 read_pmceid0(void)
>  {
>  	u64 val = read_sysreg(PMCEID0);
> diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/arm_pmuv3.h
> index 468a049bc63b..8a777dec8d88 100644
> --- a/arch/arm64/include/asm/arm_pmuv3.h
> +++ b/arch/arm64/include/asm/arm_pmuv3.h
> @@ -152,6 +152,11 @@ static inline void write_pmuserenr(u32 val)
>  	write_sysreg(val, pmuserenr_el0);
>  }
>  
> +static inline void write_pmuacr(u64 val)
> +{
> +	write_sysreg_s(val, SYS_PMUACR_EL1);
> +}
> +
>  static inline u64 read_pmceid0(void)
>  {
>  	return read_sysreg(pmceid0_el0);
> @@ -178,4 +183,9 @@ static inline bool is_pmuv3p5(int pmuver)
>  	return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P5;
>  }
>  
> +static inline bool is_pmuv3p9(int pmuver)
> +{
> +	return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P9;
> +}
> +
>  #endif
> diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
> index 8d637ac4b7c6..74fb5af91d4f 100644
> --- a/arch/arm64/tools/sysreg
> +++ b/arch/arm64/tools/sysreg
> @@ -1238,6 +1238,7 @@ UnsignedEnum	11:8	PMUVer
>  	0b0110	V3P5
>  	0b0111	V3P7
>  	0b1000	V3P8
> +	0b1001	V3P9
>  	0b1111	IMP_DEF
>  EndEnum
>  UnsignedEnum	7:4	TraceVer
> @@ -2178,6 +2179,13 @@ Field	4	P
>  Field	3:0	ALIGN
>  EndSysreg
>  
> +Sysreg	PMUACR_EL1	3	0	9	14	4
> +Res0	63:33
> +Field	32	F0
> +Field	31	C
> +Field	30:0	P
> +EndSysreg
> +
>  Sysreg	PMSELR_EL0	3	3	9	12	5
>  Res0	63:5
>  Field	4:0	SEL
> diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
> index 0afe02f879b4..bb93d32b86ea 100644
> --- a/drivers/perf/arm_pmuv3.c
> +++ b/drivers/perf/arm_pmuv3.c
> @@ -770,18 +770,27 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
>  	int i;
>  	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
>  
> -	/* Clear any unused counters to avoid leaking their contents */
> -	for_each_andnot_bit(i, cpu_pmu->cntr_mask, cpuc->used_mask,
> -			    ARMPMU_MAX_HWEVENTS) {
> -		if (i == ARMV8_PMU_CYCLE_IDX)
> -			write_pmccntr(0);
> -		else if (i == ARMV8_PMU_INSTR_IDX)
> -			write_pmicntr(0);
> -		else
> -			armv8pmu_write_evcntr(i, 0);
> +	if (is_pmuv3p9(cpu_pmu->pmuver)) {
> +		u64 mask = 0;
> +		for_each_set_bit(i, cpuc->used_mask, ARMPMU_MAX_HWEVENTS) {
> +			if (armv8pmu_event_has_user_read(cpuc->events[i]))
> +				mask |= BIT(i);
> +		}
> +		write_pmuacr(mask);

Since this is a new register, should we be zeroing it as part of our
reset callback?

Will
Rob Herring (Arm) Oct. 24, 2024, 4:41 p.m. UTC | #2
On Thu, Oct 24, 2024 at 5:40 AM Will Deacon <will@kernel.org> wrote:
>
> On Wed, Oct 02, 2024 at 01:43:24PM -0500, Rob Herring (Arm) wrote:
> > Armv8.9/9.4 PMUv3.9 adds per counter EL0 access controls. Per counter
> > access is enabled with the UEN bit in PMUSERENR_EL1 register. Individual
> > counters are enabled/disabled in the PMUACR_EL1 register. When UEN is
> > set, the CR/ER bits control EL0 write access and must be set to disable
> > write access.
> >
> > With the access controls, the clearing of unused counters can be
> > skipped.
> >
> > KVM also configures PMUSERENR_EL1 in order to trap to EL2. UEN does not
> > need to be set for it since only PMUv3.5 is exposed to guests.
> >
> > Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
> > ---
> >  arch/arm/include/asm/arm_pmuv3.h   |  6 ++++++
> >  arch/arm64/include/asm/arm_pmuv3.h | 10 ++++++++++
> >  arch/arm64/tools/sysreg            |  8 ++++++++
> >  drivers/perf/arm_pmuv3.c           | 29 +++++++++++++++++++----------
> >  include/linux/perf/arm_pmuv3.h     |  1 +
> >  5 files changed, 44 insertions(+), 10 deletions(-)
> >
> > diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
> > index f63ba8986b24..d242b5e1ca0d 100644
> > --- a/arch/arm/include/asm/arm_pmuv3.h
> > +++ b/arch/arm/include/asm/arm_pmuv3.h
> > @@ -231,6 +231,7 @@ static inline void kvm_vcpu_pmu_resync_el0(void) {}
> >  #define ARMV8_PMU_DFR_VER_V3P1      0x4
> >  #define ARMV8_PMU_DFR_VER_V3P4      0x5
> >  #define ARMV8_PMU_DFR_VER_V3P5      0x6
> > +#define ARMV8_PMU_DFR_VER_V3P9      0x9
> >  #define ARMV8_PMU_DFR_VER_IMP_DEF   0xF
> >
> >  static inline bool pmuv3_implemented(int pmuver)
> > @@ -249,6 +250,11 @@ static inline bool is_pmuv3p5(int pmuver)
> >       return pmuver >= ARMV8_PMU_DFR_VER_V3P5;
> >  }
> >
> > +static inline bool is_pmuv3p9(int pmuver)
> > +{
> > +     return pmuver >= ARMV8_PMU_DFR_VER_V3P9;
> > +}
> > +
> >  static inline u64 read_pmceid0(void)
> >  {
> >       u64 val = read_sysreg(PMCEID0);
> > diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/arm_pmuv3.h
> > index 468a049bc63b..8a777dec8d88 100644
> > --- a/arch/arm64/include/asm/arm_pmuv3.h
> > +++ b/arch/arm64/include/asm/arm_pmuv3.h
> > @@ -152,6 +152,11 @@ static inline void write_pmuserenr(u32 val)
> >       write_sysreg(val, pmuserenr_el0);
> >  }
> >
> > +static inline void write_pmuacr(u64 val)
> > +{
> > +     write_sysreg_s(val, SYS_PMUACR_EL1);
> > +}
> > +
> >  static inline u64 read_pmceid0(void)
> >  {
> >       return read_sysreg(pmceid0_el0);
> > @@ -178,4 +183,9 @@ static inline bool is_pmuv3p5(int pmuver)
> >       return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P5;
> >  }
> >
> > +static inline bool is_pmuv3p9(int pmuver)
> > +{
> > +     return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P9;
> > +}
> > +
> >  #endif
> > diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
> > index 8d637ac4b7c6..74fb5af91d4f 100644
> > --- a/arch/arm64/tools/sysreg
> > +++ b/arch/arm64/tools/sysreg
> > @@ -1238,6 +1238,7 @@ UnsignedEnum    11:8    PMUVer
> >       0b0110  V3P5
> >       0b0111  V3P7
> >       0b1000  V3P8
> > +     0b1001  V3P9
> >       0b1111  IMP_DEF
> >  EndEnum
> >  UnsignedEnum 7:4     TraceVer
> > @@ -2178,6 +2179,13 @@ Field  4       P
> >  Field        3:0     ALIGN
> >  EndSysreg
> >
> > +Sysreg       PMUACR_EL1      3       0       9       14      4
> > +Res0 63:33
> > +Field        32      F0
> > +Field        31      C
> > +Field        30:0    P
> > +EndSysreg
> > +
> >  Sysreg       PMSELR_EL0      3       3       9       12      5
> >  Res0 63:5
> >  Field        4:0     SEL
> > diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
> > index 0afe02f879b4..bb93d32b86ea 100644
> > --- a/drivers/perf/arm_pmuv3.c
> > +++ b/drivers/perf/arm_pmuv3.c
> > @@ -770,18 +770,27 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
> >       int i;
> >       struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
> >
> > -     /* Clear any unused counters to avoid leaking their contents */
> > -     for_each_andnot_bit(i, cpu_pmu->cntr_mask, cpuc->used_mask,
> > -                         ARMPMU_MAX_HWEVENTS) {
> > -             if (i == ARMV8_PMU_CYCLE_IDX)
> > -                     write_pmccntr(0);
> > -             else if (i == ARMV8_PMU_INSTR_IDX)
> > -                     write_pmicntr(0);
> > -             else
> > -                     armv8pmu_write_evcntr(i, 0);
> > +     if (is_pmuv3p9(cpu_pmu->pmuver)) {
> > +             u64 mask = 0;
> > +             for_each_set_bit(i, cpuc->used_mask, ARMPMU_MAX_HWEVENTS) {
> > +                     if (armv8pmu_event_has_user_read(cpuc->events[i]))
> > +                             mask |= BIT(i);
> > +             }
> > +             write_pmuacr(mask);
>
> Since this is a new register, should we be zeroing it as part of our
> reset callback?

That should not be necessary since EL0 access is gated off in PMUSEREN
in general and enabling this register additionally requires setting
the UEN bit. That's only done right after this.

But if the policy is to initialize all registers with unknown reset
state, then yes, I can add that.

Rob
Will Deacon Oct. 28, 2024, 5:26 p.m. UTC | #3
On Thu, Oct 24, 2024 at 11:41:11AM -0500, Rob Herring wrote:
> On Thu, Oct 24, 2024 at 5:40 AM Will Deacon <will@kernel.org> wrote:
> > On Wed, Oct 02, 2024 at 01:43:24PM -0500, Rob Herring (Arm) wrote:
> > > diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
> > > index 0afe02f879b4..bb93d32b86ea 100644
> > > --- a/drivers/perf/arm_pmuv3.c
> > > +++ b/drivers/perf/arm_pmuv3.c
> > > @@ -770,18 +770,27 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
> > >       int i;
> > >       struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
> > >
> > > -     /* Clear any unused counters to avoid leaking their contents */
> > > -     for_each_andnot_bit(i, cpu_pmu->cntr_mask, cpuc->used_mask,
> > > -                         ARMPMU_MAX_HWEVENTS) {
> > > -             if (i == ARMV8_PMU_CYCLE_IDX)
> > > -                     write_pmccntr(0);
> > > -             else if (i == ARMV8_PMU_INSTR_IDX)
> > > -                     write_pmicntr(0);
> > > -             else
> > > -                     armv8pmu_write_evcntr(i, 0);
> > > +     if (is_pmuv3p9(cpu_pmu->pmuver)) {
> > > +             u64 mask = 0;
> > > +             for_each_set_bit(i, cpuc->used_mask, ARMPMU_MAX_HWEVENTS) {
> > > +                     if (armv8pmu_event_has_user_read(cpuc->events[i]))
> > > +                             mask |= BIT(i);
> > > +             }
> > > +             write_pmuacr(mask);
> >
> > Since this is a new register, should we be zeroing it as part of our
> > reset callback?
> 
> That should not be necessary since EL0 access is gated off in PMUSEREN
> in general and enabling this register additionally requires setting
> the UEN bit. That's only done right after this.

That bit is set unconditionally, but it looks like we always write pmuacr
for v3p9 parts so it should be ok. Thanks.

Will
Will Deacon Oct. 28, 2024, 6:52 p.m. UTC | #4
On Wed, 02 Oct 2024 13:43:24 -0500, Rob Herring (Arm) wrote:
> Armv8.9/9.4 PMUv3.9 adds per counter EL0 access controls. Per counter
> access is enabled with the UEN bit in PMUSERENR_EL1 register. Individual
> counters are enabled/disabled in the PMUACR_EL1 register. When UEN is
> set, the CR/ER bits control EL0 write access and must be set to disable
> write access.
> 
> With the access controls, the clearing of unused counters can be
> skipped.
> 
> [...]

Applied to will (for-next/perf), thanks!

[1/1] perf/arm_pmuv3: Add PMUv3.9 per counter EL0 access control
      https://git.kernel.org/will/c/0bbff9ed8165

Cheers,
diff mbox series

Patch

diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
index f63ba8986b24..d242b5e1ca0d 100644
--- a/arch/arm/include/asm/arm_pmuv3.h
+++ b/arch/arm/include/asm/arm_pmuv3.h
@@ -231,6 +231,7 @@  static inline void kvm_vcpu_pmu_resync_el0(void) {}
 #define ARMV8_PMU_DFR_VER_V3P1      0x4
 #define ARMV8_PMU_DFR_VER_V3P4      0x5
 #define ARMV8_PMU_DFR_VER_V3P5      0x6
+#define ARMV8_PMU_DFR_VER_V3P9      0x9
 #define ARMV8_PMU_DFR_VER_IMP_DEF   0xF
 
 static inline bool pmuv3_implemented(int pmuver)
@@ -249,6 +250,11 @@  static inline bool is_pmuv3p5(int pmuver)
 	return pmuver >= ARMV8_PMU_DFR_VER_V3P5;
 }
 
+static inline bool is_pmuv3p9(int pmuver)
+{
+	return pmuver >= ARMV8_PMU_DFR_VER_V3P9;
+}
+
 static inline u64 read_pmceid0(void)
 {
 	u64 val = read_sysreg(PMCEID0);
diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/arm_pmuv3.h
index 468a049bc63b..8a777dec8d88 100644
--- a/arch/arm64/include/asm/arm_pmuv3.h
+++ b/arch/arm64/include/asm/arm_pmuv3.h
@@ -152,6 +152,11 @@  static inline void write_pmuserenr(u32 val)
 	write_sysreg(val, pmuserenr_el0);
 }
 
+static inline void write_pmuacr(u64 val)
+{
+	write_sysreg_s(val, SYS_PMUACR_EL1);
+}
+
 static inline u64 read_pmceid0(void)
 {
 	return read_sysreg(pmceid0_el0);
@@ -178,4 +183,9 @@  static inline bool is_pmuv3p5(int pmuver)
 	return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P5;
 }
 
+static inline bool is_pmuv3p9(int pmuver)
+{
+	return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P9;
+}
+
 #endif
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 8d637ac4b7c6..74fb5af91d4f 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -1238,6 +1238,7 @@  UnsignedEnum	11:8	PMUVer
 	0b0110	V3P5
 	0b0111	V3P7
 	0b1000	V3P8
+	0b1001	V3P9
 	0b1111	IMP_DEF
 EndEnum
 UnsignedEnum	7:4	TraceVer
@@ -2178,6 +2179,13 @@  Field	4	P
 Field	3:0	ALIGN
 EndSysreg
 
+Sysreg	PMUACR_EL1	3	0	9	14	4
+Res0	63:33
+Field	32	F0
+Field	31	C
+Field	30:0	P
+EndSysreg
+
 Sysreg	PMSELR_EL0	3	3	9	12	5
 Res0	63:5
 Field	4:0	SEL
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 0afe02f879b4..bb93d32b86ea 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -770,18 +770,27 @@  static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
 	int i;
 	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
 
-	/* Clear any unused counters to avoid leaking their contents */
-	for_each_andnot_bit(i, cpu_pmu->cntr_mask, cpuc->used_mask,
-			    ARMPMU_MAX_HWEVENTS) {
-		if (i == ARMV8_PMU_CYCLE_IDX)
-			write_pmccntr(0);
-		else if (i == ARMV8_PMU_INSTR_IDX)
-			write_pmicntr(0);
-		else
-			armv8pmu_write_evcntr(i, 0);
+	if (is_pmuv3p9(cpu_pmu->pmuver)) {
+		u64 mask = 0;
+		for_each_set_bit(i, cpuc->used_mask, ARMPMU_MAX_HWEVENTS) {
+			if (armv8pmu_event_has_user_read(cpuc->events[i]))
+				mask |= BIT(i);
+		}
+		write_pmuacr(mask);
+	} else {
+		/* Clear any unused counters to avoid leaking their contents */
+		for_each_andnot_bit(i, cpu_pmu->cntr_mask, cpuc->used_mask,
+				    ARMPMU_MAX_HWEVENTS) {
+			if (i == ARMV8_PMU_CYCLE_IDX)
+				write_pmccntr(0);
+			else if (i == ARMV8_PMU_INSTR_IDX)
+				write_pmicntr(0);
+			else
+				armv8pmu_write_evcntr(i, 0);
+		}
 	}
 
-	update_pmuserenr(ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_CR);
+	update_pmuserenr(ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_CR | ARMV8_PMU_USERENR_UEN);
 }
 
 static void armv8pmu_enable_event(struct perf_event *event)
diff --git a/include/linux/perf/arm_pmuv3.h b/include/linux/perf/arm_pmuv3.h
index 3372c1b56486..d698efba28a2 100644
--- a/include/linux/perf/arm_pmuv3.h
+++ b/include/linux/perf/arm_pmuv3.h
@@ -257,6 +257,7 @@ 
 #define ARMV8_PMU_USERENR_SW	(1 << 1) /* PMSWINC can be written at EL0 */
 #define ARMV8_PMU_USERENR_CR	(1 << 2) /* Cycle counter can be read at EL0 */
 #define ARMV8_PMU_USERENR_ER	(1 << 3) /* Event counter can be read at EL0 */
+#define ARMV8_PMU_USERENR_UEN	(1 << 4) /* Fine grained per counter access at EL0 */
 /* Mask for writable bits */
 #define ARMV8_PMU_USERENR_MASK	(ARMV8_PMU_USERENR_EN | ARMV8_PMU_USERENR_SW | \
 				 ARMV8_PMU_USERENR_CR | ARMV8_PMU_USERENR_ER)