* [PATCH V18 1/9] arm64/sysreg: Add BRBE registers and fields
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-13 10:10 ` Mark Rutland
2024-06-13 6:17 ` [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED Anshuman Khandual
` (7 subsequent siblings)
8 siblings, 1 reply; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users
This patch adds definitions related to the Branch Record Buffer Extension
(BRBE) as per ARM DDI 0487K.a. These will be used by KVM and a BRBE driver
in subsequent patches.
Some existing BRBE definitions in asm/sysreg.h are replaced with equivalent
generated definitions.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
----
Changes in V18:
- Changed BRBIDR0_EL1 register fields CC and FORMAT, updated the commit message
arch/arm64/include/asm/sysreg.h | 17 ++---
arch/arm64/tools/sysreg | 131 ++++++++++++++++++++++++++++++++
2 files changed, 137 insertions(+), 11 deletions(-)
---
arch/arm64/include/asm/sysreg.h | 17 ++---
arch/arm64/tools/sysreg | 131 ++++++++++++++++++++++++++++++++
2 files changed, 137 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index af3b206fa423..cb3c9c83dc7a 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -195,16 +195,8 @@
#define SYS_DBGVCR32_EL2 sys_reg(2, 4, 0, 7, 0)
#define SYS_BRBINF_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 0))
-#define SYS_BRBINFINJ_EL1 sys_reg(2, 1, 9, 1, 0)
#define SYS_BRBSRC_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 1))
-#define SYS_BRBSRCINJ_EL1 sys_reg(2, 1, 9, 1, 1)
#define SYS_BRBTGT_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 2))
-#define SYS_BRBTGTINJ_EL1 sys_reg(2, 1, 9, 1, 2)
-#define SYS_BRBTS_EL1 sys_reg(2, 1, 9, 0, 2)
-
-#define SYS_BRBCR_EL1 sys_reg(2, 1, 9, 0, 0)
-#define SYS_BRBFCR_EL1 sys_reg(2, 1, 9, 0, 1)
-#define SYS_BRBIDR0_EL1 sys_reg(2, 1, 9, 2, 0)
#define SYS_TRCITECR_EL1 sys_reg(3, 0, 1, 2, 3)
#define SYS_TRCACATR(m) sys_reg(2, 1, 2, ((m & 7) << 1), (2 | (m >> 3)))
@@ -270,8 +262,6 @@
/* ETM */
#define SYS_TRCOSLAR sys_reg(2, 1, 1, 0, 4)
-#define SYS_BRBCR_EL2 sys_reg(2, 4, 9, 0, 0)
-
#define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0)
#define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5)
#define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6)
@@ -601,7 +591,6 @@
#define SYS_CNTHV_CVAL_EL2 sys_reg(3, 4, 14, 3, 2)
/* VHE encodings for architectural EL0/1 system registers */
-#define SYS_BRBCR_EL12 sys_reg(2, 5, 9, 0, 0)
#define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)
#define SYS_CPACR_EL12 sys_reg(3, 5, 1, 0, 2)
#define SYS_SCTLR2_EL12 sys_reg(3, 5, 1, 0, 3)
@@ -794,6 +783,12 @@
#define OP_COSP_RCTX sys_insn(1, 3, 7, 3, 6)
#define OP_CPP_RCTX sys_insn(1, 3, 7, 3, 7)
+/*
+ * BRBE Instructions
+ */
+#define BRB_IALL_INSN __emit_inst(0xd5000000 | OP_BRB_IALL | (0x1f))
+#define BRB_INJ_INSN __emit_inst(0xd5000000 | OP_BRB_INJ | (0x1f))
+
/* Common SCTLR_ELx flags. */
#define SCTLR_ELx_ENTP2 (BIT(60))
#define SCTLR_ELx_DSSBS (BIT(44))
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index a4c1dd4741a4..c6d3390f39ee 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -1025,6 +1025,137 @@ UnsignedEnum 3:0 MTEPERM
EndEnum
EndSysreg
+
+SysregFields BRBINFx_EL1
+Res0 63:47
+Field 46 CCU
+Field 45:32 CC
+Res0 31:18
+Field 17 LASTFAILED
+Field 16 T
+Res0 15:14
+Enum 13:8 TYPE
+ 0b000000 DIRECT_UNCOND
+ 0b000001 INDIRECT
+ 0b000010 DIRECT_LINK
+ 0b000011 INDIRECT_LINK
+ 0b000101 RET
+ 0b000111 ERET
+ 0b001000 DIRECT_COND
+ 0b100001 DEBUG_HALT
+ 0b100010 CALL
+ 0b100011 TRAP
+ 0b100100 SERROR
+ 0b100110 INSN_DEBUG
+ 0b100111 DATA_DEBUG
+ 0b101010 ALIGN_FAULT
+ 0b101011 INSN_FAULT
+ 0b101100 DATA_FAULT
+ 0b101110 IRQ
+ 0b101111 FIQ
+ 0b110000 IMPDEF_TRAP_EL3
+ 0b111001 DEBUG_EXIT
+EndEnum
+Enum 7:6 EL
+ 0b00 EL0
+ 0b01 EL1
+ 0b10 EL2
+ 0b11 EL3
+EndEnum
+Field 5 MPRED
+Res0 4:2
+Enum 1:0 VALID
+ 0b00 NONE
+ 0b01 TARGET
+ 0b10 SOURCE
+ 0b11 FULL
+EndEnum
+EndSysregFields
+
+SysregFields BRBCR_ELx
+Res0 63:24
+Field 23 EXCEPTION
+Field 22 ERTN
+Res0 21:10
+Field 9 FZPSS
+Field 8 FZP
+Res0 7
+Enum 6:5 TS
+ 0b01 VIRTUAL
+ 0b10 GUEST_PHYSICAL
+ 0b11 PHYSICAL
+EndEnum
+Field 4 MPRED
+Field 3 CC
+Res0 2
+Field 1 ExBRE
+Field 0 E0BRE
+EndSysregFields
+
+Sysreg BRBCR_EL1 2 1 9 0 0
+Fields BRBCR_ELx
+EndSysreg
+
+Sysreg BRBFCR_EL1 2 1 9 0 1
+Res0 63:30
+Enum 29:28 BANK
+ 0b00 BANK_0
+ 0b01 BANK_1
+EndEnum
+Res0 27:23
+Field 22 CONDDIR
+Field 21 DIRCALL
+Field 20 INDCALL
+Field 19 RTN
+Field 18 INDIRECT
+Field 17 DIRECT
+Field 16 EnI
+Res0 15:8
+Field 7 PAUSED
+Field 6 LASTFAILED
+Res0 5:0
+EndSysreg
+
+Sysreg BRBTS_EL1 2 1 9 0 2
+Field 63:0 TS
+EndSysreg
+
+Sysreg BRBINFINJ_EL1 2 1 9 1 0
+Fields BRBINFx_EL1
+EndSysreg
+
+Sysreg BRBSRCINJ_EL1 2 1 9 1 1
+Field 63:0 ADDRESS
+EndSysreg
+
+Sysreg BRBTGTINJ_EL1 2 1 9 1 2
+Field 63:0 ADDRESS
+EndSysreg
+
+Sysreg BRBIDR0_EL1 2 1 9 2 0
+Res0 63:16
+Enum 15:12 CC
+ 0b0101 20_BIT
+EndEnum
+Enum 11:8 FORMAT
+ 0b0000 FORMAT_0
+EndEnum
+Enum 7:0 NUMREC
+ 0b00001000 8
+ 0b00010000 16
+ 0b00100000 32
+ 0b01000000 64
+EndEnum
+EndSysreg
+
+Sysreg BRBCR_EL2 2 4 9 0 0
+Fields BRBCR_ELx
+EndSysreg
+
+Sysreg BRBCR_EL12 2 5 9 0 0
+Fields BRBCR_ELx
+EndSysreg
+
Sysreg ID_AA64ZFR0_EL1 3 0 0 4 4
Res0 63:60
UnsignedEnum 59:56 F64MM
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH V18 1/9] arm64/sysreg: Add BRBE registers and fields
2024-06-13 6:17 ` [PATCH V18 1/9] arm64/sysreg: Add BRBE registers and fields Anshuman Khandual
@ 2024-06-13 10:10 ` Mark Rutland
0 siblings, 0 replies; 23+ messages in thread
From: Mark Rutland @ 2024-06-13 10:10 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users
On Thu, Jun 13, 2024 at 11:47:23AM +0530, Anshuman Khandual wrote:
> This patch adds definitions related to the Branch Record Buffer Extension
> (BRBE) as per ARM DDI 0487K.a. These will be used by KVM and a BRBE driver
> in subsequent patches.
>
> Some existing BRBE definitions in asm/sysreg.h are replaced with equivalent
> generated definitions.
>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Marc Zyngier <maz@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> Reviewed-by: Mark Brown <broonie@kernel.org>
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> ----
> Changes in V18:
>
> - Changed BRBIDR0_EL1 register fields CC and FORMAT, updated the commit message
Thanks, I see that matches my requsts on v17, so this looks good to me.
>
> arch/arm64/include/asm/sysreg.h | 17 ++---
> arch/arm64/tools/sysreg | 131 ++++++++++++++++++++++++++++++++
> 2 files changed, 137 insertions(+), 11 deletions(-)
> ---
> arch/arm64/include/asm/sysreg.h | 17 ++---
> arch/arm64/tools/sysreg | 131 ++++++++++++++++++++++++++++++++
> 2 files changed, 137 insertions(+), 11 deletions(-)
Something went wrong here to have this teice, but that doesn't affect
the actual patch.
Mark.
>
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index af3b206fa423..cb3c9c83dc7a 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -195,16 +195,8 @@
> #define SYS_DBGVCR32_EL2 sys_reg(2, 4, 0, 7, 0)
>
> #define SYS_BRBINF_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 0))
> -#define SYS_BRBINFINJ_EL1 sys_reg(2, 1, 9, 1, 0)
> #define SYS_BRBSRC_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 1))
> -#define SYS_BRBSRCINJ_EL1 sys_reg(2, 1, 9, 1, 1)
> #define SYS_BRBTGT_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 2))
> -#define SYS_BRBTGTINJ_EL1 sys_reg(2, 1, 9, 1, 2)
> -#define SYS_BRBTS_EL1 sys_reg(2, 1, 9, 0, 2)
> -
> -#define SYS_BRBCR_EL1 sys_reg(2, 1, 9, 0, 0)
> -#define SYS_BRBFCR_EL1 sys_reg(2, 1, 9, 0, 1)
> -#define SYS_BRBIDR0_EL1 sys_reg(2, 1, 9, 2, 0)
>
> #define SYS_TRCITECR_EL1 sys_reg(3, 0, 1, 2, 3)
> #define SYS_TRCACATR(m) sys_reg(2, 1, 2, ((m & 7) << 1), (2 | (m >> 3)))
> @@ -270,8 +262,6 @@
> /* ETM */
> #define SYS_TRCOSLAR sys_reg(2, 1, 1, 0, 4)
>
> -#define SYS_BRBCR_EL2 sys_reg(2, 4, 9, 0, 0)
> -
> #define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0)
> #define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5)
> #define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6)
> @@ -601,7 +591,6 @@
> #define SYS_CNTHV_CVAL_EL2 sys_reg(3, 4, 14, 3, 2)
>
> /* VHE encodings for architectural EL0/1 system registers */
> -#define SYS_BRBCR_EL12 sys_reg(2, 5, 9, 0, 0)
> #define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)
> #define SYS_CPACR_EL12 sys_reg(3, 5, 1, 0, 2)
> #define SYS_SCTLR2_EL12 sys_reg(3, 5, 1, 0, 3)
> @@ -794,6 +783,12 @@
> #define OP_COSP_RCTX sys_insn(1, 3, 7, 3, 6)
> #define OP_CPP_RCTX sys_insn(1, 3, 7, 3, 7)
>
> +/*
> + * BRBE Instructions
> + */
> +#define BRB_IALL_INSN __emit_inst(0xd5000000 | OP_BRB_IALL | (0x1f))
> +#define BRB_INJ_INSN __emit_inst(0xd5000000 | OP_BRB_INJ | (0x1f))
> +
> /* Common SCTLR_ELx flags. */
> #define SCTLR_ELx_ENTP2 (BIT(60))
> #define SCTLR_ELx_DSSBS (BIT(44))
> diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
> index a4c1dd4741a4..c6d3390f39ee 100644
> --- a/arch/arm64/tools/sysreg
> +++ b/arch/arm64/tools/sysreg
> @@ -1025,6 +1025,137 @@ UnsignedEnum 3:0 MTEPERM
> EndEnum
> EndSysreg
>
> +
> +SysregFields BRBINFx_EL1
> +Res0 63:47
> +Field 46 CCU
> +Field 45:32 CC
> +Res0 31:18
> +Field 17 LASTFAILED
> +Field 16 T
> +Res0 15:14
> +Enum 13:8 TYPE
> + 0b000000 DIRECT_UNCOND
> + 0b000001 INDIRECT
> + 0b000010 DIRECT_LINK
> + 0b000011 INDIRECT_LINK
> + 0b000101 RET
> + 0b000111 ERET
> + 0b001000 DIRECT_COND
> + 0b100001 DEBUG_HALT
> + 0b100010 CALL
> + 0b100011 TRAP
> + 0b100100 SERROR
> + 0b100110 INSN_DEBUG
> + 0b100111 DATA_DEBUG
> + 0b101010 ALIGN_FAULT
> + 0b101011 INSN_FAULT
> + 0b101100 DATA_FAULT
> + 0b101110 IRQ
> + 0b101111 FIQ
> + 0b110000 IMPDEF_TRAP_EL3
> + 0b111001 DEBUG_EXIT
> +EndEnum
> +Enum 7:6 EL
> + 0b00 EL0
> + 0b01 EL1
> + 0b10 EL2
> + 0b11 EL3
> +EndEnum
> +Field 5 MPRED
> +Res0 4:2
> +Enum 1:0 VALID
> + 0b00 NONE
> + 0b01 TARGET
> + 0b10 SOURCE
> + 0b11 FULL
> +EndEnum
> +EndSysregFields
> +
> +SysregFields BRBCR_ELx
> +Res0 63:24
> +Field 23 EXCEPTION
> +Field 22 ERTN
> +Res0 21:10
> +Field 9 FZPSS
> +Field 8 FZP
> +Res0 7
> +Enum 6:5 TS
> + 0b01 VIRTUAL
> + 0b10 GUEST_PHYSICAL
> + 0b11 PHYSICAL
> +EndEnum
> +Field 4 MPRED
> +Field 3 CC
> +Res0 2
> +Field 1 ExBRE
> +Field 0 E0BRE
> +EndSysregFields
> +
> +Sysreg BRBCR_EL1 2 1 9 0 0
> +Fields BRBCR_ELx
> +EndSysreg
> +
> +Sysreg BRBFCR_EL1 2 1 9 0 1
> +Res0 63:30
> +Enum 29:28 BANK
> + 0b00 BANK_0
> + 0b01 BANK_1
> +EndEnum
> +Res0 27:23
> +Field 22 CONDDIR
> +Field 21 DIRCALL
> +Field 20 INDCALL
> +Field 19 RTN
> +Field 18 INDIRECT
> +Field 17 DIRECT
> +Field 16 EnI
> +Res0 15:8
> +Field 7 PAUSED
> +Field 6 LASTFAILED
> +Res0 5:0
> +EndSysreg
> +
> +Sysreg BRBTS_EL1 2 1 9 0 2
> +Field 63:0 TS
> +EndSysreg
> +
> +Sysreg BRBINFINJ_EL1 2 1 9 1 0
> +Fields BRBINFx_EL1
> +EndSysreg
> +
> +Sysreg BRBSRCINJ_EL1 2 1 9 1 1
> +Field 63:0 ADDRESS
> +EndSysreg
> +
> +Sysreg BRBTGTINJ_EL1 2 1 9 1 2
> +Field 63:0 ADDRESS
> +EndSysreg
> +
> +Sysreg BRBIDR0_EL1 2 1 9 2 0
> +Res0 63:16
> +Enum 15:12 CC
> + 0b0101 20_BIT
> +EndEnum
> +Enum 11:8 FORMAT
> + 0b0000 FORMAT_0
> +EndEnum
> +Enum 7:0 NUMREC
> + 0b00001000 8
> + 0b00010000 16
> + 0b00100000 32
> + 0b01000000 64
> +EndEnum
> +EndSysreg
> +
> +Sysreg BRBCR_EL2 2 4 9 0 0
> +Fields BRBCR_ELx
> +EndSysreg
> +
> +Sysreg BRBCR_EL12 2 5 9 0 0
> +Fields BRBCR_ELx
> +EndSysreg
> +
> Sysreg ID_AA64ZFR0_EL1 3 0 0 4 4
> Res0 63:60
> UnsignedEnum 59:56 F64MM
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 1/9] arm64/sysreg: Add BRBE registers and fields Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-13 10:14 ` Mark Rutland
2024-06-14 12:33 ` Marc Zyngier
2024-06-13 6:17 ` [PATCH V18 3/9] drivers: perf: arm_pmu: Add infrastructure for branch stack sampling Anshuman Khandual
` (6 subsequent siblings)
8 siblings, 2 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Oliver Upton,
James Morse, kvmarm
The Branch Record Buffer Extension (BRBE) adds a number of system registers
and instructions, which we don't currently intend to expose to guests. Our
existing logic handles this safely, but this could be improved with some
explicit handling of BRBE.
The presence of BRBE is currently hidden from guests as the cpufeature
code's ftr_id_aa64dfr0[] table doesn't have an entry for the BRBE field,
and so this will be zero in the sanitised value of ID_AA64DFR0 exposed to
guests via read_sanitised_id_aa64dfr0_el1(). As the ftr_id_aa64dfr0[] table
may gain an entry for the BRBE field in future, for robustness we should
explicitly mask out the BRBE field in read_sanitised_id_aa64dfr0_el1().
The BRBE system registers and instructions are currently trapped by the
existing configuration of the fine-grained traps. As neither the registers
nor the instructions are described in the sys_reg_descs[] table,
emulate_sys_reg() will warn that these are unknown before injecting an
UNDEFINED exception into the guest.
Well-behaved guests shouldn't try to use the registers or instructions, but
badly-behaved guests could use these, resulting in unnecessary warnings. To
avoid those warnings, we should explicitly handle the BRBE registers and
instructions as UNDEFINED.
Address the above by having read_sanitised_id_aa64dfr0_el1() mask out the
ID_AA64DFR0.BRBE field, and explicitly handling all of the BRBE system
registers and instructions as UNDEFINED.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: James Morse <james.morse@arm.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: kvmarm@lists.linux.dev
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
----
Changes in V18:
- Updated the commit message
arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
---
arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 22b45a15d068..3d4686abe5ee 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1304,6 +1304,11 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
return 0;
}
+#define BRB_INF_SRC_TGT_EL1(n) \
+ { SYS_DESC(SYS_BRBINF_EL1(n)), undef_access }, \
+ { SYS_DESC(SYS_BRBSRC_EL1(n)), undef_access }, \
+ { SYS_DESC(SYS_BRBTGT_EL1(n)), undef_access } \
+
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
{ SYS_DESC(SYS_DBGBVRn_EL1(n)), \
@@ -1722,6 +1727,9 @@ static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
/* Hide SPE from guests */
val &= ~ID_AA64DFR0_EL1_PMSVer_MASK;
+ /* Hide BRBE from guests */
+ val &= ~ID_AA64DFR0_EL1_BRBE_MASK;
+
return val;
}
@@ -2240,6 +2248,52 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ SYS_DESC(SYS_DBGCLAIMCLR_EL1), trap_raz_wi },
{ SYS_DESC(SYS_DBGAUTHSTATUS_EL1), trap_dbgauthstatus_el1 },
+ /*
+ * BRBE branch record sysreg address space is interleaved between
+ * corresponding BRBINF<N>_EL1, BRBSRC<N>_EL1, and BRBTGT<N>_EL1.
+ */
+ BRB_INF_SRC_TGT_EL1(0),
+ BRB_INF_SRC_TGT_EL1(16),
+ BRB_INF_SRC_TGT_EL1(1),
+ BRB_INF_SRC_TGT_EL1(17),
+ BRB_INF_SRC_TGT_EL1(2),
+ BRB_INF_SRC_TGT_EL1(18),
+ BRB_INF_SRC_TGT_EL1(3),
+ BRB_INF_SRC_TGT_EL1(19),
+ BRB_INF_SRC_TGT_EL1(4),
+ BRB_INF_SRC_TGT_EL1(20),
+ BRB_INF_SRC_TGT_EL1(5),
+ BRB_INF_SRC_TGT_EL1(21),
+ BRB_INF_SRC_TGT_EL1(6),
+ BRB_INF_SRC_TGT_EL1(22),
+ BRB_INF_SRC_TGT_EL1(7),
+ BRB_INF_SRC_TGT_EL1(23),
+ BRB_INF_SRC_TGT_EL1(8),
+ BRB_INF_SRC_TGT_EL1(24),
+ BRB_INF_SRC_TGT_EL1(9),
+ BRB_INF_SRC_TGT_EL1(25),
+ BRB_INF_SRC_TGT_EL1(10),
+ BRB_INF_SRC_TGT_EL1(26),
+ BRB_INF_SRC_TGT_EL1(11),
+ BRB_INF_SRC_TGT_EL1(27),
+ BRB_INF_SRC_TGT_EL1(12),
+ BRB_INF_SRC_TGT_EL1(28),
+ BRB_INF_SRC_TGT_EL1(13),
+ BRB_INF_SRC_TGT_EL1(29),
+ BRB_INF_SRC_TGT_EL1(14),
+ BRB_INF_SRC_TGT_EL1(30),
+ BRB_INF_SRC_TGT_EL1(15),
+ BRB_INF_SRC_TGT_EL1(31),
+
+ /* Remaining BRBE sysreg addresses space */
+ { SYS_DESC(SYS_BRBCR_EL1), undef_access },
+ { SYS_DESC(SYS_BRBFCR_EL1), undef_access },
+ { SYS_DESC(SYS_BRBTS_EL1), undef_access },
+ { SYS_DESC(SYS_BRBINFINJ_EL1), undef_access },
+ { SYS_DESC(SYS_BRBSRCINJ_EL1), undef_access },
+ { SYS_DESC(SYS_BRBTGTINJ_EL1), undef_access },
+ { SYS_DESC(SYS_BRBIDR0_EL1), undef_access },
+
{ SYS_DESC(SYS_MDCCSR_EL0), trap_raz_wi },
{ SYS_DESC(SYS_DBGDTR_EL0), trap_raz_wi },
// DBGDTR[TR]X_EL0 share the same encoding
@@ -2751,6 +2805,8 @@ static struct sys_reg_desc sys_insn_descs[] = {
{ SYS_DESC(SYS_DC_CISW), access_dcsw },
{ SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
{ SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },
+ { SYS_DESC(OP_BRB_IALL), undef_access },
+ { SYS_DESC(OP_BRB_INJ), undef_access },
};
static const struct sys_reg_desc *first_idreg;
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED
2024-06-13 6:17 ` [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED Anshuman Khandual
@ 2024-06-13 10:14 ` Mark Rutland
2024-06-14 12:33 ` Marc Zyngier
1 sibling, 0 replies; 23+ messages in thread
From: Mark Rutland @ 2024-06-13 10:14 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users, Oliver Upton, James Morse, kvmarm
On Thu, Jun 13, 2024 at 11:47:24AM +0530, Anshuman Khandual wrote:
> The Branch Record Buffer Extension (BRBE) adds a number of system registers
> and instructions, which we don't currently intend to expose to guests. Our
> existing logic handles this safely, but this could be improved with some
> explicit handling of BRBE.
>
> The presence of BRBE is currently hidden from guests as the cpufeature
> code's ftr_id_aa64dfr0[] table doesn't have an entry for the BRBE field,
> and so this will be zero in the sanitised value of ID_AA64DFR0 exposed to
> guests via read_sanitised_id_aa64dfr0_el1(). As the ftr_id_aa64dfr0[] table
> may gain an entry for the BRBE field in future, for robustness we should
> explicitly mask out the BRBE field in read_sanitised_id_aa64dfr0_el1().
>
> The BRBE system registers and instructions are currently trapped by the
> existing configuration of the fine-grained traps. As neither the registers
> nor the instructions are described in the sys_reg_descs[] table,
> emulate_sys_reg() will warn that these are unknown before injecting an
> UNDEFINED exception into the guest.
>
> Well-behaved guests shouldn't try to use the registers or instructions, but
> badly-behaved guests could use these, resulting in unnecessary warnings. To
> avoid those warnings, we should explicitly handle the BRBE registers and
> instructions as UNDEFINED.
>
> Address the above by having read_sanitised_id_aa64dfr0_el1() mask out the
> ID_AA64DFR0.BRBE field, and explicitly handling all of the BRBE system
> registers and instructions as UNDEFINED.
>
> Cc: Marc Zyngier <maz@kernel.org>
> Cc: Oliver Upton <oliver.upton@linux.dev>
> Cc: James Morse <james.morse@arm.com>
> Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: kvmarm@lists.linux.dev
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> ----
> Changes in V18:
>
> - Updated the commit message
>
> arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> ---
> arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
Something has gone wrong here, but that doesn't affect the patch itself.
Mark.
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 22b45a15d068..3d4686abe5ee 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -1304,6 +1304,11 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
> return 0;
> }
>
> +#define BRB_INF_SRC_TGT_EL1(n) \
> + { SYS_DESC(SYS_BRBINF_EL1(n)), undef_access }, \
> + { SYS_DESC(SYS_BRBSRC_EL1(n)), undef_access }, \
> + { SYS_DESC(SYS_BRBTGT_EL1(n)), undef_access } \
> +
> /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
> #define DBG_BCR_BVR_WCR_WVR_EL1(n) \
> { SYS_DESC(SYS_DBGBVRn_EL1(n)), \
> @@ -1722,6 +1727,9 @@ static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> /* Hide SPE from guests */
> val &= ~ID_AA64DFR0_EL1_PMSVer_MASK;
>
> + /* Hide BRBE from guests */
> + val &= ~ID_AA64DFR0_EL1_BRBE_MASK;
> +
> return val;
> }
>
> @@ -2240,6 +2248,52 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> { SYS_DESC(SYS_DBGCLAIMCLR_EL1), trap_raz_wi },
> { SYS_DESC(SYS_DBGAUTHSTATUS_EL1), trap_dbgauthstatus_el1 },
>
> + /*
> + * BRBE branch record sysreg address space is interleaved between
> + * corresponding BRBINF<N>_EL1, BRBSRC<N>_EL1, and BRBTGT<N>_EL1.
> + */
> + BRB_INF_SRC_TGT_EL1(0),
> + BRB_INF_SRC_TGT_EL1(16),
> + BRB_INF_SRC_TGT_EL1(1),
> + BRB_INF_SRC_TGT_EL1(17),
> + BRB_INF_SRC_TGT_EL1(2),
> + BRB_INF_SRC_TGT_EL1(18),
> + BRB_INF_SRC_TGT_EL1(3),
> + BRB_INF_SRC_TGT_EL1(19),
> + BRB_INF_SRC_TGT_EL1(4),
> + BRB_INF_SRC_TGT_EL1(20),
> + BRB_INF_SRC_TGT_EL1(5),
> + BRB_INF_SRC_TGT_EL1(21),
> + BRB_INF_SRC_TGT_EL1(6),
> + BRB_INF_SRC_TGT_EL1(22),
> + BRB_INF_SRC_TGT_EL1(7),
> + BRB_INF_SRC_TGT_EL1(23),
> + BRB_INF_SRC_TGT_EL1(8),
> + BRB_INF_SRC_TGT_EL1(24),
> + BRB_INF_SRC_TGT_EL1(9),
> + BRB_INF_SRC_TGT_EL1(25),
> + BRB_INF_SRC_TGT_EL1(10),
> + BRB_INF_SRC_TGT_EL1(26),
> + BRB_INF_SRC_TGT_EL1(11),
> + BRB_INF_SRC_TGT_EL1(27),
> + BRB_INF_SRC_TGT_EL1(12),
> + BRB_INF_SRC_TGT_EL1(28),
> + BRB_INF_SRC_TGT_EL1(13),
> + BRB_INF_SRC_TGT_EL1(29),
> + BRB_INF_SRC_TGT_EL1(14),
> + BRB_INF_SRC_TGT_EL1(30),
> + BRB_INF_SRC_TGT_EL1(15),
> + BRB_INF_SRC_TGT_EL1(31),
> +
> + /* Remaining BRBE sysreg addresses space */
> + { SYS_DESC(SYS_BRBCR_EL1), undef_access },
> + { SYS_DESC(SYS_BRBFCR_EL1), undef_access },
> + { SYS_DESC(SYS_BRBTS_EL1), undef_access },
> + { SYS_DESC(SYS_BRBINFINJ_EL1), undef_access },
> + { SYS_DESC(SYS_BRBSRCINJ_EL1), undef_access },
> + { SYS_DESC(SYS_BRBTGTINJ_EL1), undef_access },
> + { SYS_DESC(SYS_BRBIDR0_EL1), undef_access },
> +
> { SYS_DESC(SYS_MDCCSR_EL0), trap_raz_wi },
> { SYS_DESC(SYS_DBGDTR_EL0), trap_raz_wi },
> // DBGDTR[TR]X_EL0 share the same encoding
> @@ -2751,6 +2805,8 @@ static struct sys_reg_desc sys_insn_descs[] = {
> { SYS_DESC(SYS_DC_CISW), access_dcsw },
> { SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
> { SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },
> + { SYS_DESC(OP_BRB_IALL), undef_access },
> + { SYS_DESC(OP_BRB_INJ), undef_access },
> };
>
> static const struct sys_reg_desc *first_idreg;
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED
2024-06-13 6:17 ` [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED Anshuman Khandual
2024-06-13 10:14 ` Mark Rutland
@ 2024-06-14 12:33 ` Marc Zyngier
2024-06-14 13:09 ` Marc Zyngier
1 sibling, 1 reply; 23+ messages in thread
From: Marc Zyngier @ 2024-06-14 12:33 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland, Mark Brown, James Clark, Rob Herring,
Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Oliver Upton,
James Morse, kvmarm
On Thu, 13 Jun 2024 07:17:24 +0100,
Anshuman Khandual <anshuman.khandual@arm.com> wrote:
>
> The Branch Record Buffer Extension (BRBE) adds a number of system registers
> and instructions, which we don't currently intend to expose to guests. Our
> existing logic handles this safely, but this could be improved with some
> explicit handling of BRBE.
>
> The presence of BRBE is currently hidden from guests as the cpufeature
> code's ftr_id_aa64dfr0[] table doesn't have an entry for the BRBE field,
> and so this will be zero in the sanitised value of ID_AA64DFR0 exposed to
> guests via read_sanitised_id_aa64dfr0_el1(). As the ftr_id_aa64dfr0[] table
> may gain an entry for the BRBE field in future, for robustness we should
> explicitly mask out the BRBE field in read_sanitised_id_aa64dfr0_el1().
>
> The BRBE system registers and instructions are currently trapped by the
> existing configuration of the fine-grained traps. As neither the registers
> nor the instructions are described in the sys_reg_descs[] table,
> emulate_sys_reg() will warn that these are unknown before injecting an
> UNDEFINED exception into the guest.
>
> Well-behaved guests shouldn't try to use the registers or instructions, but
> badly-behaved guests could use these, resulting in unnecessary warnings. To
> avoid those warnings, we should explicitly handle the BRBE registers and
> instructions as UNDEFINED.
>
> Address the above by having read_sanitised_id_aa64dfr0_el1() mask out the
> ID_AA64DFR0.BRBE field, and explicitly handling all of the BRBE system
> registers and instructions as UNDEFINED.
>
> Cc: Marc Zyngier <maz@kernel.org>
> Cc: Oliver Upton <oliver.upton@linux.dev>
> Cc: James Morse <james.morse@arm.com>
> Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: kvmarm@lists.linux.dev
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> ----
> Changes in V18:
>
> - Updated the commit message
>
> arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> ---
> arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 22b45a15d068..3d4686abe5ee 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -1304,6 +1304,11 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
> return 0;
> }
>
> +#define BRB_INF_SRC_TGT_EL1(n) \
> + { SYS_DESC(SYS_BRBINF_EL1(n)), undef_access }, \
> + { SYS_DESC(SYS_BRBSRC_EL1(n)), undef_access }, \
> + { SYS_DESC(SYS_BRBTGT_EL1(n)), undef_access } \
> +
> /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
> #define DBG_BCR_BVR_WCR_WVR_EL1(n) \
> { SYS_DESC(SYS_DBGBVRn_EL1(n)), \
> @@ -1722,6 +1727,9 @@ static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> /* Hide SPE from guests */
> val &= ~ID_AA64DFR0_EL1_PMSVer_MASK;
>
> + /* Hide BRBE from guests */
> + val &= ~ID_AA64DFR0_EL1_BRBE_MASK;
> +
> return val;
> }
>
> @@ -2240,6 +2248,52 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> { SYS_DESC(SYS_DBGCLAIMCLR_EL1), trap_raz_wi },
> { SYS_DESC(SYS_DBGAUTHSTATUS_EL1), trap_dbgauthstatus_el1 },
>
> + /*
> + * BRBE branch record sysreg address space is interleaved between
> + * corresponding BRBINF<N>_EL1, BRBSRC<N>_EL1, and BRBTGT<N>_EL1.
> + */
> + BRB_INF_SRC_TGT_EL1(0),
> + BRB_INF_SRC_TGT_EL1(16),
> + BRB_INF_SRC_TGT_EL1(1),
> + BRB_INF_SRC_TGT_EL1(17),
> + BRB_INF_SRC_TGT_EL1(2),
> + BRB_INF_SRC_TGT_EL1(18),
> + BRB_INF_SRC_TGT_EL1(3),
> + BRB_INF_SRC_TGT_EL1(19),
> + BRB_INF_SRC_TGT_EL1(4),
> + BRB_INF_SRC_TGT_EL1(20),
> + BRB_INF_SRC_TGT_EL1(5),
> + BRB_INF_SRC_TGT_EL1(21),
> + BRB_INF_SRC_TGT_EL1(6),
> + BRB_INF_SRC_TGT_EL1(22),
> + BRB_INF_SRC_TGT_EL1(7),
> + BRB_INF_SRC_TGT_EL1(23),
> + BRB_INF_SRC_TGT_EL1(8),
> + BRB_INF_SRC_TGT_EL1(24),
> + BRB_INF_SRC_TGT_EL1(9),
> + BRB_INF_SRC_TGT_EL1(25),
> + BRB_INF_SRC_TGT_EL1(10),
> + BRB_INF_SRC_TGT_EL1(26),
> + BRB_INF_SRC_TGT_EL1(11),
> + BRB_INF_SRC_TGT_EL1(27),
> + BRB_INF_SRC_TGT_EL1(12),
> + BRB_INF_SRC_TGT_EL1(28),
> + BRB_INF_SRC_TGT_EL1(13),
> + BRB_INF_SRC_TGT_EL1(29),
> + BRB_INF_SRC_TGT_EL1(14),
> + BRB_INF_SRC_TGT_EL1(30),
> + BRB_INF_SRC_TGT_EL1(15),
> + BRB_INF_SRC_TGT_EL1(31),
> +
> + /* Remaining BRBE sysreg addresses space */
> + { SYS_DESC(SYS_BRBCR_EL1), undef_access },
> + { SYS_DESC(SYS_BRBFCR_EL1), undef_access },
> + { SYS_DESC(SYS_BRBTS_EL1), undef_access },
> + { SYS_DESC(SYS_BRBINFINJ_EL1), undef_access },
> + { SYS_DESC(SYS_BRBSRCINJ_EL1), undef_access },
> + { SYS_DESC(SYS_BRBTGTINJ_EL1), undef_access },
> + { SYS_DESC(SYS_BRBIDR0_EL1), undef_access },
> +
> { SYS_DESC(SYS_MDCCSR_EL0), trap_raz_wi },
> { SYS_DESC(SYS_DBGDTR_EL0), trap_raz_wi },
> // DBGDTR[TR]X_EL0 share the same encoding
> @@ -2751,6 +2805,8 @@ static struct sys_reg_desc sys_insn_descs[] = {
> { SYS_DESC(SYS_DC_CISW), access_dcsw },
> { SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
> { SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },
> + { SYS_DESC(OP_BRB_IALL), undef_access },
> + { SYS_DESC(OP_BRB_INJ), undef_access },
> };
>
> static const struct sys_reg_desc *first_idreg;
I don't think we need any update to the sys_reg table to handle
this. Instead, we should make use of the FGU infrastructure that has
been in since 6.9 to make this stuff UNDEF unconditionally.
It should be as simple as:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ee33f5467ce5..7cafe3f72c01 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -4964,6 +4964,11 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
kvm->arch.fgu[HAFGRTR_GROUP] |= ~(HAFGRTR_EL2_RES0 |
HAFGRTR_EL2_RES1);
+ if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP))
+ kvm->arch.fgu[HDFGRTR_GROUP] |= (HDFGRTR_nBRBDATA |
+ HDFGRTR_nBRBCTL |
+ HDFGRTR_nBRBIDR);
+
set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
out:
mutex_unlock(&kvm->arch.config_lock);
which is of course untested, but that I expect to be correct.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED
2024-06-14 12:33 ` Marc Zyngier
@ 2024-06-14 13:09 ` Marc Zyngier
2024-06-17 6:27 ` Anshuman Khandual
0 siblings, 1 reply; 23+ messages in thread
From: Marc Zyngier @ 2024-06-14 13:09 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland, Mark Brown, James Clark, Rob Herring,
Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Oliver Upton,
James Morse, kvmarm
On Fri, 14 Jun 2024 13:33:37 +0100,
Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 13 Jun 2024 07:17:24 +0100,
> Anshuman Khandual <anshuman.khandual@arm.com> wrote:
> >
> > The Branch Record Buffer Extension (BRBE) adds a number of system registers
> > and instructions, which we don't currently intend to expose to guests. Our
> > existing logic handles this safely, but this could be improved with some
> > explicit handling of BRBE.
> >
> > The presence of BRBE is currently hidden from guests as the cpufeature
> > code's ftr_id_aa64dfr0[] table doesn't have an entry for the BRBE field,
> > and so this will be zero in the sanitised value of ID_AA64DFR0 exposed to
> > guests via read_sanitised_id_aa64dfr0_el1(). As the ftr_id_aa64dfr0[] table
> > may gain an entry for the BRBE field in future, for robustness we should
> > explicitly mask out the BRBE field in read_sanitised_id_aa64dfr0_el1().
> >
> > The BRBE system registers and instructions are currently trapped by the
> > existing configuration of the fine-grained traps. As neither the registers
> > nor the instructions are described in the sys_reg_descs[] table,
> > emulate_sys_reg() will warn that these are unknown before injecting an
> > UNDEFINED exception into the guest.
> >
> > Well-behaved guests shouldn't try to use the registers or instructions, but
> > badly-behaved guests could use these, resulting in unnecessary warnings. To
> > avoid those warnings, we should explicitly handle the BRBE registers and
> > instructions as UNDEFINED.
> >
> > Address the above by having read_sanitised_id_aa64dfr0_el1() mask out the
> > ID_AA64DFR0.BRBE field, and explicitly handling all of the BRBE system
> > registers and instructions as UNDEFINED.
> >
> > Cc: Marc Zyngier <maz@kernel.org>
> > Cc: Oliver Upton <oliver.upton@linux.dev>
> > Cc: James Morse <james.morse@arm.com>
> > Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
> > Cc: Catalin Marinas <catalin.marinas@arm.com>
> > Cc: Will Deacon <will@kernel.org>
> > Cc: kvmarm@lists.linux.dev
> > Cc: linux-arm-kernel@lists.infradead.org
> > Cc: linux-kernel@vger.kernel.org
> > Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> > ----
> > Changes in V18:
> >
> > - Updated the commit message
> >
> > arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 56 insertions(+)
> > Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> > ---
> > arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 56 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index 22b45a15d068..3d4686abe5ee 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -1304,6 +1304,11 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
> > return 0;
> > }
> >
> > +#define BRB_INF_SRC_TGT_EL1(n) \
> > + { SYS_DESC(SYS_BRBINF_EL1(n)), undef_access }, \
> > + { SYS_DESC(SYS_BRBSRC_EL1(n)), undef_access }, \
> > + { SYS_DESC(SYS_BRBTGT_EL1(n)), undef_access } \
> > +
> > /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
> > #define DBG_BCR_BVR_WCR_WVR_EL1(n) \
> > { SYS_DESC(SYS_DBGBVRn_EL1(n)), \
> > @@ -1722,6 +1727,9 @@ static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
> > /* Hide SPE from guests */
> > val &= ~ID_AA64DFR0_EL1_PMSVer_MASK;
> >
> > + /* Hide BRBE from guests */
> > + val &= ~ID_AA64DFR0_EL1_BRBE_MASK;
> > +
> > return val;
> > }
> >
> > @@ -2240,6 +2248,52 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> > { SYS_DESC(SYS_DBGCLAIMCLR_EL1), trap_raz_wi },
> > { SYS_DESC(SYS_DBGAUTHSTATUS_EL1), trap_dbgauthstatus_el1 },
> >
> > + /*
> > + * BRBE branch record sysreg address space is interleaved between
> > + * corresponding BRBINF<N>_EL1, BRBSRC<N>_EL1, and BRBTGT<N>_EL1.
> > + */
> > + BRB_INF_SRC_TGT_EL1(0),
> > + BRB_INF_SRC_TGT_EL1(16),
> > + BRB_INF_SRC_TGT_EL1(1),
> > + BRB_INF_SRC_TGT_EL1(17),
> > + BRB_INF_SRC_TGT_EL1(2),
> > + BRB_INF_SRC_TGT_EL1(18),
> > + BRB_INF_SRC_TGT_EL1(3),
> > + BRB_INF_SRC_TGT_EL1(19),
> > + BRB_INF_SRC_TGT_EL1(4),
> > + BRB_INF_SRC_TGT_EL1(20),
> > + BRB_INF_SRC_TGT_EL1(5),
> > + BRB_INF_SRC_TGT_EL1(21),
> > + BRB_INF_SRC_TGT_EL1(6),
> > + BRB_INF_SRC_TGT_EL1(22),
> > + BRB_INF_SRC_TGT_EL1(7),
> > + BRB_INF_SRC_TGT_EL1(23),
> > + BRB_INF_SRC_TGT_EL1(8),
> > + BRB_INF_SRC_TGT_EL1(24),
> > + BRB_INF_SRC_TGT_EL1(9),
> > + BRB_INF_SRC_TGT_EL1(25),
> > + BRB_INF_SRC_TGT_EL1(10),
> > + BRB_INF_SRC_TGT_EL1(26),
> > + BRB_INF_SRC_TGT_EL1(11),
> > + BRB_INF_SRC_TGT_EL1(27),
> > + BRB_INF_SRC_TGT_EL1(12),
> > + BRB_INF_SRC_TGT_EL1(28),
> > + BRB_INF_SRC_TGT_EL1(13),
> > + BRB_INF_SRC_TGT_EL1(29),
> > + BRB_INF_SRC_TGT_EL1(14),
> > + BRB_INF_SRC_TGT_EL1(30),
> > + BRB_INF_SRC_TGT_EL1(15),
> > + BRB_INF_SRC_TGT_EL1(31),
> > +
> > + /* Remaining BRBE sysreg addresses space */
> > + { SYS_DESC(SYS_BRBCR_EL1), undef_access },
> > + { SYS_DESC(SYS_BRBFCR_EL1), undef_access },
> > + { SYS_DESC(SYS_BRBTS_EL1), undef_access },
> > + { SYS_DESC(SYS_BRBINFINJ_EL1), undef_access },
> > + { SYS_DESC(SYS_BRBSRCINJ_EL1), undef_access },
> > + { SYS_DESC(SYS_BRBTGTINJ_EL1), undef_access },
> > + { SYS_DESC(SYS_BRBIDR0_EL1), undef_access },
> > +
> > { SYS_DESC(SYS_MDCCSR_EL0), trap_raz_wi },
> > { SYS_DESC(SYS_DBGDTR_EL0), trap_raz_wi },
> > // DBGDTR[TR]X_EL0 share the same encoding
> > @@ -2751,6 +2805,8 @@ static struct sys_reg_desc sys_insn_descs[] = {
> > { SYS_DESC(SYS_DC_CISW), access_dcsw },
> > { SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
> > { SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },
> > + { SYS_DESC(OP_BRB_IALL), undef_access },
> > + { SYS_DESC(OP_BRB_INJ), undef_access },
> > };
> >
> > static const struct sys_reg_desc *first_idreg;
>
> I don't think we need any update to the sys_reg table to handle
> this. Instead, we should make use of the FGU infrastructure that has
> been in since 6.9 to make this stuff UNDEF unconditionally.
>
> It should be as simple as:
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index ee33f5467ce5..7cafe3f72c01 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -4964,6 +4964,11 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
> kvm->arch.fgu[HAFGRTR_GROUP] |= ~(HAFGRTR_EL2_RES0 |
> HAFGRTR_EL2_RES1);
>
> + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP))
> + kvm->arch.fgu[HDFGRTR_GROUP] |= (HDFGRTR_nBRBDATA |
> + HDFGRTR_nBRBCTL |
> + HDFGRTR_nBRBIDR);
> +
> set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
> out:
> mutex_unlock(&kvm->arch.config_lock);
>
> which is of course untested, but that I expect to be correct.
Actually, to disable the *instructions*, a similar hack must be
applied to HFGITR_EL2. The resulting patch should be something like:
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ee33f5467ce5..49d86dae8d80 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -4964,6 +4964,15 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
kvm->arch.fgu[HAFGRTR_GROUP] |= ~(HAFGRTR_EL2_RES0 |
HAFGRTR_EL2_RES1);
+ if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP)) {
+ kvm->arch.fgu[HDFGRTR_GROUP] |= (HDFGRTR_nBRBDATA |
+ HDFGRTR_nBRBCTL |
+ HDFGRTR_nBRBIDR);
+ kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_nBRBINJ |
+ HFGITR_EL2_nBRBIALL);
+ }
+
+
set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
out:
mutex_unlock(&kvm->arch.config_lock);
The implicit dependency here is that FGT is always present on a system
that implements BRBE. The architecture supports this assertion:
- BRBE is not available before ARMv9.1
- FGT is mandatory from ARMv8.6
Given that v9.1 is congruent to v8.6, we have the required overlap.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED
2024-06-14 13:09 ` Marc Zyngier
@ 2024-06-17 6:27 ` Anshuman Khandual
2024-06-17 7:41 ` Marc Zyngier
0 siblings, 1 reply; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-17 6:27 UTC (permalink / raw)
To: Marc Zyngier
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland, Mark Brown, James Clark, Rob Herring,
Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Oliver Upton,
James Morse, kvmarm
On 6/14/24 18:39, Marc Zyngier wrote:
> On Fri, 14 Jun 2024 13:33:37 +0100,
> Marc Zyngier <maz@kernel.org> wrote:
>>
>> On Thu, 13 Jun 2024 07:17:24 +0100,
>> Anshuman Khandual <anshuman.khandual@arm.com> wrote:
>>>
>>> The Branch Record Buffer Extension (BRBE) adds a number of system registers
>>> and instructions, which we don't currently intend to expose to guests. Our
>>> existing logic handles this safely, but this could be improved with some
>>> explicit handling of BRBE.
>>>
>>> The presence of BRBE is currently hidden from guests as the cpufeature
>>> code's ftr_id_aa64dfr0[] table doesn't have an entry for the BRBE field,
>>> and so this will be zero in the sanitised value of ID_AA64DFR0 exposed to
>>> guests via read_sanitised_id_aa64dfr0_el1(). As the ftr_id_aa64dfr0[] table
>>> may gain an entry for the BRBE field in future, for robustness we should
>>> explicitly mask out the BRBE field in read_sanitised_id_aa64dfr0_el1().
>>>
>>> The BRBE system registers and instructions are currently trapped by the
>>> existing configuration of the fine-grained traps. As neither the registers
>>> nor the instructions are described in the sys_reg_descs[] table,
>>> emulate_sys_reg() will warn that these are unknown before injecting an
>>> UNDEFINED exception into the guest.
>>>
>>> Well-behaved guests shouldn't try to use the registers or instructions, but
>>> badly-behaved guests could use these, resulting in unnecessary warnings. To
>>> avoid those warnings, we should explicitly handle the BRBE registers and
>>> instructions as UNDEFINED.
>>>
>>> Address the above by having read_sanitised_id_aa64dfr0_el1() mask out the
>>> ID_AA64DFR0.BRBE field, and explicitly handling all of the BRBE system
>>> registers and instructions as UNDEFINED.
>>>
>>> Cc: Marc Zyngier <maz@kernel.org>
>>> Cc: Oliver Upton <oliver.upton@linux.dev>
>>> Cc: James Morse <james.morse@arm.com>
>>> Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>>> Cc: Will Deacon <will@kernel.org>
>>> Cc: kvmarm@lists.linux.dev
>>> Cc: linux-arm-kernel@lists.infradead.org
>>> Cc: linux-kernel@vger.kernel.org
>>> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
>>> ----
>>> Changes in V18:
>>>
>>> - Updated the commit message
>>>
>>> arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 56 insertions(+)
>>> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
>>> ---
>>> arch/arm64/kvm/sys_regs.c | 56 +++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 56 insertions(+)
>>>
>>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>>> index 22b45a15d068..3d4686abe5ee 100644
>>> --- a/arch/arm64/kvm/sys_regs.c
>>> +++ b/arch/arm64/kvm/sys_regs.c
>>> @@ -1304,6 +1304,11 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
>>> return 0;
>>> }
>>>
>>> +#define BRB_INF_SRC_TGT_EL1(n) \
>>> + { SYS_DESC(SYS_BRBINF_EL1(n)), undef_access }, \
>>> + { SYS_DESC(SYS_BRBSRC_EL1(n)), undef_access }, \
>>> + { SYS_DESC(SYS_BRBTGT_EL1(n)), undef_access } \
>>> +
>>> /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
>>> #define DBG_BCR_BVR_WCR_WVR_EL1(n) \
>>> { SYS_DESC(SYS_DBGBVRn_EL1(n)), \
>>> @@ -1722,6 +1727,9 @@ static u64 read_sanitised_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
>>> /* Hide SPE from guests */
>>> val &= ~ID_AA64DFR0_EL1_PMSVer_MASK;
>>>
>>> + /* Hide BRBE from guests */
>>> + val &= ~ID_AA64DFR0_EL1_BRBE_MASK;
>>> +
>>> return val;
>>> }
>>>
>>> @@ -2240,6 +2248,52 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>>> { SYS_DESC(SYS_DBGCLAIMCLR_EL1), trap_raz_wi },
>>> { SYS_DESC(SYS_DBGAUTHSTATUS_EL1), trap_dbgauthstatus_el1 },
>>>
>>> + /*
>>> + * BRBE branch record sysreg address space is interleaved between
>>> + * corresponding BRBINF<N>_EL1, BRBSRC<N>_EL1, and BRBTGT<N>_EL1.
>>> + */
>>> + BRB_INF_SRC_TGT_EL1(0),
>>> + BRB_INF_SRC_TGT_EL1(16),
>>> + BRB_INF_SRC_TGT_EL1(1),
>>> + BRB_INF_SRC_TGT_EL1(17),
>>> + BRB_INF_SRC_TGT_EL1(2),
>>> + BRB_INF_SRC_TGT_EL1(18),
>>> + BRB_INF_SRC_TGT_EL1(3),
>>> + BRB_INF_SRC_TGT_EL1(19),
>>> + BRB_INF_SRC_TGT_EL1(4),
>>> + BRB_INF_SRC_TGT_EL1(20),
>>> + BRB_INF_SRC_TGT_EL1(5),
>>> + BRB_INF_SRC_TGT_EL1(21),
>>> + BRB_INF_SRC_TGT_EL1(6),
>>> + BRB_INF_SRC_TGT_EL1(22),
>>> + BRB_INF_SRC_TGT_EL1(7),
>>> + BRB_INF_SRC_TGT_EL1(23),
>>> + BRB_INF_SRC_TGT_EL1(8),
>>> + BRB_INF_SRC_TGT_EL1(24),
>>> + BRB_INF_SRC_TGT_EL1(9),
>>> + BRB_INF_SRC_TGT_EL1(25),
>>> + BRB_INF_SRC_TGT_EL1(10),
>>> + BRB_INF_SRC_TGT_EL1(26),
>>> + BRB_INF_SRC_TGT_EL1(11),
>>> + BRB_INF_SRC_TGT_EL1(27),
>>> + BRB_INF_SRC_TGT_EL1(12),
>>> + BRB_INF_SRC_TGT_EL1(28),
>>> + BRB_INF_SRC_TGT_EL1(13),
>>> + BRB_INF_SRC_TGT_EL1(29),
>>> + BRB_INF_SRC_TGT_EL1(14),
>>> + BRB_INF_SRC_TGT_EL1(30),
>>> + BRB_INF_SRC_TGT_EL1(15),
>>> + BRB_INF_SRC_TGT_EL1(31),
>>> +
>>> + /* Remaining BRBE sysreg addresses space */
>>> + { SYS_DESC(SYS_BRBCR_EL1), undef_access },
>>> + { SYS_DESC(SYS_BRBFCR_EL1), undef_access },
>>> + { SYS_DESC(SYS_BRBTS_EL1), undef_access },
>>> + { SYS_DESC(SYS_BRBINFINJ_EL1), undef_access },
>>> + { SYS_DESC(SYS_BRBSRCINJ_EL1), undef_access },
>>> + { SYS_DESC(SYS_BRBTGTINJ_EL1), undef_access },
>>> + { SYS_DESC(SYS_BRBIDR0_EL1), undef_access },
>>> +
>>> { SYS_DESC(SYS_MDCCSR_EL0), trap_raz_wi },
>>> { SYS_DESC(SYS_DBGDTR_EL0), trap_raz_wi },
>>> // DBGDTR[TR]X_EL0 share the same encoding
>>> @@ -2751,6 +2805,8 @@ static struct sys_reg_desc sys_insn_descs[] = {
>>> { SYS_DESC(SYS_DC_CISW), access_dcsw },
>>> { SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
>>> { SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },
>>> + { SYS_DESC(OP_BRB_IALL), undef_access },
>>> + { SYS_DESC(OP_BRB_INJ), undef_access },
>>> };
>>>
>>> static const struct sys_reg_desc *first_idreg;
>>
>> I don't think we need any update to the sys_reg table to handle
>> this. Instead, we should make use of the FGU infrastructure that has
>> been in since 6.9 to make this stuff UNDEF unconditionally.
>>
>> It should be as simple as:
>>
>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>> index ee33f5467ce5..7cafe3f72c01 100644
>> --- a/arch/arm64/kvm/sys_regs.c
>> +++ b/arch/arm64/kvm/sys_regs.c
>> @@ -4964,6 +4964,11 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
>> kvm->arch.fgu[HAFGRTR_GROUP] |= ~(HAFGRTR_EL2_RES0 |
>> HAFGRTR_EL2_RES1);
>>
>> + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP))
>> + kvm->arch.fgu[HDFGRTR_GROUP] |= (HDFGRTR_nBRBDATA |
>> + HDFGRTR_nBRBCTL |
>> + HDFGRTR_nBRBIDR);
>> +
>> set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
>> out:
>> mutex_unlock(&kvm->arch.config_lock);
>>
>> which is of course untested, but that I expect to be correct.
>
> Actually, to disable the *instructions*, a similar hack must be
> applied to HFGITR_EL2. The resulting patch should be something like:
>
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index ee33f5467ce5..49d86dae8d80 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -4964,6 +4964,15 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
> kvm->arch.fgu[HAFGRTR_GROUP] |= ~(HAFGRTR_EL2_RES0 |
> HAFGRTR_EL2_RES1);
>
> + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP)) {
> + kvm->arch.fgu[HDFGRTR_GROUP] |= (HDFGRTR_nBRBDATA |
> + HDFGRTR_nBRBCTL |
> + HDFGRTR_nBRBIDR);
> + kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_nBRBINJ |
> + HFGITR_EL2_nBRBIALL);
> + }
> +
> +
> set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
> out:
> mutex_unlock(&kvm->arch.config_lock);
This makes sense, will remove all the changes to sys_reg table and
instead fold the above suggestion into the patch.
>
> The implicit dependency here is that FGT is always present on a system
> that implements BRBE. The architecture supports this assertion:
>
> - BRBE is not available before ARMv9.1
> - FGT is mandatory from ARMv8.6
>
> Given that v9.1 is congruent to v8.6, we have the required overlap.
So this overlap need not be asserted in software again ?
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED
2024-06-17 6:27 ` Anshuman Khandual
@ 2024-06-17 7:41 ` Marc Zyngier
0 siblings, 0 replies; 23+ messages in thread
From: Marc Zyngier @ 2024-06-17 7:41 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland, Mark Brown, James Clark, Rob Herring,
Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Oliver Upton,
James Morse, kvmarm
On Mon, 17 Jun 2024 07:27:13 +0100,
Anshuman Khandual <anshuman.khandual@arm.com> wrote:
> On 6/14/24 18:39, Marc Zyngier wrote:
> > On Fri, 14 Jun 2024 13:33:37 +0100,
> >
> > Actually, to disable the *instructions*, a similar hack must be
> > applied to HFGITR_EL2. The resulting patch should be something like:
> >
> > diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> > index ee33f5467ce5..49d86dae8d80 100644
> > --- a/arch/arm64/kvm/sys_regs.c
> > +++ b/arch/arm64/kvm/sys_regs.c
> > @@ -4964,6 +4964,15 @@ void kvm_init_sysreg(struct kvm_vcpu *vcpu)
> > kvm->arch.fgu[HAFGRTR_GROUP] |= ~(HAFGRTR_EL2_RES0 |
> > HAFGRTR_EL2_RES1);
> >
> > + if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, BRBE, IMP)) {
> > + kvm->arch.fgu[HDFGRTR_GROUP] |= (HDFGRTR_nBRBDATA |
> > + HDFGRTR_nBRBCTL |
> > + HDFGRTR_nBRBIDR);
Obviously, this needs to be spelled HDFGRTR_EL2_nBRB* so that it
actually compiles.
> > + kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_nBRBINJ |
> > + HFGITR_EL2_nBRBIALL);
> > + }
> > +
> > +
> > set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags);
> > out:
> > mutex_unlock(&kvm->arch.config_lock);
>
> This makes sense, will remove all the changes to sys_reg table and
> instead fold the above suggestion into the patch.
>
> >
> > The implicit dependency here is that FGT is always present on a system
> > that implements BRBE. The architecture supports this assertion:
> >
> > - BRBE is not available before ARMv9.1
> > - FGT is mandatory from ARMv8.6
> >
> > Given that v9.1 is congruent to v8.6, we have the required overlap.
>
> So this overlap need not be asserted in software again ?
I don't think there's a need for that.
We went through the same thing with SME (which has the exact same
dependency), and concluded that there was no need to paper over broken
implementations at the moment (only QEMU was affected, and that was
quickly fixed). If we find an implementation in the wild that didn't
get the memo, we can add a workaround at that time.
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V18 3/9] drivers: perf: arm_pmu: Add infrastructure for branch stack sampling
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 1/9] arm64/sysreg: Add BRBE registers and fields Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 2/9] KVM: arm64: Explicitly handle BRBE traps as UNDEFINED Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-14 15:01 ` Mark Rutland
2024-06-13 6:17 ` [PATCH V18 4/9] arm64/boot: Enable EL2 requirements for BRBE Anshuman Khandual
` (5 subsequent siblings)
8 siblings, 1 reply; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users
In order to support the Branch Record Buffer Extension (BRBE), we need to
extend the arm_pmu framework with some basic infrastructure for branch
stack sampling which arm_pmu drivers can opt-in to using. Subsequent
patches will use this to add support for BRBE in the PMUv3 driver.
With BRBE, the hardware records branches into a hardware FIFO, which will
be sampled by software when perf events overflow. A task may be context-
switched an arbitrary number of times between overflows, and to avoid
losing samples we need to save the current records when a task is context-
switched out. To do these we'll need to use the pmu::sched_task() callback,
and we'll also need to allocate some per-task storage space via event flag
PERF_ATTACH_TASK_DATA.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
----
Changes in V18:
- Scan valid branch stack events in armpmu_start() to create merged filter
- Updated the commit message
drivers/perf/arm_pmu.c | 42 +++++++++++++++++++++++++++++++++---
include/linux/perf/arm_pmu.h | 32 ++++++++++++++++++++++++++-
2 files changed, 70 insertions(+), 4 deletions(-)
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 8458fe2cebb4..219c1e276327 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -289,6 +289,23 @@ static void armpmu_start(struct perf_event *event, int flags)
{
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
+ struct pmu_hw_events *cpuc = this_cpu_ptr(armpmu->hw_events);
+ int idx;
+
+ /*
+ * Merge all branch filter requests from different perf
+ * events being added into this PMU. This includes both
+ * privilege and branch type filters.
+ */
+ if (armpmu->has_branch_stack) {
+ cpuc->branch_sample_type = 0;
+ for (idx = 0; idx < ARMPMU_MAX_HWEVENTS; idx++) {
+ struct perf_event *event_idx = cpuc->events[idx];
+
+ if (event_idx && has_branch_stack(event_idx))
+ cpuc->branch_sample_type |= event_idx->attr.branch_sample_type;
+ }
+ }
/*
* ARM pmu always has to reprogram the period, so ignore
@@ -317,6 +334,9 @@ armpmu_del(struct perf_event *event, int flags)
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
+ if (has_branch_stack(event))
+ armpmu->branch_stack_del(event, hw_events);
+
armpmu_stop(event, PERF_EF_UPDATE);
hw_events->events[idx] = NULL;
armpmu->clear_event_idx(hw_events, event);
@@ -342,6 +362,9 @@ armpmu_add(struct perf_event *event, int flags)
if (idx < 0)
return idx;
+ if (has_branch_stack(event))
+ armpmu->branch_stack_add(event, hw_events);
+
/*
* If there is an event in the counter we are going to use then make
* sure it is disabled.
@@ -511,13 +534,25 @@ static int armpmu_event_init(struct perf_event *event)
!cpumask_test_cpu(event->cpu, &armpmu->supported_cpus))
return -ENOENT;
- /* does not support taken branch sampling */
- if (has_branch_stack(event))
- return -EOPNOTSUPP;
+ if (has_branch_stack(event)) {
+ if (!armpmu->has_branch_stack)
+ return -EOPNOTSUPP;
+
+ if (!armpmu->branch_stack_init(event))
+ return -EOPNOTSUPP;
+ }
return __hw_perf_event_init(event);
}
+static void armpmu_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(pmu_ctx->pmu);
+
+ if (armpmu->sched_task)
+ armpmu->sched_task(pmu_ctx, sched_in);
+}
+
static void armpmu_enable(struct pmu *pmu)
{
struct arm_pmu *armpmu = to_arm_pmu(pmu);
@@ -864,6 +899,7 @@ struct arm_pmu *armpmu_alloc(void)
}
pmu->pmu = (struct pmu) {
+ .sched_task = armpmu_sched_task,
.pmu_enable = armpmu_enable,
.pmu_disable = armpmu_disable,
.event_init = armpmu_event_init,
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index b3b34f6670cf..9eda16dd684e 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -46,6 +46,18 @@ static_assert((PERF_EVENT_FLAG_ARCH & ARMPMU_EVT_63BIT) == ARMPMU_EVT_63BIT);
}, \
}
+/*
+ * Maximum branch record entries which could be processed
+ * for core perf branch stack sampling support, regardless
+ * of the hardware support available on a given ARM PMU.
+ */
+#define MAX_BRANCH_RECORDS 64
+
+struct branch_records {
+ struct perf_branch_stack branch_stack;
+ struct perf_branch_entry branch_entries[MAX_BRANCH_RECORDS];
+};
+
/* The events for a given PMU register set. */
struct pmu_hw_events {
/*
@@ -66,6 +78,17 @@ struct pmu_hw_events {
struct arm_pmu *percpu_pmu;
int irq;
+
+ struct branch_records *branches;
+
+ /* Active context for task events */
+ void *branch_context;
+
+ /* Active events requesting branch records */
+ unsigned int branch_users;
+
+ /* Active branch sample type filters */
+ unsigned long branch_sample_type;
};
enum armpmu_attr_groups {
@@ -96,8 +119,15 @@ struct arm_pmu {
void (*stop)(struct arm_pmu *);
void (*reset)(void *);
int (*map_event)(struct perf_event *event);
+ void (*sched_task)(struct perf_event_pmu_context *pmu_ctx, bool sched_in);
+ bool (*branch_stack_init)(struct perf_event *event);
+ void (*branch_stack_add)(struct perf_event *event, struct pmu_hw_events *cpuc);
+ void (*branch_stack_del)(struct perf_event *event, struct pmu_hw_events *cpuc);
+ void (*branch_stack_reset)(void);
int num_events;
- bool secure_access; /* 32-bit ARM only */
+ unsigned int secure_access : 1, /* 32-bit ARM only */
+ has_branch_stack: 1, /* 64-bit ARM only */
+ reserved : 30;
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40
DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);
#define ARMV8_PMUV3_EXT_COMMON_EVENT_BASE 0x4000
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH V18 3/9] drivers: perf: arm_pmu: Add infrastructure for branch stack sampling
2024-06-13 6:17 ` [PATCH V18 3/9] drivers: perf: arm_pmu: Add infrastructure for branch stack sampling Anshuman Khandual
@ 2024-06-14 15:01 ` Mark Rutland
2024-06-17 4:37 ` Anshuman Khandual
0 siblings, 1 reply; 23+ messages in thread
From: Mark Rutland @ 2024-06-14 15:01 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users
On Thu, Jun 13, 2024 at 11:47:25AM +0530, Anshuman Khandual wrote:
> @@ -289,6 +289,23 @@ static void armpmu_start(struct perf_event *event, int flags)
> {
> struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
> struct hw_perf_event *hwc = &event->hw;
> + struct pmu_hw_events *cpuc = this_cpu_ptr(armpmu->hw_events);
> + int idx;
> +
> + /*
> + * Merge all branch filter requests from different perf
> + * events being added into this PMU. This includes both
> + * privilege and branch type filters.
> + */
> + if (armpmu->has_branch_stack) {
> + cpuc->branch_sample_type = 0;
> + for (idx = 0; idx < ARMPMU_MAX_HWEVENTS; idx++) {
> + struct perf_event *event_idx = cpuc->events[idx];
> +
> + if (event_idx && has_branch_stack(event_idx))
> + cpuc->branch_sample_type |= event_idx->attr.branch_sample_type;
> + }
> + }
When we spoke about this, I meant that we should do this under armpmu::start(),
or a callee or caller thereof once we know all the events are configured, just
before we actually enable the PMU.
For example, this could live in armv8pmu_branch_enable(), which'd allow
all the actual logic to be added in the BRBE enablement patch.
Doing this in armpmu_start() doesn't work as well because it won't handle
events being removed.
[...]
> diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
> index b3b34f6670cf..9eda16dd684e 100644
> --- a/include/linux/perf/arm_pmu.h
> +++ b/include/linux/perf/arm_pmu.h
> @@ -46,6 +46,18 @@ static_assert((PERF_EVENT_FLAG_ARCH & ARMPMU_EVT_63BIT) == ARMPMU_EVT_63BIT);
> }, \
> }
>
> +/*
> + * Maximum branch record entries which could be processed
> + * for core perf branch stack sampling support, regardless
> + * of the hardware support available on a given ARM PMU.
> + */
> +#define MAX_BRANCH_RECORDS 64
> +
> +struct branch_records {
> + struct perf_branch_stack branch_stack;
> + struct perf_branch_entry branch_entries[MAX_BRANCH_RECORDS];
> +};
> +
> /* The events for a given PMU register set. */
> struct pmu_hw_events {
> /*
> @@ -66,6 +78,17 @@ struct pmu_hw_events {
> struct arm_pmu *percpu_pmu;
>
> int irq;
> +
> + struct branch_records *branches;
> +
> + /* Active context for task events */
> + void *branch_context;
Using 'void *' here makes this harder to reason about and hides type
safety issues.
Give this a real type. IIUC it should be 'perf_event_context *'.
> +
> + /* Active events requesting branch records */
> + unsigned int branch_users;
> +
> + /* Active branch sample type filters */
> + unsigned long branch_sample_type;
> };
>
> enum armpmu_attr_groups {
> @@ -96,8 +119,15 @@ struct arm_pmu {
> void (*stop)(struct arm_pmu *);
> void (*reset)(void *);
> int (*map_event)(struct perf_event *event);
> + void (*sched_task)(struct perf_event_pmu_context *pmu_ctx, bool sched_in);
> + bool (*branch_stack_init)(struct perf_event *event);
> + void (*branch_stack_add)(struct perf_event *event, struct pmu_hw_events *cpuc);
> + void (*branch_stack_del)(struct perf_event *event, struct pmu_hw_events *cpuc);
> + void (*branch_stack_reset)(void);
The reset callback isn't used in this series; s
Subsequent patches call armv8pmu_branch_stack_reset() directly from
PMUv3 and the BRBE driver, and arm_pmu::branch_stack_reset() is never
used, so we can delete it.
Mark.
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 3/9] drivers: perf: arm_pmu: Add infrastructure for branch stack sampling
2024-06-14 15:01 ` Mark Rutland
@ 2024-06-17 4:37 ` Anshuman Khandual
0 siblings, 0 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-17 4:37 UTC (permalink / raw)
To: Mark Rutland
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users
On 6/14/24 20:31, Mark Rutland wrote:
> On Thu, Jun 13, 2024 at 11:47:25AM +0530, Anshuman Khandual wrote:
>> @@ -289,6 +289,23 @@ static void armpmu_start(struct perf_event *event, int flags)
>> {
>> struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
>> struct hw_perf_event *hwc = &event->hw;
>> + struct pmu_hw_events *cpuc = this_cpu_ptr(armpmu->hw_events);
>> + int idx;
>> +
>> + /*
>> + * Merge all branch filter requests from different perf
>> + * events being added into this PMU. This includes both
>> + * privilege and branch type filters.
>> + */
>> + if (armpmu->has_branch_stack) {
>> + cpuc->branch_sample_type = 0;
>> + for (idx = 0; idx < ARMPMU_MAX_HWEVENTS; idx++) {
>> + struct perf_event *event_idx = cpuc->events[idx];
>> +
>> + if (event_idx && has_branch_stack(event_idx))
>> + cpuc->branch_sample_type |= event_idx->attr.branch_sample_type;
>> + }
>> + }
>
> When we spoke about this, I meant that we should do this under armpmu::start(),
> or a callee or caller thereof once we know all the events are configured, just
> before we actually enable the PMU.
>
> For example, this could live in armv8pmu_branch_enable(), which'd allow
> all the actual logic to be added in the BRBE enablement patch.
>
> Doing this in armpmu_start() doesn't work as well because it won't handle
> events being removed.
Sure, will move this filter aggregation inside armv8pmu_branch_enable() instead
which is being added via the BRBE driver.
diff --git a/drivers/perf/arm_brbe.c b/drivers/perf/arm_brbe.c
index d795e8fd646f..9cf824bdc8b7 100644
--- a/drivers/perf/arm_brbe.c
+++ b/drivers/perf/arm_brbe.c
@@ -856,6 +856,22 @@ void armv8pmu_branch_enable(struct arm_pmu *arm_pmu)
{
struct pmu_hw_events *cpuc = this_cpu_ptr(arm_pmu->hw_events);
u64 brbfcr, brbcr;
+ int idx;
+
+ /*
+ * Merge all branch filter requests from different perf
+ * events being added into this PMU. This includes both
+ * privilege and branch type filters.
+ */
+ if (arm_pmu->has_branch_stack) {
+ cpuc->branch_sample_type = 0;
+ for (idx = 0; idx < ARMPMU_MAX_HWEVENTS; idx++) {
+ struct perf_event *event_idx = cpuc->events[idx];
+
+ if (event_idx && has_branch_stack(event_idx))
+ cpuc->branch_sample_type |= event_idx->attr.branch_sample_type;
+ }
+ }
if (!(cpuc->branch_sample_type && cpuc->branch_users))
return;
>
> [...]
>
>> diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
>> index b3b34f6670cf..9eda16dd684e 100644
>> --- a/include/linux/perf/arm_pmu.h
>> +++ b/include/linux/perf/arm_pmu.h
>> @@ -46,6 +46,18 @@ static_assert((PERF_EVENT_FLAG_ARCH & ARMPMU_EVT_63BIT) == ARMPMU_EVT_63BIT);
>> }, \
>> }
>>
>> +/*
>> + * Maximum branch record entries which could be processed
>> + * for core perf branch stack sampling support, regardless
>> + * of the hardware support available on a given ARM PMU.
>> + */
>> +#define MAX_BRANCH_RECORDS 64
>> +
>> +struct branch_records {
>> + struct perf_branch_stack branch_stack;
>> + struct perf_branch_entry branch_entries[MAX_BRANCH_RECORDS];
>> +};
>> +
>> /* The events for a given PMU register set. */
>> struct pmu_hw_events {
>> /*
>> @@ -66,6 +78,17 @@ struct pmu_hw_events {
>> struct arm_pmu *percpu_pmu;
>>
>> int irq;
>> +
>> + struct branch_records *branches;
>> +
>> + /* Active context for task events */
>> + void *branch_context;
>
> Using 'void *' here makes this harder to reason about and hides type
> safety issues.
>
> Give this a real type. IIUC it should be 'perf_event_context *'.
Sure, will change the type.
>
>> +
>> + /* Active events requesting branch records */
>> + unsigned int branch_users;
>> +
>> + /* Active branch sample type filters */
>> + unsigned long branch_sample_type;
>> };
>>
>> enum armpmu_attr_groups {
>> @@ -96,8 +119,15 @@ struct arm_pmu {
>> void (*stop)(struct arm_pmu *);
>> void (*reset)(void *);
>> int (*map_event)(struct perf_event *event);
>> + void (*sched_task)(struct perf_event_pmu_context *pmu_ctx, bool sched_in);
>> + bool (*branch_stack_init)(struct perf_event *event);
>> + void (*branch_stack_add)(struct perf_event *event, struct pmu_hw_events *cpuc);
>> + void (*branch_stack_del)(struct perf_event *event, struct pmu_hw_events *cpuc);
>> + void (*branch_stack_reset)(void);
>
> The reset callback isn't used in this series; s
>
> Subsequent patches call armv8pmu_branch_stack_reset() directly from
> PMUv3 and the BRBE driver, and arm_pmu::branch_stack_reset() is never
> used, so we can delete it.
Sure, will drop branch_stack_reset() callback.
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH V18 4/9] arm64/boot: Enable EL2 requirements for BRBE
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
` (2 preceding siblings ...)
2024-06-13 6:17 ` [PATCH V18 3/9] drivers: perf: arm_pmu: Add infrastructure for branch stack sampling Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 5/9] drivers: perf: arm_pmuv3: Enable branch stack sampling via FEAT_BRBE Anshuman Khandual
` (4 subsequent siblings)
8 siblings, 0 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Jonathan Corbet,
Oliver Upton, linux-doc
Fine grained trap control for BRBE registers, and instructions access need
to be configured in HDFGRTR_EL2, HDFGWTR_EL2 and HFGITR_EL2 registers when
kernel enters at EL1 but EL2 is present. This changes __init_el2_fgt() as
required.
Similarly cycle and mis-prediction capture need to be enabled in BRBCR_EL1
and BRBCR_EL2 when the kernel enters either into EL1 or EL2. This adds new
__init_el2_brbe() to achieve this objective.
This also updates Documentation/arch/arm64/booting.rst with all the above
EL2 along with MDRC_EL3.SBRBE requirements.
First this replaces an existing hard encoding (1 << 62) with corresponding
applicable macro HDFGRTR_EL2_nPMSNEVFR_EL1_MASK.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
----
Changes in V18:
- Dropped ifdef CONFIG_ARM64_BRBE around __init_el2_brbe()
- Updated the in code comment around __init_el2_brbe()
- Dropped the write up for EL2->EL1 transition, moved up the EL3 write up
Documentation/arch/arm64/booting.rst | 21 +++++++
arch/arm64/include/asm/el2_setup.h | 87 +++++++++++++++++++++++++++-
2 files changed, 105 insertions(+), 3 deletions(-)
diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst
index b57776a68f15..c8a17c021968 100644
--- a/Documentation/arch/arm64/booting.rst
+++ b/Documentation/arch/arm64/booting.rst
@@ -349,6 +349,27 @@ Before jumping into the kernel, the following conditions must be met:
- HWFGWTR_EL2.nSMPRI_EL1 (bit 54) must be initialised to 0b01.
+ For CPUs with feature Branch Record Buffer Extension (FEAT_BRBE):
+
+ - If EL3 is present:
+
+ - MDCR_EL3.SBRBE (bits 33:32) must be initialised to 0b11.
+
+ - If the kernel is entered at EL1 and EL2 is present:
+
+ - BRBCR_EL2.CC (bit 3) must be initialised to 0b1.
+ - BRBCR_EL2.MPRED (bit 4) must be initialised to 0b1.
+
+ - HDFGRTR_EL2.nBRBDATA (bit 61) must be initialised to 0b1.
+ - HDFGRTR_EL2.nBRBCTL (bit 60) must be initialised to 0b1.
+ - HDFGRTR_EL2.nBRBIDR (bit 59) must be initialised to 0b1.
+
+ - HDFGWTR_EL2.nBRBDATA (bit 61) must be initialised to 0b1.
+ - HDFGWTR_EL2.nBRBCTL (bit 60) must be initialised to 0b1.
+
+ - HFGITR_EL2.nBRBIALL (bit 56) must be initialised to 0b1.
+ - HFGITR_EL2.nBRBINJ (bit 55) must be initialised to 0b1.
+
For CPUs with the Scalable Matrix Extension FA64 feature (FEAT_SME_FA64):
- If EL3 is present:
diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h
index fd87c4b8f984..1c7b131ad2be 100644
--- a/arch/arm64/include/asm/el2_setup.h
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -155,6 +155,40 @@
.Lskip_set_cptr_\@:
.endm
+/*
+ * Enable BRBE to record cycle counts and branch mispredicts.
+ *
+ * At any EL, to record cycle counts BRBE requires that both
+ * BRBCR_EL2.CC=1 and BRBCR_EL1.CC=1.
+ *
+ * At any EL, to record branch mispredicts BRBE requires that both
+ * BRBCR_EL2.MPRED=1 and BRBCR_EL1.MPRED=1.
+ *
+ * When HCR_EL2.E2H=1, the BRBCR_EL1 encoding is redirected to
+ * BRBCR_EL2, but the {CC,MPRED} bits in the real BRBCR_EL1 register
+ * still apply.
+ *
+ * Set {CC,MPRBED} in both BRBCR_EL2 and BRBCR_EL1 so that at runtime we
+ * only need to enable/disable thse in BRBCR_EL1 regardless of whether
+ * the kernel ends up executing in EL1 or EL2.
+ */
+.macro __init_el2_brbe
+ mrs x1, id_aa64dfr0_el1
+ ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4
+ cbz x1, .Lskip_brbe_\@
+
+ mov_q x0, BRBCR_ELx_CC | BRBCR_ELx_MPRED
+ msr_s SYS_BRBCR_EL2, x0
+
+ __check_hvhe .Lset_brbe_nvhe_\@, x1
+ msr_s SYS_BRBCR_EL12, x0 // VHE
+ b .Lskip_brbe_\@
+
+.Lset_brbe_nvhe_\@:
+ msr_s SYS_BRBCR_EL1, x0 // NVHE
+.Lskip_brbe_\@:
+.endm
+
/* Disable any fine grained traps */
.macro __init_el2_fgt
mrs x1, id_aa64mmfr0_el1
@@ -162,16 +196,48 @@
cbz x1, .Lskip_fgt_\@
mov x0, xzr
+ mov x2, xzr
mrs x1, id_aa64dfr0_el1
ubfx x1, x1, #ID_AA64DFR0_EL1_PMSVer_SHIFT, #4
cmp x1, #3
b.lt .Lset_debug_fgt_\@
+
/* Disable PMSNEVFR_EL1 read and write traps */
- orr x0, x0, #(1 << 62)
+ orr x0, x0, #HDFGRTR_EL2_nPMSNEVFR_EL1_MASK
+ orr x2, x2, #HDFGWTR_EL2_nPMSNEVFR_EL1_MASK
.Lset_debug_fgt_\@:
+#ifdef CONFIG_ARM64_BRBE
+ mrs x1, id_aa64dfr0_el1
+ ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4
+ cbz x1, .Lskip_brbe_reg_fgt_\@
+
+ /*
+ * Disable read traps for the following registers
+ *
+ * [BRBSRC|BRBTGT|RBINF]_EL1
+ * [BRBSRCINJ|BRBTGTINJ|BRBINFINJ|BRBTS]_EL1
+ */
+ orr x0, x0, #HDFGRTR_EL2_nBRBDATA_MASK
+
+ /*
+ * Disable write traps for the following registers
+ *
+ * [BRBSRCINJ|BRBTGTINJ|BRBINFINJ|BRBTS]_EL1
+ */
+ orr x2, x2, #HDFGWTR_EL2_nBRBDATA_MASK
+
+ /* Disable read and write traps for [BRBCR|BRBFCR]_EL1 */
+ orr x0, x0, #HDFGRTR_EL2_nBRBCTL_MASK
+ orr x2, x2, #HDFGWTR_EL2_nBRBCTL_MASK
+
+ /* Disable read traps for BRBIDR_EL1 */
+ orr x0, x0, #HDFGRTR_EL2_nBRBIDR_MASK
+
+.Lskip_brbe_reg_fgt_\@:
+#endif /* CONFIG_ARM64_BRBE */
msr_s SYS_HDFGRTR_EL2, x0
- msr_s SYS_HDFGWTR_EL2, x0
+ msr_s SYS_HDFGWTR_EL2, x2
mov x0, xzr
mrs x1, id_aa64pfr1_el1
@@ -194,7 +260,21 @@
.Lset_fgt_\@:
msr_s SYS_HFGRTR_EL2, x0
msr_s SYS_HFGWTR_EL2, x0
- msr_s SYS_HFGITR_EL2, xzr
+ mov x0, xzr
+#ifdef CONFIG_ARM64_BRBE
+ mrs x1, id_aa64dfr0_el1
+ ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4
+ cbz x1, .Lskip_brbe_insn_fgt_\@
+
+ /* Disable traps for BRBIALL instruction */
+ orr x0, x0, #HFGITR_EL2_nBRBIALL_MASK
+
+ /* Disable traps for BRBINJ instruction */
+ orr x0, x0, #HFGITR_EL2_nBRBINJ_MASK
+
+.Lskip_brbe_insn_fgt_\@:
+#endif /* CONFIG_ARM64_BRBE */
+ msr_s SYS_HFGITR_EL2, x0
mrs x1, id_aa64pfr0_el1 // AMU traps UNDEF without AMU
ubfx x1, x1, #ID_AA64PFR0_EL1_AMU_SHIFT, #4
@@ -229,6 +309,7 @@
__init_el2_nvhe_idregs
__init_el2_cptr
__init_el2_fgt
+ __init_el2_brbe
.endm
#ifndef __KVM_NVHE_HYPERVISOR__
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V18 5/9] drivers: perf: arm_pmuv3: Enable branch stack sampling via FEAT_BRBE
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
` (3 preceding siblings ...)
2024-06-13 6:17 ` [PATCH V18 4/9] arm64/boot: Enable EL2 requirements for BRBE Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests Anshuman Khandual
` (3 subsequent siblings)
8 siblings, 0 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users
This extends recently added branch stack sampling framework in ARMV8 PMU to
enable such events via new architecture feature called Branch Record Buffer
Extension aka BRBE. This implements all the armv8pmu_branch_xxx() callbacks
as expected at ARMV8 PMU level required to drive perf branch stack sampling
events. This adds a new config option CONFIG_ARM64_BRBE to encapsulate this
BRBE based implementation, available only on ARM64 platforms.
BRBE hardware captures a branch record via three distinct system registers
representing branch source address, branch target address, and other branch
information. A BRBE buffer implementation is organized as multiple banks of
32 branch records each, which is a collection of BRBSRC_EL1, BRBTGT_EL1 and
BRBINF_EL1 registers. Though total BRBE record entries i.e BRBE_MAX_ENTRIES
cannot exceed MAX_BRANCH_RECORDS as defined for ARM PMU.
Branch stack sampling is enabled and disabled along with regular PMU events
. This adds required function callbacks in armv8pmu_branch_xxx() format, to
drive the PMU branch stack hardware when supported. This also adds fallback
stub definitions for these callbacks for PMUs which would not have required
support.
BRBE hardware attributes get captured in a new reg_brbidr element in struct
arm_pmu during armv8pmu_branch_probe() which is called from broader probing
function __armv8pmu_probe_pmu(). Attributes such as number of branch record
entries implemented in the hardware can be derived from armpmu->reg_brbidr.
BRBE gets enabled via armv8pmu_branch_enable() where it also derives branch
filter, and additional requirements from event's 'attr.branch_sample_type'
and configures them via BRBFCR_EL1 and BRBCR_EL1 registers.
PMU event overflow triggers IRQ, where current branch records get captured,
stitched along with older records available in 'task_ctx', before getting
processed for core perf ring buffer. Task context switch outs incrementally
save current branch records in event's 'pmu_ctx->task_ctx_data' to optimize
workload's branch record samples.
In case multiple events with different branch sample type requests converge
on the same PMU, BRBE gets enabled for the merged branch filter accommoding
all those event's branch sample type. Captured branch records get filterted
in software for an overflown event if BRBE hardware config does not match
its branch sample type, while handling the PMU IRQ.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
----
Changes in V18:
- Replaced BRBIDR0_EL1_FORMAT_0 as BRBIDR0_EL1_FORMAT_FORMAT_0 in BRBE driver
- Added SW filtering framework in read_branch_records() during filter mismatch
- Added SW filtering for both privilege modes and branch types
drivers/perf/Kconfig | 11 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_brbe.c | 1198 +++++++++++++++++++++++++++++++
drivers/perf/arm_pmuv3.c | 160 ++++-
drivers/perf/arm_pmuv3_branch.h | 83 +++
include/linux/perf/arm_pmu.h | 5 +
6 files changed, 1457 insertions(+), 1 deletion(-)
create mode 100644 drivers/perf/arm_brbe.c
create mode 100644 drivers/perf/arm_pmuv3_branch.h
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 7526a9e714fa..a8ce723642f0 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -204,6 +204,17 @@ config ARM_SPE_PMU
Extension, which provides periodic sampling of operations in
the CPU pipeline and reports this via the perf AUX interface.
+config ARM64_BRBE
+ bool "Enable support for branch stack sampling using FEAT_BRBE"
+ depends on PERF_EVENTS && ARM64 && ARM_PMU
+ default y
+ help
+ Enable perf support for Branch Record Buffer Extension (BRBE) which
+ records all branches taken in an execution path. This supports some
+ branch types and privilege based filtering. It captures additional
+ relevant information such as cycle count, misprediction and branch
+ type, branch privilege level etc.
+
config ARM_DMC620_PMU
tristate "Enable PMU support for the ARM DMC-620 memory controller"
depends on (ARM64 && ACPI) || COMPILE_TEST
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index 29b1c28203ef..7f9b2b67fea2 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_STARFIVE_STARLINK_PMU) += starfive_starlink_pmu.o
obj-$(CONFIG_THUNDERX2_PMU) += thunderx2_pmu.o
obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o
+obj-$(CONFIG_ARM64_BRBE) += arm_brbe.o
obj-$(CONFIG_ARM_DMC620_PMU) += arm_dmc620_pmu.o
obj-$(CONFIG_MARVELL_CN10K_TAD_PMU) += marvell_cn10k_tad_pmu.o
obj-$(CONFIG_MARVELL_CN10K_DDR_PMU) += marvell_cn10k_ddr_pmu.o
diff --git a/drivers/perf/arm_brbe.c b/drivers/perf/arm_brbe.c
new file mode 100644
index 000000000000..d795e8fd646f
--- /dev/null
+++ b/drivers/perf/arm_brbe.c
@@ -0,0 +1,1198 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Branch Record Buffer Extension Driver.
+ *
+ * Copyright (C) 2022-2023 ARM Limited
+ *
+ * Author: Anshuman Khandual <anshuman.khandual@arm.com>
+ */
+#include "arm_pmuv3_branch.h"
+
+#define BRBFCR_EL1_BRANCH_FILTERS (BRBFCR_EL1_DIRECT | \
+ BRBFCR_EL1_INDIRECT | \
+ BRBFCR_EL1_RTN | \
+ BRBFCR_EL1_INDCALL | \
+ BRBFCR_EL1_DIRCALL | \
+ BRBFCR_EL1_CONDDIR)
+
+#define BRBFCR_EL1_CONFIG_MASK (BRBFCR_EL1_BANK_MASK | \
+ BRBFCR_EL1_PAUSED | \
+ BRBFCR_EL1_EnI | \
+ BRBFCR_EL1_BRANCH_FILTERS)
+
+/*
+ * BRBTS_EL1 is currently not used for branch stack implementation
+ * purpose but BRBCR_ELx.TS needs to have a valid value from all
+ * available options. BRBCR_ELx_TS_VIRTUAL is selected for this.
+ */
+#define BRBCR_ELx_DEFAULT_TS FIELD_PREP(BRBCR_ELx_TS_MASK, BRBCR_ELx_TS_VIRTUAL)
+
+#define BRBCR_ELx_CONFIG_MASK (BRBCR_ELx_EXCEPTION | \
+ BRBCR_ELx_ERTN | \
+ BRBCR_ELx_CC | \
+ BRBCR_ELx_MPRED | \
+ BRBCR_ELx_ExBRE | \
+ BRBCR_ELx_E0BRE | \
+ BRBCR_ELx_FZP | \
+ BRBCR_ELx_TS_MASK)
+
+/*
+ * BRBE Buffer Organization
+ *
+ * BRBE buffer is arranged as multiple banks of 32 branch record
+ * entries each. An individual branch record in a given bank could
+ * be accessed, after selecting the bank in BRBFCR_EL1.BANK and
+ * accessing the registers i.e [BRBSRC, BRBTGT, BRBINF] set with
+ * indices [0..31].
+ *
+ * Bank 0
+ *
+ * --------------------------------- ------
+ * | 00 | BRBSRC | BRBTGT | BRBINF | | 00 |
+ * --------------------------------- ------
+ * | 01 | BRBSRC | BRBTGT | BRBINF | | 01 |
+ * --------------------------------- ------
+ * | .. | BRBSRC | BRBTGT | BRBINF | | .. |
+ * --------------------------------- ------
+ * | 31 | BRBSRC | BRBTGT | BRBINF | | 31 |
+ * --------------------------------- ------
+ *
+ * Bank 1
+ *
+ * --------------------------------- ------
+ * | 32 | BRBSRC | BRBTGT | BRBINF | | 00 |
+ * --------------------------------- ------
+ * | 33 | BRBSRC | BRBTGT | BRBINF | | 01 |
+ * --------------------------------- ------
+ * | .. | BRBSRC | BRBTGT | BRBINF | | .. |
+ * --------------------------------- ------
+ * | 63 | BRBSRC | BRBTGT | BRBINF | | 31 |
+ * --------------------------------- ------
+ */
+#define BRBE_BANK_MAX_ENTRIES 32
+#define BRBE_MAX_BANK 2
+#define BRBE_MAX_ENTRIES (BRBE_BANK_MAX_ENTRIES * BRBE_MAX_BANK)
+
+#define BRBE_BANK0_IDX_MIN 0
+#define BRBE_BANK0_IDX_MAX 31
+#define BRBE_BANK1_IDX_MIN 32
+#define BRBE_BANK1_IDX_MAX 63
+
+struct brbe_regset {
+ unsigned long brbsrc;
+ unsigned long brbtgt;
+ unsigned long brbinf;
+};
+
+#define PERF_BR_ARM64_MAX (PERF_BR_MAX + PERF_BR_NEW_MAX)
+
+struct arm64_perf_task_context {
+ struct brbe_regset store[BRBE_MAX_ENTRIES];
+ int nr_brbe_records;
+
+ /*
+ * Branch Filter Mask
+ *
+ * This mask represents all branch record types i.e PERF_BR_XXX
+ * (as defined in core perf ABI) that can be generated with the
+ * event's branch_sample_type request. The mask layout could be
+ * found here. Although the bit 15 i.e PERF_BR_EXTEND_ABI never
+ * gets set in the mask.
+ *
+ * 23 (PERF_BR_MAX + PERF_BR_NEW_MAX) 0
+ * | |
+ * ---------------------------------------------------------
+ * | Extended ABI section | X | ABI section |
+ * ---------------------------------------------------------
+ */
+ DECLARE_BITMAP(br_type_mask, PERF_BR_ARM64_MAX);
+};
+
+static void branch_mask_set_all(unsigned long *event_type_mask)
+{
+ int idx;
+
+ for (idx = PERF_BR_UNKNOWN; idx < PERF_BR_EXTEND_ABI; idx++)
+ set_bit(idx, event_type_mask);
+
+ for (idx = PERF_BR_NEW_FAULT_ALGN; idx < PERF_BR_NEW_MAX; idx++)
+ set_bit(PERF_BR_MAX + idx, event_type_mask);
+}
+
+static void branch_mask_set_arch(unsigned long *event_type_mask)
+{
+ set_bit(PERF_BR_MAX + PERF_BR_NEW_FAULT_ALGN, event_type_mask);
+ set_bit(PERF_BR_MAX + PERF_BR_NEW_FAULT_DATA, event_type_mask);
+ set_bit(PERF_BR_MAX + PERF_BR_NEW_FAULT_INST, event_type_mask);
+
+ set_bit(PERF_BR_MAX + PERF_BR_ARM64_FIQ, event_type_mask);
+ set_bit(PERF_BR_MAX + PERF_BR_ARM64_DEBUG_HALT, event_type_mask);
+ set_bit(PERF_BR_MAX + PERF_BR_ARM64_DEBUG_EXIT, event_type_mask);
+ set_bit(PERF_BR_MAX + PERF_BR_ARM64_DEBUG_INST, event_type_mask);
+ set_bit(PERF_BR_MAX + PERF_BR_ARM64_DEBUG_DATA, event_type_mask);
+}
+
+static void branch_entry_mask(struct perf_branch_entry *entry,
+ unsigned long *event_type_mask)
+{
+ u64 idx;
+
+ bitmap_zero(event_type_mask, PERF_BR_ARM64_MAX);
+ for (idx = PERF_BR_UNKNOWN; idx < PERF_BR_EXTEND_ABI; idx++) {
+ if (entry->type == idx)
+ set_bit(idx, event_type_mask);
+ }
+
+ if (entry->type == PERF_BR_EXTEND_ABI) {
+ for (idx = PERF_BR_NEW_FAULT_ALGN; idx < PERF_BR_NEW_MAX; idx++) {
+ if (entry->new_type == idx)
+ set_bit(PERF_BR_MAX + idx, event_type_mask);
+ }
+ }
+}
+
+static void prepare_event_branch_type_mask(struct perf_event *event,
+ unsigned long *event_type_mask)
+{
+ u64 branch_sample = event->attr.branch_sample_type;
+
+ bitmap_zero(event_type_mask, PERF_BR_ARM64_MAX);
+
+ /*
+ * The platform specific branch types might not follow event's
+ * branch filter requests accurately. Let's add all of them as
+ * acceptible branch types during the filtering process.
+ */
+ branch_mask_set_arch(event_type_mask);
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_ANY) {
+ branch_mask_set_all(event_type_mask);
+ return;
+ }
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_IND_JUMP)
+ set_bit(PERF_BR_IND, event_type_mask);
+
+ set_bit(PERF_BR_UNCOND, event_type_mask);
+ if (branch_sample & PERF_SAMPLE_BRANCH_COND) {
+ clear_bit(PERF_BR_UNCOND, event_type_mask);
+ set_bit(PERF_BR_COND, event_type_mask);
+ }
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_CALL)
+ set_bit(PERF_BR_CALL, event_type_mask);
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_IND_CALL)
+ set_bit(PERF_BR_IND_CALL, event_type_mask);
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_ANY_CALL) {
+ set_bit(PERF_BR_CALL, event_type_mask);
+ set_bit(PERF_BR_IRQ, event_type_mask);
+ set_bit(PERF_BR_SYSCALL, event_type_mask);
+ set_bit(PERF_BR_SERROR, event_type_mask);
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_COND)
+ set_bit(PERF_BR_COND_CALL, event_type_mask);
+ }
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_ANY_RETURN) {
+ set_bit(PERF_BR_RET, event_type_mask);
+ set_bit(PERF_BR_ERET, event_type_mask);
+ set_bit(PERF_BR_SYSRET, event_type_mask);
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_COND)
+ set_bit(PERF_BR_COND_RET, event_type_mask);
+ }
+}
+
+struct brbe_hw_attr {
+ int brbe_version;
+ int brbe_cc;
+ int brbe_nr;
+ int brbe_format;
+};
+
+#define RETURN_READ_BRBSRCN(n) \
+ read_sysreg_s(SYS_BRBSRC_EL1(n))
+
+#define RETURN_READ_BRBTGTN(n) \
+ read_sysreg_s(SYS_BRBTGT_EL1(n))
+
+#define RETURN_READ_BRBINFN(n) \
+ read_sysreg_s(SYS_BRBINF_EL1(n))
+
+#define BRBE_REGN_CASE(n, case_macro) \
+ case n: return case_macro(n); break
+
+#define BRBE_REGN_SWITCH(x, case_macro) \
+ do { \
+ switch (x) { \
+ BRBE_REGN_CASE(0, case_macro); \
+ BRBE_REGN_CASE(1, case_macro); \
+ BRBE_REGN_CASE(2, case_macro); \
+ BRBE_REGN_CASE(3, case_macro); \
+ BRBE_REGN_CASE(4, case_macro); \
+ BRBE_REGN_CASE(5, case_macro); \
+ BRBE_REGN_CASE(6, case_macro); \
+ BRBE_REGN_CASE(7, case_macro); \
+ BRBE_REGN_CASE(8, case_macro); \
+ BRBE_REGN_CASE(9, case_macro); \
+ BRBE_REGN_CASE(10, case_macro); \
+ BRBE_REGN_CASE(11, case_macro); \
+ BRBE_REGN_CASE(12, case_macro); \
+ BRBE_REGN_CASE(13, case_macro); \
+ BRBE_REGN_CASE(14, case_macro); \
+ BRBE_REGN_CASE(15, case_macro); \
+ BRBE_REGN_CASE(16, case_macro); \
+ BRBE_REGN_CASE(17, case_macro); \
+ BRBE_REGN_CASE(18, case_macro); \
+ BRBE_REGN_CASE(19, case_macro); \
+ BRBE_REGN_CASE(20, case_macro); \
+ BRBE_REGN_CASE(21, case_macro); \
+ BRBE_REGN_CASE(22, case_macro); \
+ BRBE_REGN_CASE(23, case_macro); \
+ BRBE_REGN_CASE(24, case_macro); \
+ BRBE_REGN_CASE(25, case_macro); \
+ BRBE_REGN_CASE(26, case_macro); \
+ BRBE_REGN_CASE(27, case_macro); \
+ BRBE_REGN_CASE(28, case_macro); \
+ BRBE_REGN_CASE(29, case_macro); \
+ BRBE_REGN_CASE(30, case_macro); \
+ BRBE_REGN_CASE(31, case_macro); \
+ default: \
+ pr_warn("unknown register index\n"); \
+ return -1; \
+ } \
+ } while (0)
+
+static inline int buffer_to_brbe_idx(int buffer_idx)
+{
+ return buffer_idx % BRBE_BANK_MAX_ENTRIES;
+}
+
+static inline u64 get_brbsrc_reg(int buffer_idx)
+{
+ int brbe_idx = buffer_to_brbe_idx(buffer_idx);
+
+ BRBE_REGN_SWITCH(brbe_idx, RETURN_READ_BRBSRCN);
+}
+
+static inline u64 get_brbtgt_reg(int buffer_idx)
+{
+ int brbe_idx = buffer_to_brbe_idx(buffer_idx);
+
+ BRBE_REGN_SWITCH(brbe_idx, RETURN_READ_BRBTGTN);
+}
+
+static inline u64 get_brbinf_reg(int buffer_idx)
+{
+ int brbe_idx = buffer_to_brbe_idx(buffer_idx);
+
+ BRBE_REGN_SWITCH(brbe_idx, RETURN_READ_BRBINFN);
+}
+
+static inline u64 brbe_record_valid(u64 brbinf)
+{
+ return FIELD_GET(BRBINFx_EL1_VALID_MASK, brbinf);
+}
+
+static inline bool brbe_invalid(u64 brbinf)
+{
+ return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_NONE;
+}
+
+static inline bool brbe_record_is_complete(u64 brbinf)
+{
+ return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_FULL;
+}
+
+static inline bool brbe_record_is_source_only(u64 brbinf)
+{
+ return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_SOURCE;
+}
+
+static inline bool brbe_record_is_target_only(u64 brbinf)
+{
+ return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_TARGET;
+}
+
+static inline int brbe_get_in_tx(u64 brbinf)
+{
+ return FIELD_GET(BRBINFx_EL1_T_MASK, brbinf);
+}
+
+static inline int brbe_get_mispredict(u64 brbinf)
+{
+ return FIELD_GET(BRBINFx_EL1_MPRED_MASK, brbinf);
+}
+
+static inline int brbe_get_lastfailed(u64 brbinf)
+{
+ return FIELD_GET(BRBINFx_EL1_LASTFAILED_MASK, brbinf);
+}
+
+static inline int brbe_get_cycles(u64 brbinf)
+{
+ /*
+ * Captured cycle count is unknown and hence
+ * should not be passed on to the user space.
+ */
+ if (brbinf & BRBINFx_EL1_CCU)
+ return 0;
+
+ return FIELD_GET(BRBINFx_EL1_CC_MASK, brbinf);
+}
+
+static inline int brbe_get_type(u64 brbinf)
+{
+ return FIELD_GET(BRBINFx_EL1_TYPE_MASK, brbinf);
+}
+
+static inline int brbe_get_el(u64 brbinf)
+{
+ return FIELD_GET(BRBINFx_EL1_EL_MASK, brbinf);
+}
+
+static inline int brbe_get_numrec(u64 brbidr)
+{
+ return FIELD_GET(BRBIDR0_EL1_NUMREC_MASK, brbidr);
+}
+
+static inline int brbe_get_format(u64 brbidr)
+{
+ return FIELD_GET(BRBIDR0_EL1_FORMAT_MASK, brbidr);
+}
+
+static inline int brbe_get_cc_bits(u64 brbidr)
+{
+ return FIELD_GET(BRBIDR0_EL1_CC_MASK, brbidr);
+}
+
+void armv8pmu_branch_stack_reset(void)
+{
+ asm volatile(BRB_IALL_INSN);
+ isb();
+}
+
+void armv8pmu_branch_stack_add(struct perf_event *event, struct pmu_hw_events *hw_events)
+{
+ struct arm64_perf_task_context *task_ctx = event->pmu_ctx->task_ctx_data;
+
+ if (event->ctx->task)
+ prepare_event_branch_type_mask(event, task_ctx->br_type_mask);
+
+ /*
+ * Reset branch records buffer if a new CPU bound event
+ * gets scheduled on a PMU. Otherwise existing branch
+ * records present in the buffer might just leak into
+ * such events.
+ *
+ * Also reset current 'hw_events->branch_context' because
+ * any previous task bound event now would have lost an
+ * opportunity for continuous branch records.
+ */
+ if (!event->ctx->task) {
+ hw_events->branch_context = NULL;
+ armv8pmu_branch_stack_reset();
+ }
+
+ /*
+ * Reset branch records buffer if a new task event gets
+ * scheduled on a PMU which might have existing records.
+ * Otherwise older branch records present in the buffer
+ * might leak into the new task event.
+ */
+ if (event->ctx->task && hw_events->branch_context != event->ctx) {
+ hw_events->branch_context = event->ctx;
+ armv8pmu_branch_stack_reset();
+ }
+ hw_events->branch_users++;
+}
+
+void armv8pmu_branch_stack_del(struct perf_event *event, struct pmu_hw_events *hw_events)
+{
+ WARN_ON_ONCE(!hw_events->branch_users);
+ hw_events->branch_users--;
+ if (!hw_events->branch_users) {
+ hw_events->branch_context = NULL;
+ hw_events->branch_sample_type = 0;
+ }
+}
+
+static bool valid_brbe_nr(int brbe_nr)
+{
+ return brbe_nr == BRBIDR0_EL1_NUMREC_8 ||
+ brbe_nr == BRBIDR0_EL1_NUMREC_16 ||
+ brbe_nr == BRBIDR0_EL1_NUMREC_32 ||
+ brbe_nr == BRBIDR0_EL1_NUMREC_64;
+}
+
+static bool valid_brbe_cc(int brbe_cc)
+{
+ return brbe_cc == BRBIDR0_EL1_CC_20_BIT;
+}
+
+static bool valid_brbe_format(int brbe_format)
+{
+ return brbe_format == BRBIDR0_EL1_FORMAT_FORMAT_0;
+}
+
+static bool valid_brbe_version(int brbe_version)
+{
+ return brbe_version == ID_AA64DFR0_EL1_BRBE_IMP ||
+ brbe_version == ID_AA64DFR0_EL1_BRBE_BRBE_V1P1;
+}
+
+static void select_brbe_bank(int bank)
+{
+ u64 brbfcr;
+
+ WARN_ON(bank > 1);
+ brbfcr = read_sysreg_s(SYS_BRBFCR_EL1);
+ brbfcr &= ~BRBFCR_EL1_BANK_MASK;
+ brbfcr |= SYS_FIELD_PREP(BRBFCR_EL1, BANK, bank);
+ write_sysreg_s(brbfcr, SYS_BRBFCR_EL1);
+ isb();
+}
+
+static bool __read_brbe_regset(struct brbe_regset *entry, int idx)
+{
+ entry->brbinf = get_brbinf_reg(idx);
+
+ if (brbe_invalid(entry->brbinf))
+ return false;
+
+ entry->brbsrc = get_brbsrc_reg(idx);
+ entry->brbtgt = get_brbtgt_reg(idx);
+ return true;
+}
+
+/*
+ * Read all BRBE entries in HW until the first invalid entry.
+ *
+ * The caller must ensure that the BRBE is not concurrently modifying these
+ * branch entries.
+ */
+static int capture_brbe_regset(struct brbe_regset *buf, int nr_hw_entries)
+{
+ int idx = 0;
+
+ select_brbe_bank(0);
+ while (idx < nr_hw_entries && idx <= BRBE_BANK0_IDX_MAX) {
+ if (!__read_brbe_regset(&buf[idx], idx))
+ return idx;
+ idx++;
+ }
+
+ select_brbe_bank(1);
+ while (idx < nr_hw_entries && idx <= BRBE_BANK1_IDX_MAX) {
+ if (!__read_brbe_regset(&buf[idx], idx))
+ return idx;
+ idx++;
+ }
+ return idx;
+}
+
+/*
+ * This function concatenates branch records from stored and live buffer
+ * up to maximum nr_max records and the stored buffer holds the resultant
+ * buffer. The concatenated buffer contains all the branch records from
+ * the live buffer but might contain some from stored buffer considering
+ * the maximum combined length does not exceed 'nr_max'.
+ *
+ * Stored records Live records
+ * ------------------------------------------------^
+ * | S0 | L0 | Newest |
+ * --------------------------------- |
+ * | S1 | L1 | |
+ * --------------------------------- |
+ * | S2 | L2 | |
+ * --------------------------------- |
+ * | S3 | L3 | |
+ * --------------------------------- |
+ * | S4 | L4 | nr_max
+ * --------------------------------- |
+ * | | L5 | |
+ * --------------------------------- |
+ * | | L6 | |
+ * --------------------------------- |
+ * | | L7 | |
+ * --------------------------------- |
+ * | | | |
+ * --------------------------------- |
+ * | | | Oldest |
+ * ------------------------------------------------V
+ *
+ *
+ * S0 is the newest in the stored records, where as L7 is the oldest in
+ * the live records. Unless the live buffer is detected as being full
+ * thus potentially dropping off some older records, L7 and S0 records
+ * are contiguous in time for a user task context. The stitched buffer
+ * here represents maximum possible branch records, contiguous in time.
+ *
+ * Stored records Live records
+ * ------------------------------------------------^
+ * | L0 | L0 | Newest |
+ * --------------------------------- |
+ * | L1 | L1 | |
+ * --------------------------------- |
+ * | L2 | L2 | |
+ * --------------------------------- |
+ * | L3 | L3 | |
+ * --------------------------------- |
+ * | L4 | L4 | nr_max
+ * --------------------------------- |
+ * | L5 | L5 | |
+ * --------------------------------- |
+ * | L6 | L6 | |
+ * --------------------------------- |
+ * | L7 | L7 | |
+ * --------------------------------- |
+ * | S0 | | |
+ * --------------------------------- |
+ * | S1 | | Oldest |
+ * ------------------------------------------------V
+ * | S2 | <----|
+ * ----------------- |
+ * | S3 | <----| Dropped off after nr_max
+ * ----------------- |
+ * | S4 | <----|
+ * -----------------
+ */
+static int stitch_stored_live_entries(struct brbe_regset *stored,
+ struct brbe_regset *live,
+ int nr_stored, int nr_live,
+ int nr_max)
+{
+ int nr_move = min(nr_stored, nr_max - nr_live);
+
+ /* Move the tail of the buffer to make room for the new entries */
+ memmove(&stored[nr_live], &stored[0], nr_move * sizeof(*stored));
+
+ /* Copy the new entries into the head of the buffer */
+ memcpy(&stored[0], &live[0], nr_live * sizeof(*stored));
+
+ /* Return the number of entries in the stitched buffer */
+ return min(nr_live + nr_stored, nr_max);
+}
+
+static int brbe_branch_save(struct brbe_regset *live, int nr_hw_entries)
+{
+ u64 brbfcr = read_sysreg_s(SYS_BRBFCR_EL1);
+ int nr_live;
+
+ write_sysreg_s(brbfcr | BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1);
+ isb();
+
+ nr_live = capture_brbe_regset(live, nr_hw_entries);
+
+ write_sysreg_s(brbfcr & ~BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1);
+ isb();
+
+ return nr_live;
+}
+
+void armv8pmu_branch_save(struct arm_pmu *arm_pmu, void *ctx)
+{
+ struct arm64_perf_task_context *task_ctx = ctx;
+ struct brbe_regset live[BRBE_MAX_ENTRIES];
+ int nr_live, nr_store, nr_hw_entries;
+
+ nr_hw_entries = brbe_get_numrec(arm_pmu->reg_brbidr);
+ nr_live = brbe_branch_save(live, nr_hw_entries);
+ nr_store = task_ctx->nr_brbe_records;
+ nr_store = stitch_stored_live_entries(task_ctx->store, live, nr_store,
+ nr_live, nr_hw_entries);
+ task_ctx->nr_brbe_records = nr_store;
+}
+
+/*
+ * Generic perf branch filters supported on BRBE
+ *
+ * New branch filters need to be evaluated whether they could be supported on
+ * BRBE. This ensures that such branch filters would not just be accepted, to
+ * fail silently. PERF_SAMPLE_BRANCH_HV is a special case that is selectively
+ * supported only on platforms where kernel is in hyp mode.
+ */
+#define BRBE_EXCLUDE_BRANCH_FILTERS (PERF_SAMPLE_BRANCH_ABORT_TX | \
+ PERF_SAMPLE_BRANCH_IN_TX | \
+ PERF_SAMPLE_BRANCH_NO_TX | \
+ PERF_SAMPLE_BRANCH_CALL_STACK | \
+ PERF_SAMPLE_BRANCH_COUNTERS)
+
+#define BRBE_ALLOWED_BRANCH_FILTERS (PERF_SAMPLE_BRANCH_USER | \
+ PERF_SAMPLE_BRANCH_KERNEL | \
+ PERF_SAMPLE_BRANCH_HV | \
+ PERF_SAMPLE_BRANCH_ANY | \
+ PERF_SAMPLE_BRANCH_ANY_CALL | \
+ PERF_SAMPLE_BRANCH_ANY_RETURN | \
+ PERF_SAMPLE_BRANCH_IND_CALL | \
+ PERF_SAMPLE_BRANCH_COND | \
+ PERF_SAMPLE_BRANCH_IND_JUMP | \
+ PERF_SAMPLE_BRANCH_CALL | \
+ PERF_SAMPLE_BRANCH_NO_FLAGS | \
+ PERF_SAMPLE_BRANCH_NO_CYCLES | \
+ PERF_SAMPLE_BRANCH_TYPE_SAVE | \
+ PERF_SAMPLE_BRANCH_HW_INDEX | \
+ PERF_SAMPLE_BRANCH_PRIV_SAVE)
+
+#define BRBE_PERF_BRANCH_FILTERS (BRBE_ALLOWED_BRANCH_FILTERS | \
+ BRBE_EXCLUDE_BRANCH_FILTERS)
+
+bool armv8pmu_branch_attr_valid(struct perf_event *event)
+{
+ u64 branch_type = event->attr.branch_sample_type;
+
+ /*
+ * Ensure both perf branch filter allowed and exclude
+ * masks are always in sync with the generic perf ABI.
+ */
+ BUILD_BUG_ON(BRBE_PERF_BRANCH_FILTERS != (PERF_SAMPLE_BRANCH_MAX - 1));
+
+ if (branch_type & ~BRBE_ALLOWED_BRANCH_FILTERS) {
+ pr_debug_once("requested branch filter not supported 0x%llx\n", branch_type);
+ return false;
+ }
+
+ /*
+ * If the event does not have at least one of the privilege
+ * branch filters as in PERF_SAMPLE_BRANCH_PLM_ALL, the core
+ * perf will adjust its value based on perf event's existing
+ * privilege level via attr.exclude_[user|kernel|hv].
+ *
+ * As event->attr.branch_sample_type might have been changed
+ * when the event reaches here, it is not possible to figure
+ * out whether the event originally had HV privilege request
+ * or got added via the core perf. Just report this situation
+ * once and continue ignoring if there are other instances.
+ */
+ if ((branch_type & PERF_SAMPLE_BRANCH_HV) && !is_kernel_in_hyp_mode())
+ pr_debug_once("hypervisor privilege filter not supported 0x%llx\n", branch_type);
+
+ return true;
+}
+
+int armv8pmu_task_ctx_cache_alloc(struct arm_pmu *arm_pmu)
+{
+ size_t size = sizeof(struct arm64_perf_task_context);
+
+ arm_pmu->pmu.task_ctx_cache = kmem_cache_create("arm64_brbe_task_ctx", size, 0, 0, NULL);
+ if (!arm_pmu->pmu.task_ctx_cache)
+ return -ENOMEM;
+ return 0;
+}
+
+void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu)
+{
+ kmem_cache_destroy(arm_pmu->pmu.task_ctx_cache);
+}
+
+static int brbe_attributes_probe(struct arm_pmu *armpmu, u32 brbe)
+{
+ u64 brbidr = read_sysreg_s(SYS_BRBIDR0_EL1);
+ int brbe_version, brbe_format, brbe_cc, brbe_nr;
+
+ brbe_version = brbe;
+ brbe_format = brbe_get_format(brbidr);
+ brbe_cc = brbe_get_cc_bits(brbidr);
+ brbe_nr = brbe_get_numrec(brbidr);
+ armpmu->reg_brbidr = brbidr;
+
+ if (!valid_brbe_version(brbe_version) ||
+ !valid_brbe_format(brbe_format) ||
+ !valid_brbe_cc(brbe_cc) ||
+ !valid_brbe_nr(brbe_nr))
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+void armv8pmu_branch_probe(struct arm_pmu *armpmu)
+{
+ u64 aa64dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1);
+ u32 brbe;
+
+ /*
+ * BRBE implementation's branch entries cannot exceed maximum
+ * branch records supported at the ARM PMU level abstraction.
+ * Otherwise there is always a possibility of array overflow,
+ * while processing BRBE branch records.
+ */
+ BUILD_BUG_ON(BRBE_BANK_MAX_ENTRIES > MAX_BRANCH_RECORDS);
+
+ brbe = cpuid_feature_extract_unsigned_field(aa64dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT);
+ if (!brbe)
+ return;
+
+ if (brbe_attributes_probe(armpmu, brbe))
+ return;
+
+ armpmu->has_branch_stack = 1;
+}
+
+/*
+ * BRBE supports the following functional branch type filters while
+ * generating branch records. These branch filters can be enabled,
+ * either individually or as a group i.e ORing multiple filters
+ * with each other.
+ *
+ * BRBFCR_EL1_CONDDIR - Conditional direct branch
+ * BRBFCR_EL1_DIRCALL - Direct call
+ * BRBFCR_EL1_INDCALL - Indirect call
+ * BRBFCR_EL1_INDIRECT - Indirect branch
+ * BRBFCR_EL1_DIRECT - Direct branch
+ * BRBFCR_EL1_RTN - Subroutine return
+ */
+static u64 branch_type_to_brbfcr(int branch_type)
+{
+ u64 brbfcr = 0;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_ANY) {
+ brbfcr |= BRBFCR_EL1_BRANCH_FILTERS;
+ return brbfcr;
+ }
+
+ if (branch_type & PERF_SAMPLE_BRANCH_ANY_CALL) {
+ brbfcr |= BRBFCR_EL1_INDCALL;
+ brbfcr |= BRBFCR_EL1_DIRCALL;
+ }
+
+ if (branch_type & PERF_SAMPLE_BRANCH_ANY_RETURN)
+ brbfcr |= BRBFCR_EL1_RTN;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_IND_CALL)
+ brbfcr |= BRBFCR_EL1_INDCALL;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_COND)
+ brbfcr |= BRBFCR_EL1_CONDDIR;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_IND_JUMP)
+ brbfcr |= BRBFCR_EL1_INDIRECT;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_CALL)
+ brbfcr |= BRBFCR_EL1_DIRCALL;
+
+ return brbfcr & BRBFCR_EL1_CONFIG_MASK;
+}
+
+/*
+ * BRBE supports the following privilege mode filters while generating
+ * branch records.
+ *
+ * BRBCR_ELx_E0BRE - EL0 branch records
+ * BRBCR_ELx_ExBRE - EL1/EL2 branch records
+ *
+ * BRBE also supports the following additional functional branch type
+ * filters while generating branch records.
+ *
+ * BRBCR_ELx_EXCEPTION - Exception
+ * BRBCR_ELx_ERTN - Exception return
+ */
+static u64 branch_type_to_brbcr(int branch_type)
+{
+ u64 brbcr = BRBCR_ELx_DEFAULT_TS;
+
+ /*
+ * BRBE should be paused on PMU interrupt while tracing kernel
+ * space to stop capturing further branch records. Otherwise
+ * interrupt handler branch records might get into the samples
+ * which is not desired.
+ *
+ * BRBE need not be paused on PMU interrupt while tracing only
+ * the user space, because it will automatically be inside the
+ * prohibited region. But even after PMU overflow occurs, the
+ * interrupt could still take much more cycles, before it can
+ * be taken and by that time BRBE will have been overwritten.
+ * Hence enable pause on PMU interrupt mechanism even for user
+ * only traces as well.
+ */
+ brbcr |= BRBCR_ELx_FZP;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_USER)
+ brbcr |= BRBCR_ELx_E0BRE;
+
+ /*
+ * When running in the hyp mode, writing into BRBCR_EL1
+ * actually writes into BRBCR_EL2 instead. Field E2BRE
+ * is also at the same position as E1BRE.
+ */
+ if (branch_type & PERF_SAMPLE_BRANCH_KERNEL)
+ brbcr |= BRBCR_ELx_ExBRE;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_HV) {
+ if (is_kernel_in_hyp_mode())
+ brbcr |= BRBCR_ELx_ExBRE;
+ }
+
+ if (!(branch_type & PERF_SAMPLE_BRANCH_NO_CYCLES))
+ brbcr |= BRBCR_ELx_CC;
+
+ if (!(branch_type & PERF_SAMPLE_BRANCH_NO_FLAGS))
+ brbcr |= BRBCR_ELx_MPRED;
+
+ /*
+ * The exception and exception return branches could be
+ * captured, irrespective of the perf event's privilege.
+ * If the perf event does not have enough privilege for
+ * a given exception level, then addresses which falls
+ * under that exception level will be reported as zero
+ * for the captured branch record, creating source only
+ * or target only records.
+ */
+ if (branch_type & PERF_SAMPLE_BRANCH_ANY) {
+ brbcr |= BRBCR_ELx_EXCEPTION;
+ brbcr |= BRBCR_ELx_ERTN;
+ }
+
+ if (branch_type & PERF_SAMPLE_BRANCH_ANY_CALL)
+ brbcr |= BRBCR_ELx_EXCEPTION;
+
+ if (branch_type & PERF_SAMPLE_BRANCH_ANY_RETURN)
+ brbcr |= BRBCR_ELx_ERTN;
+
+ return brbcr & BRBCR_ELx_CONFIG_MASK;
+}
+
+void armv8pmu_branch_enable(struct arm_pmu *arm_pmu)
+{
+ struct pmu_hw_events *cpuc = this_cpu_ptr(arm_pmu->hw_events);
+ u64 brbfcr, brbcr;
+
+ if (!(cpuc->branch_sample_type && cpuc->branch_users))
+ return;
+
+ /*
+ * BRBE gets configured with a new mismatched branch sample
+ * type request, overriding any previous branch filters.
+ */
+ brbfcr = read_sysreg_s(SYS_BRBFCR_EL1);
+ brbfcr &= ~BRBFCR_EL1_CONFIG_MASK;
+ brbfcr |= branch_type_to_brbfcr(cpuc->branch_sample_type);
+ write_sysreg_s(brbfcr, SYS_BRBFCR_EL1);
+ isb();
+
+ brbcr = read_sysreg_s(SYS_BRBCR_EL1);
+ brbcr &= ~BRBCR_ELx_CONFIG_MASK;
+ brbcr |= branch_type_to_brbcr(cpuc->branch_sample_type);
+ write_sysreg_s(brbcr, SYS_BRBCR_EL1);
+ isb();
+}
+
+void armv8pmu_branch_disable(void)
+{
+ u64 brbfcr, brbcr;
+
+ brbcr = read_sysreg_s(SYS_BRBCR_EL1);
+ brbfcr = read_sysreg_s(SYS_BRBFCR_EL1);
+ brbcr &= ~(BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE);
+ brbfcr |= BRBFCR_EL1_PAUSED;
+ write_sysreg_s(brbcr, SYS_BRBCR_EL1);
+ write_sysreg_s(brbfcr, SYS_BRBFCR_EL1);
+ isb();
+}
+
+static void brbe_set_perf_entry_type(struct perf_branch_entry *entry, u64 brbinf)
+{
+ int brbe_type = brbe_get_type(brbinf);
+
+ switch (brbe_type) {
+ case BRBINFx_EL1_TYPE_DIRECT_UNCOND:
+ entry->type = PERF_BR_UNCOND;
+ break;
+ case BRBINFx_EL1_TYPE_INDIRECT:
+ entry->type = PERF_BR_IND;
+ break;
+ case BRBINFx_EL1_TYPE_DIRECT_LINK:
+ entry->type = PERF_BR_CALL;
+ break;
+ case BRBINFx_EL1_TYPE_INDIRECT_LINK:
+ entry->type = PERF_BR_IND_CALL;
+ break;
+ case BRBINFx_EL1_TYPE_RET:
+ entry->type = PERF_BR_RET;
+ break;
+ case BRBINFx_EL1_TYPE_DIRECT_COND:
+ entry->type = PERF_BR_COND;
+ break;
+ case BRBINFx_EL1_TYPE_CALL:
+ entry->type = PERF_BR_CALL;
+ break;
+ case BRBINFx_EL1_TYPE_TRAP:
+ entry->type = PERF_BR_SYSCALL;
+ break;
+ case BRBINFx_EL1_TYPE_ERET:
+ entry->type = PERF_BR_ERET;
+ break;
+ case BRBINFx_EL1_TYPE_IRQ:
+ entry->type = PERF_BR_IRQ;
+ break;
+ case BRBINFx_EL1_TYPE_DEBUG_HALT:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_ARM64_DEBUG_HALT;
+ break;
+ case BRBINFx_EL1_TYPE_SERROR:
+ entry->type = PERF_BR_SERROR;
+ break;
+ case BRBINFx_EL1_TYPE_INSN_DEBUG:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_ARM64_DEBUG_INST;
+ break;
+ case BRBINFx_EL1_TYPE_DATA_DEBUG:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_ARM64_DEBUG_DATA;
+ break;
+ case BRBINFx_EL1_TYPE_ALIGN_FAULT:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_NEW_FAULT_ALGN;
+ break;
+ case BRBINFx_EL1_TYPE_INSN_FAULT:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_NEW_FAULT_INST;
+ break;
+ case BRBINFx_EL1_TYPE_DATA_FAULT:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_NEW_FAULT_DATA;
+ break;
+ case BRBINFx_EL1_TYPE_FIQ:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_ARM64_FIQ;
+ break;
+ case BRBINFx_EL1_TYPE_DEBUG_EXIT:
+ entry->type = PERF_BR_EXTEND_ABI;
+ entry->new_type = PERF_BR_ARM64_DEBUG_EXIT;
+ break;
+ default:
+ pr_warn_once("%d - unknown branch type captured\n", brbe_type);
+ entry->type = PERF_BR_UNKNOWN;
+ break;
+ }
+}
+
+static int brbe_get_perf_priv(u64 brbinf)
+{
+ int brbe_el = brbe_get_el(brbinf);
+
+ switch (brbe_el) {
+ case BRBINFx_EL1_EL_EL0:
+ return PERF_BR_PRIV_USER;
+ case BRBINFx_EL1_EL_EL1:
+ return PERF_BR_PRIV_KERNEL;
+ case BRBINFx_EL1_EL_EL2:
+ if (is_kernel_in_hyp_mode())
+ return PERF_BR_PRIV_KERNEL;
+ return PERF_BR_PRIV_HV;
+ default:
+ pr_warn_once("%d - unknown branch privilege captured\n", brbe_el);
+ return PERF_BR_PRIV_UNKNOWN;
+ }
+}
+
+static void capture_brbe_flags(struct perf_branch_entry *entry, struct perf_event *event,
+ u64 brbinf)
+{
+ brbe_set_perf_entry_type(entry, brbinf);
+
+ if (!branch_sample_no_cycles(event))
+ entry->cycles = brbe_get_cycles(brbinf);
+
+ if (!branch_sample_no_flags(event)) {
+ /*
+ * BRBINFx_EL1.LASTFAILED indicates that a TME transaction failed (or
+ * was cancelled) prior to this record, and some number of records
+ * prior to this one, may have been generated during an attempt to
+ * execute the transaction.
+ */
+ entry->abort = brbe_get_lastfailed(brbinf);
+
+ /*
+ * All these information (i.e transaction state and mispredicts)
+ * are available for source only and complete branch records.
+ */
+ if (brbe_record_is_complete(brbinf) ||
+ brbe_record_is_source_only(brbinf)) {
+ entry->mispred = brbe_get_mispredict(brbinf);
+ entry->predicted = !entry->mispred;
+ entry->in_tx = brbe_get_in_tx(brbinf);
+ }
+
+ /*
+ * Currently TME feature is neither implemented in any hardware
+ * nor it is being supported in the kernel. Just warn here once
+ * if TME related information shows up rather unexpectedly.
+ */
+ if (entry->abort || entry->in_tx)
+ pr_warn_once("Unknown transaction states %d %d\n",
+ entry->abort, entry->in_tx);
+ }
+
+ /*
+ * All these information (i.e branch privilege level) are
+ * available for target only and complete branch records.
+ */
+ if (brbe_record_is_complete(brbinf) ||
+ brbe_record_is_target_only(brbinf))
+ entry->priv = brbe_get_perf_priv(brbinf);
+}
+
+static void brbe_regset_branch_entries(struct pmu_hw_events *cpuc, struct perf_event *event,
+ struct brbe_regset *regset, int idx)
+{
+ struct perf_branch_entry *entry = &cpuc->branches->branch_entries[idx];
+ u64 brbinf = regset[idx].brbinf;
+
+ perf_clear_branch_entry_bitfields(entry);
+ if (brbe_record_is_complete(brbinf)) {
+ entry->from = regset[idx].brbsrc;
+ entry->to = regset[idx].brbtgt;
+ } else if (brbe_record_is_source_only(brbinf)) {
+ entry->from = regset[idx].brbsrc;
+ entry->to = 0;
+ } else if (brbe_record_is_target_only(brbinf)) {
+ entry->from = 0;
+ entry->to = regset[idx].brbtgt;
+ }
+ capture_brbe_flags(entry, event, brbinf);
+}
+
+static void process_branch_entries(struct pmu_hw_events *cpuc, struct perf_event *event,
+ struct brbe_regset *regset, int nr_regset)
+{
+ int idx;
+
+ for (idx = 0; idx < nr_regset; idx++)
+ brbe_regset_branch_entries(cpuc, event, regset, idx);
+
+ cpuc->branches->branch_stack.nr = nr_regset;
+ cpuc->branches->branch_stack.hw_idx = -1ULL;
+}
+
+void armv8pmu_branch_read(struct pmu_hw_events *cpuc, struct perf_event *event)
+{
+ struct arm64_perf_task_context *task_ctx = event->pmu_ctx->task_ctx_data;
+ struct brbe_regset live[BRBE_MAX_ENTRIES];
+ int nr_live, nr_store, nr_hw_entries;
+
+ nr_hw_entries = brbe_get_numrec(cpuc->percpu_pmu->reg_brbidr);
+ nr_live = capture_brbe_regset(live, nr_hw_entries);
+ if (event->ctx->task) {
+ nr_store = task_ctx->nr_brbe_records;
+ nr_store = stitch_stored_live_entries(task_ctx->store, live, nr_store,
+ nr_live, nr_hw_entries);
+ process_branch_entries(cpuc, event, task_ctx->store, nr_store);
+ task_ctx->nr_brbe_records = 0;
+ } else {
+ process_branch_entries(cpuc, event, live, nr_live);
+ }
+}
+
+static bool filter_branch_privilege(struct perf_branch_entry *entry, u64 branch_sample_type)
+{
+ /*
+ * Retrieve the privilege level branch filter requests
+ * from the overall branch sample type.
+ */
+ branch_sample_type &= PERF_SAMPLE_BRANCH_PLM_ALL;
+
+ /*
+ * The privilege information do not always get captured
+ * successfully for given BRBE branch record. Hence the
+ * entry->priv could be analyzed for filtering when the
+ * information has really been captured.
+ */
+ if (entry->priv) {
+ if (entry->priv == PERF_BR_PRIV_USER) {
+ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_USER))
+ return false;
+ }
+
+ if (entry->priv == PERF_BR_PRIV_KERNEL) {
+ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_KERNEL)) {
+ if (!is_kernel_in_hyp_mode())
+ return false;
+
+ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_HV))
+ return false;
+ }
+ }
+
+ if (entry->priv == PERF_BR_PRIV_HV) {
+ /*
+ * PERF_SAMPLE_BRANCH_HV request actually gets configured
+ * similar to PERF_SAMPLE_BRANCH_KERNEL when kernel is in
+ * hyp mode. In that case PERF_BR_PRIV_KERNEL should have
+ * been reported for corresponding branch records.
+ */
+ pr_warn_once("PERF_BR_PRIV_HV should not have been captured\n");
+ }
+ return true;
+ }
+
+ if (is_ttbr0_addr(entry->from) || is_ttbr0_addr(entry->to)) {
+ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_USER))
+ return false;
+ }
+
+ if (is_ttbr1_addr(entry->from) || is_ttbr1_addr(entry->to)) {
+ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_KERNEL)) {
+ if (!is_kernel_in_hyp_mode())
+ return false;
+
+ if (!(branch_sample_type & PERF_SAMPLE_BRANCH_HV))
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool filter_branch_record(struct pmu_hw_events *cpuc,
+ struct perf_event *event,
+ struct perf_branch_entry *entry)
+{
+ struct arm64_perf_task_context *task_ctx = event->pmu_ctx->task_ctx_data;
+ u64 branch_sample = event->attr.branch_sample_type;
+ DECLARE_BITMAP(entry_type_mask, PERF_BR_ARM64_MAX);
+ DECLARE_BITMAP(event_type_mask, PERF_BR_ARM64_MAX);
+
+ if (!filter_branch_privilege(entry, branch_sample))
+ return false;
+
+ if (entry->type == PERF_BR_UNKNOWN)
+ return true;
+
+ if (branch_sample & PERF_SAMPLE_BRANCH_ANY)
+ return true;
+
+ /*
+ * Both PMU and event branch filters match here except the privilege
+ * filters - which have already been tested earlier. Skip functional
+ * branch type test and just return success.
+ */
+ if ((cpuc->branch_sample_type & ~PERF_SAMPLE_BRANCH_PLM_ALL) ==
+ event->attr.branch_sample_type)
+ return true;
+
+ branch_entry_mask(entry, entry_type_mask);
+ if (task_ctx)
+ return bitmap_subset(entry_type_mask, task_ctx->br_type_mask, PERF_BR_ARM64_MAX);
+
+ prepare_event_branch_type_mask(event, event_type_mask);
+ return bitmap_subset(entry_type_mask, event_type_mask, PERF_BR_ARM64_MAX);
+}
+
+void arm64_filter_branch_records(struct pmu_hw_events *cpuc,
+ struct perf_event *event,
+ struct branch_records *event_records)
+{
+ struct perf_branch_entry *entry;
+ int idx, count = 0;
+
+ memset(event_records, 0, sizeof(*event_records));
+ for (idx = 0; idx < cpuc->branches->branch_stack.nr; idx++) {
+ entry = &cpuc->branches->branch_entries[idx];
+ if (!filter_branch_record(cpuc, event, entry))
+ continue;
+
+ memcpy(&event_records->branch_entries[count], entry, sizeof(*entry));
+ count++;
+ }
+ event_records->branch_stack.nr = count;
+}
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 23fa6c5da82c..11d0645b0d79 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -26,6 +26,7 @@
#include <linux/nmi.h>
#include <asm/arm_pmuv3.h>
+#include "arm_pmuv3_branch.h"
/* ARMv8 Cortex-A53 specific event types. */
#define ARMV8_A53_PERFCTR_PREF_LINEFILL 0xC2
@@ -829,14 +830,70 @@ static void armv8pmu_start(struct arm_pmu *cpu_pmu)
armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
kvm_vcpu_pmu_resync_el0();
+ if (cpu_pmu->has_branch_stack)
+ armv8pmu_branch_enable(cpu_pmu);
}
static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
{
+ if (cpu_pmu->has_branch_stack)
+ armv8pmu_branch_disable();
+
/* Disable all counters */
armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
}
+static void read_branch_records(struct pmu_hw_events *cpuc,
+ struct perf_event *event,
+ struct perf_sample_data *data,
+ bool *branch_captured)
+{
+ struct branch_records event_records;
+
+ /*
+ * CPU specific branch records buffer must have been allocated already
+ * for the hardware records to be captured and processed further.
+ */
+ if (WARN_ON(!cpuc->branches))
+ return;
+
+ /*
+ * When the current task context does not match with the PMU overflown
+ * event, the captured branch records here cannot be co-related to the
+ * overflowed event. Report to the user - as if no branch records have
+ * been captured, and flush branch records.
+ */
+ if (event->ctx->task && (cpuc->branch_context != event->ctx))
+ return;
+
+ /*
+ * Read the branch records from the hardware once after the PMU IRQ
+ * has been triggered but subsequently same records can be used for
+ * other events that might have been overflowed simultaneously thus
+ * saving much CPU cycles.
+ */
+ if (!*branch_captured) {
+ armv8pmu_branch_read(cpuc, event);
+ *branch_captured = true;
+ }
+
+ /*
+ * Filter captured branch records
+ *
+ * PMU captured branch records would contain samples applicable for
+ * the aggregated branch filters, for all events that got scheduled
+ * on this PMU simultaneously. Hence these branch records need to
+ * be filtered first so that each individual event get samples they
+ * had requested originally.
+ */
+ if (cpuc->branch_sample_type != event->attr.branch_sample_type) {
+ arm64_filter_branch_records(cpuc, event, &event_records);
+ perf_sample_save_brstack(data, event, &event_records.branch_stack, NULL);
+ return;
+ }
+ perf_sample_save_brstack(data, event, &cpuc->branches->branch_stack, NULL);
+}
+
static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
{
u32 pmovsr;
@@ -844,6 +901,7 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs;
int idx;
+ bool branch_captured = false;
/*
* Get and reset the IRQ flags
@@ -887,6 +945,13 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
if (!armpmu_event_set_period(event))
continue;
+ /*
+ * PMU IRQ should remain asserted until all branch records
+ * are captured and processed into struct perf_sample_data.
+ */
+ if (has_branch_stack(event) && cpu_pmu->has_branch_stack)
+ read_branch_records(cpuc, event, &data, &branch_captured);
+
/*
* Perf event overflow will queue the processing of the event as
* an irq_work which will be taken care of in the handling of
@@ -896,6 +961,8 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
cpu_pmu->disable(event);
}
armv8pmu_start(cpu_pmu);
+ if (cpu_pmu->has_branch_stack)
+ armv8pmu_branch_stack_reset();
return IRQ_HANDLED;
}
@@ -985,6 +1052,40 @@ static int armv8pmu_user_event_idx(struct perf_event *event)
return event->hw.idx;
}
+static bool armv8pmu_branch_stack_init(struct perf_event *event)
+{
+ if (armv8pmu_branch_attr_valid(event)) {
+ /*
+ * If a task gets scheduled out, the current branch records
+ * get saved in the task's context data, which can be later
+ * used to fill in the records upon an event overflow. Let's
+ * enable PERF_ATTACH_TASK_DATA in 'event->attach_state' for
+ * all branch stack sampling perf events.
+ */
+ event->attach_state |= PERF_ATTACH_TASK_DATA;
+ return true;
+ }
+ return false;
+}
+
+static void armv8pmu_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(pmu_ctx->pmu);
+ void *task_ctx = pmu_ctx->task_ctx_data;
+
+ if (armpmu->has_branch_stack) {
+ /* Save branch records in task_ctx on sched out */
+ if (task_ctx && !sched_in) {
+ armv8pmu_branch_save(armpmu, task_ctx);
+ return;
+ }
+
+ /* Reset branch records on sched in */
+ if (sched_in)
+ armv8pmu_branch_stack_reset();
+ }
+}
+
/*
* Add an event filter to a given event.
*/
@@ -1077,6 +1178,9 @@ static void armv8pmu_reset(void *info)
pmcr |= ARMV8_PMU_PMCR_LP;
armv8pmu_pmcr_write(pmcr);
+
+ if (cpu_pmu->has_branch_stack)
+ armv8pmu_branch_stack_reset();
}
static int __armv8_pmuv3_map_event_id(struct arm_pmu *armpmu,
@@ -1229,6 +1333,41 @@ static void __armv8pmu_probe_pmu(void *info)
cpu_pmu->reg_pmmir = read_pmmir();
else
cpu_pmu->reg_pmmir = 0;
+
+ /*
+ * BRBE is being probed on a single cpu for a
+ * given PMU. The remaining cpus, are assumed
+ * to have the exact same BRBE implementation.
+ */
+ armv8pmu_branch_probe(cpu_pmu);
+}
+
+static int branch_records_alloc(struct arm_pmu *armpmu)
+{
+ struct branch_records __percpu *records;
+ int cpu;
+
+ records = alloc_percpu_gfp(struct branch_records, GFP_KERNEL);
+ if (!records)
+ return -ENOMEM;
+
+ /*
+ * percpu memory allocated for 'records' gets completely consumed
+ * here, and never required to be freed up later. So permanently
+ * losing access to this anchor i.e 'records' is acceptable.
+ *
+ * Otherwise this allocation handle would have to be saved up for
+ * free_percpu() release later if required.
+ */
+ for_each_possible_cpu(cpu) {
+ struct pmu_hw_events *events_cpu;
+ struct branch_records *records_cpu;
+
+ events_cpu = per_cpu_ptr(armpmu->hw_events, cpu);
+ records_cpu = per_cpu_ptr(records, cpu);
+ events_cpu->branches = records_cpu;
+ }
+ return 0;
}
static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu)
@@ -1245,7 +1384,21 @@ static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu)
if (ret)
return ret;
- return probe.present ? 0 : -ENODEV;
+ if (!probe.present)
+ return -ENODEV;
+
+ if (cpu_pmu->has_branch_stack) {
+ ret = armv8pmu_task_ctx_cache_alloc(cpu_pmu);
+ if (ret)
+ return ret;
+
+ ret = branch_records_alloc(cpu_pmu);
+ if (ret) {
+ armv8pmu_task_ctx_cache_free(cpu_pmu);
+ return ret;
+ }
+ }
+ return 0;
}
static void armv8pmu_disable_user_access_ipi(void *unused)
@@ -1304,6 +1457,11 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name,
cpu_pmu->set_event_filter = armv8pmu_set_event_filter;
cpu_pmu->pmu.event_idx = armv8pmu_user_event_idx;
+ cpu_pmu->sched_task = armv8pmu_sched_task;
+ cpu_pmu->branch_stack_init = armv8pmu_branch_stack_init;
+ cpu_pmu->branch_stack_add = armv8pmu_branch_stack_add;
+ cpu_pmu->branch_stack_del = armv8pmu_branch_stack_del;
+ cpu_pmu->branch_stack_reset = armv8pmu_branch_stack_reset;
cpu_pmu->name = name;
cpu_pmu->map_event = map_event;
diff --git a/drivers/perf/arm_pmuv3_branch.h b/drivers/perf/arm_pmuv3_branch.h
new file mode 100644
index 000000000000..4142da38cfb5
--- /dev/null
+++ b/drivers/perf/arm_pmuv3_branch.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Branch Record Buffer Extension Helpers.
+ *
+ * Copyright (C) 2022-2023 ARM Limited
+ *
+ * Author: Anshuman Khandual <anshuman.khandual@arm.com>
+ */
+#include <linux/perf/arm_pmu.h>
+
+#ifdef CONFIG_ARM64_BRBE
+void armv8pmu_branch_stack_add(struct perf_event *event, struct pmu_hw_events *cpuc);
+void armv8pmu_branch_stack_del(struct perf_event *event, struct pmu_hw_events *cpuc);
+void armv8pmu_branch_stack_reset(void);
+void armv8pmu_branch_probe(struct arm_pmu *arm_pmu);
+bool armv8pmu_branch_attr_valid(struct perf_event *event);
+void armv8pmu_branch_enable(struct arm_pmu *arm_pmu);
+void armv8pmu_branch_disable(void);
+void armv8pmu_branch_read(struct pmu_hw_events *cpuc,
+ struct perf_event *event);
+void arm64_filter_branch_records(struct pmu_hw_events *cpuc,
+ struct perf_event *event,
+ struct branch_records *event_records);
+void armv8pmu_branch_save(struct arm_pmu *arm_pmu, void *ctx);
+int armv8pmu_task_ctx_cache_alloc(struct arm_pmu *arm_pmu);
+void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu);
+#else
+static inline void armv8pmu_branch_stack_add(struct perf_event *event, struct pmu_hw_events *cpuc)
+{
+}
+
+static inline void armv8pmu_branch_stack_del(struct perf_event *event, struct pmu_hw_events *cpuc)
+{
+}
+
+static inline void armv8pmu_branch_stack_reset(void)
+{
+}
+
+static inline void armv8pmu_branch_probe(struct arm_pmu *arm_pmu)
+{
+}
+
+static inline bool armv8pmu_branch_attr_valid(struct perf_event *event)
+{
+ WARN_ON_ONCE(!has_branch_stack(event));
+ return false;
+}
+
+static inline void armv8pmu_branch_enable(struct arm_pmu *arm_pmu)
+{
+}
+
+static inline void armv8pmu_branch_disable(void)
+{
+}
+
+static inline void armv8pmu_branch_read(struct pmu_hw_events *cpuc,
+ struct perf_event *event)
+{
+ WARN_ON_ONCE(!has_branch_stack(event));
+}
+
+static inline void arm64_filter_branch_records(struct pmu_hw_events *cpuc,
+ struct perf_event *event,
+ struct branch_records *event_records)
+{
+
+}
+
+static inline void armv8pmu_branch_save(struct arm_pmu *arm_pmu, void *ctx)
+{
+}
+
+static inline int armv8pmu_task_ctx_cache_alloc(struct arm_pmu *arm_pmu)
+{
+ return 0;
+}
+
+static inline void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu)
+{
+}
+#endif
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 9eda16dd684e..a8f916aa6823 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -141,6 +141,11 @@ struct arm_pmu {
/* store the PMMIR_EL1 to expose slots */
u64 reg_pmmir;
+#ifdef CONFIG_ARM64_BRBE
+ /* store the BRBIDR0_EL1 capturing attributes */
+ u64 reg_brbidr;
+#endif
+
/* Only to be used by ACPI probing code */
unsigned long acpi_cpuid;
};
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
` (4 preceding siblings ...)
2024-06-13 6:17 ` [PATCH V18 5/9] drivers: perf: arm_pmuv3: Enable branch stack sampling via FEAT_BRBE Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-14 15:23 ` Mark Rutland
2024-06-13 6:17 ` [PATCH V18 7/9] perf: test: Speed up running brstack test on an Arm model Anshuman Khandual
` (2 subsequent siblings)
8 siblings, 1 reply; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, Oliver Upton,
James Morse, kvmarm
Disable the BRBE before we enter the guest, saving the status and enable it
back once we get out of the guest. This avoids capturing branch records in
the guest kernel or userspace, which would be confusing the host samples.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: James Morse <james.morse@arm.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: kvmarm@lists.linux.dev
Cc: linux-arm-kernel@lists.infradead.org
CC: linux-kernel@vger.kernel.org
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
----
Changes in V18:
- Used host_data_ptr() to access host_debug_state.brbcr_el1 register
- Changed DEBUG_STATE_SAVE_BRBE to use BIT(7)
- Reverted back iflags as u8
arch/arm64/include/asm/kvm_host.h | 3 +++
arch/arm64/kvm/debug.c | 5 +++++
arch/arm64/kvm/hyp/nvhe/debug-sr.c | 31 ++++++++++++++++++++++++++++++
3 files changed, 39 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 36b8e97bf49e..db922c10bd2a 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -579,6 +579,7 @@ struct kvm_host_data {
u64 trfcr_el1;
/* Values of trap registers for the host before guest entry. */
u64 mdcr_el2;
+ u64 brbcr_el1;
} host_debug_state;
};
@@ -842,6 +843,8 @@ struct kvm_vcpu_arch {
#define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))
/* Save TRBE context if active */
#define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6))
+/* Save BRBE context if active */
+#define DEBUG_STATE_SAVE_BRBE __vcpu_single_flag(iflags, BIT(7))
/* SVE enabled for host EL0 */
#define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index ce8886122ed3..8fa648943f0f 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -336,10 +336,15 @@ void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu)
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
!(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
+
+ /* Check if we have BRBE implemented and available at the host */
+ if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
+ vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
}
void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
{
vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
+ vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
}
diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
index 53efda0235cf..97e861df1b45 100644
--- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c
+++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
@@ -79,6 +79,32 @@ static void __debug_restore_trace(u64 trfcr_el1)
write_sysreg_el1(trfcr_el1, SYS_TRFCR);
}
+static void __debug_save_brbe(u64 *brbcr_el1)
+{
+ *brbcr_el1 = 0;
+
+ /* Check if the BRBE is enabled */
+ if (!(read_sysreg_el1(SYS_BRBCR) & (BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE)))
+ return;
+
+ /*
+ * Prohibit branch record generation while we are in guest.
+ * Since access to BRBCR_EL1 is trapped, the guest can't
+ * modify the filtering set by the host.
+ */
+ *brbcr_el1 = read_sysreg_el1(SYS_BRBCR);
+ write_sysreg_el1(0, SYS_BRBCR);
+}
+
+static void __debug_restore_brbe(u64 brbcr_el1)
+{
+ if (!brbcr_el1)
+ return;
+
+ /* Restore BRBE controls */
+ write_sysreg_el1(brbcr_el1, SYS_BRBCR);
+}
+
void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
{
/* Disable and flush SPE data generation */
@@ -87,6 +113,9 @@ void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
/* Disable and flush Self-Hosted Trace generation */
if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
__debug_save_trace(host_data_ptr(host_debug_state.trfcr_el1));
+ /* Disable BRBE branch records */
+ if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
+ __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1));
}
void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
@@ -100,6 +129,8 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
__debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1));
if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
__debug_restore_trace(*host_data_ptr(host_debug_state.trfcr_el1));
+ if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
+ __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1));
}
void __debug_switch_to_host(struct kvm_vcpu *vcpu)
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests
2024-06-13 6:17 ` [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests Anshuman Khandual
@ 2024-06-14 15:23 ` Mark Rutland
2024-06-17 6:45 ` Anshuman Khandual
0 siblings, 1 reply; 23+ messages in thread
From: Mark Rutland @ 2024-06-14 15:23 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users, Oliver Upton, James Morse, kvmarm
On Thu, Jun 13, 2024 at 11:47:28AM +0530, Anshuman Khandual wrote:
> Disable the BRBE before we enter the guest, saving the status and enable it
> back once we get out of the guest. This avoids capturing branch records in
> the guest kernel or userspace, which would be confusing the host samples.
It'd be good to explain why we need to do this for nVHE, but not for
VHE. I *think* that you're relying on BRBCR_EL2.EL0HBRE being ignored
when HCR_EL2.TGE == 0, and BRBCR_EL1.E{1,0}BRE being initialized to 0
out-of-reset.
What should a user do if they *want* samples from a guest? Is that
possible to do on other architectures, or do is that always prevented?
Mark.
>
> Cc: Marc Zyngier <maz@kernel.org>
> Cc: Oliver Upton <oliver.upton@linux.dev>
> Cc: James Morse <james.morse@arm.com>
> Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: kvmarm@lists.linux.dev
> Cc: linux-arm-kernel@lists.infradead.org
> CC: linux-kernel@vger.kernel.org
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> ----
> Changes in V18:
>
> - Used host_data_ptr() to access host_debug_state.brbcr_el1 register
> - Changed DEBUG_STATE_SAVE_BRBE to use BIT(7)
> - Reverted back iflags as u8
>
> arch/arm64/include/asm/kvm_host.h | 3 +++
> arch/arm64/kvm/debug.c | 5 +++++
> arch/arm64/kvm/hyp/nvhe/debug-sr.c | 31 ++++++++++++++++++++++++++++++
> 3 files changed, 39 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 36b8e97bf49e..db922c10bd2a 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -579,6 +579,7 @@ struct kvm_host_data {
> u64 trfcr_el1;
> /* Values of trap registers for the host before guest entry. */
> u64 mdcr_el2;
> + u64 brbcr_el1;
> } host_debug_state;
> };
>
> @@ -842,6 +843,8 @@ struct kvm_vcpu_arch {
> #define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))
> /* Save TRBE context if active */
> #define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6))
> +/* Save BRBE context if active */
> +#define DEBUG_STATE_SAVE_BRBE __vcpu_single_flag(iflags, BIT(7))
>
> /* SVE enabled for host EL0 */
> #define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
> index ce8886122ed3..8fa648943f0f 100644
> --- a/arch/arm64/kvm/debug.c
> +++ b/arch/arm64/kvm/debug.c
> @@ -336,10 +336,15 @@ void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu)
> if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
> !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
> vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
> +
> + /* Check if we have BRBE implemented and available at the host */
> + if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
> + vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
> }
>
> void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
> {
> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
> + vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
> }
> diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
> index 53efda0235cf..97e861df1b45 100644
> --- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c
> +++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
> @@ -79,6 +79,32 @@ static void __debug_restore_trace(u64 trfcr_el1)
> write_sysreg_el1(trfcr_el1, SYS_TRFCR);
> }
>
> +static void __debug_save_brbe(u64 *brbcr_el1)
> +{
> + *brbcr_el1 = 0;
> +
> + /* Check if the BRBE is enabled */
> + if (!(read_sysreg_el1(SYS_BRBCR) & (BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE)))
> + return;
> +
> + /*
> + * Prohibit branch record generation while we are in guest.
> + * Since access to BRBCR_EL1 is trapped, the guest can't
> + * modify the filtering set by the host.
> + */
> + *brbcr_el1 = read_sysreg_el1(SYS_BRBCR);
> + write_sysreg_el1(0, SYS_BRBCR);
> +}
> +
> +static void __debug_restore_brbe(u64 brbcr_el1)
> +{
> + if (!brbcr_el1)
> + return;
> +
> + /* Restore BRBE controls */
> + write_sysreg_el1(brbcr_el1, SYS_BRBCR);
> +}
> +
> void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
> {
> /* Disable and flush SPE data generation */
> @@ -87,6 +113,9 @@ void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
> /* Disable and flush Self-Hosted Trace generation */
> if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
> __debug_save_trace(host_data_ptr(host_debug_state.trfcr_el1));
> + /* Disable BRBE branch records */
> + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
> + __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1));
> }
>
> void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
> @@ -100,6 +129,8 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
> __debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1));
> if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
> __debug_restore_trace(*host_data_ptr(host_debug_state.trfcr_el1));
> + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
> + __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1));
> }
>
> void __debug_switch_to_host(struct kvm_vcpu *vcpu)
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests
2024-06-14 15:23 ` Mark Rutland
@ 2024-06-17 6:45 ` Anshuman Khandual
2024-06-17 9:39 ` Mark Rutland
0 siblings, 1 reply; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-17 6:45 UTC (permalink / raw)
To: Mark Rutland
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users, Oliver Upton, James Morse, kvmarm
On 6/14/24 20:53, Mark Rutland wrote:
> On Thu, Jun 13, 2024 at 11:47:28AM +0530, Anshuman Khandual wrote:
>> Disable the BRBE before we enter the guest, saving the status and enable it
>> back once we get out of the guest. This avoids capturing branch records in
>> the guest kernel or userspace, which would be confusing the host samples.
>
> It'd be good to explain why we need to do this for nVHE, but not for
> VHE. I *think* that you're relying on BRBCR_EL2.EL0HBRE being ignored
> when HCR_EL2.TGE == 0, and BRBCR_EL1.E{1,0}BRE being initialized to 0
> out-of-reset.
That's right, there is no possibility for the host and guest BRBE config
to overlap.
>
> What should a user do if they *want* samples from a guest? Is that
That is not supported currently. But in order to enable capturing guest
branch samples from inside the host - BRBCR_EL2 configs need to migrate
into BRBCR_EL1 when the guest runs on the cpu.
> possible to do on other architectures, or do is that always prevented?
I am not sure about other architectures, but for now this falls within
guest support which might be looked into later. But is not the proposed
patch complete in itself without any further guest support ?
>
> Mark.
>
>>
>> Cc: Marc Zyngier <maz@kernel.org>
>> Cc: Oliver Upton <oliver.upton@linux.dev>
>> Cc: James Morse <james.morse@arm.com>
>> Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>> Cc: Will Deacon <will@kernel.org>
>> Cc: kvmarm@lists.linux.dev
>> Cc: linux-arm-kernel@lists.infradead.org
>> CC: linux-kernel@vger.kernel.org
>> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
>> ----
>> Changes in V18:
>>
>> - Used host_data_ptr() to access host_debug_state.brbcr_el1 register
>> - Changed DEBUG_STATE_SAVE_BRBE to use BIT(7)
>> - Reverted back iflags as u8
>>
>> arch/arm64/include/asm/kvm_host.h | 3 +++
>> arch/arm64/kvm/debug.c | 5 +++++
>> arch/arm64/kvm/hyp/nvhe/debug-sr.c | 31 ++++++++++++++++++++++++++++++
>> 3 files changed, 39 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>> index 36b8e97bf49e..db922c10bd2a 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -579,6 +579,7 @@ struct kvm_host_data {
>> u64 trfcr_el1;
>> /* Values of trap registers for the host before guest entry. */
>> u64 mdcr_el2;
>> + u64 brbcr_el1;
>> } host_debug_state;
>> };
>>
>> @@ -842,6 +843,8 @@ struct kvm_vcpu_arch {
>> #define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))
>> /* Save TRBE context if active */
>> #define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6))
>> +/* Save BRBE context if active */
>> +#define DEBUG_STATE_SAVE_BRBE __vcpu_single_flag(iflags, BIT(7))
>>
>> /* SVE enabled for host EL0 */
>> #define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
>> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
>> index ce8886122ed3..8fa648943f0f 100644
>> --- a/arch/arm64/kvm/debug.c
>> +++ b/arch/arm64/kvm/debug.c
>> @@ -336,10 +336,15 @@ void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu)
>> if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
>> !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
>> vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
>> +
>> + /* Check if we have BRBE implemented and available at the host */
>> + if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
>> + vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
>> }
>>
>> void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
>> {
>> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
>> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
>> + vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
>> }
>> diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
>> index 53efda0235cf..97e861df1b45 100644
>> --- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c
>> +++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
>> @@ -79,6 +79,32 @@ static void __debug_restore_trace(u64 trfcr_el1)
>> write_sysreg_el1(trfcr_el1, SYS_TRFCR);
>> }
>>
>> +static void __debug_save_brbe(u64 *brbcr_el1)
>> +{
>> + *brbcr_el1 = 0;
>> +
>> + /* Check if the BRBE is enabled */
>> + if (!(read_sysreg_el1(SYS_BRBCR) & (BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE)))
>> + return;
>> +
>> + /*
>> + * Prohibit branch record generation while we are in guest.
>> + * Since access to BRBCR_EL1 is trapped, the guest can't
>> + * modify the filtering set by the host.
>> + */
>> + *brbcr_el1 = read_sysreg_el1(SYS_BRBCR);
>> + write_sysreg_el1(0, SYS_BRBCR);
>> +}
>> +
>> +static void __debug_restore_brbe(u64 brbcr_el1)
>> +{
>> + if (!brbcr_el1)
>> + return;
>> +
>> + /* Restore BRBE controls */
>> + write_sysreg_el1(brbcr_el1, SYS_BRBCR);
>> +}
>> +
>> void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
>> {
>> /* Disable and flush SPE data generation */
>> @@ -87,6 +113,9 @@ void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
>> /* Disable and flush Self-Hosted Trace generation */
>> if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
>> __debug_save_trace(host_data_ptr(host_debug_state.trfcr_el1));
>> + /* Disable BRBE branch records */
>> + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
>> + __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1));
>> }
>>
>> void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
>> @@ -100,6 +129,8 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
>> __debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1));
>> if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
>> __debug_restore_trace(*host_data_ptr(host_debug_state.trfcr_el1));
>> + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
>> + __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1));
>> }
>>
>> void __debug_switch_to_host(struct kvm_vcpu *vcpu)
>> --
>> 2.25.1
>>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests
2024-06-17 6:45 ` Anshuman Khandual
@ 2024-06-17 9:39 ` Mark Rutland
2024-06-20 4:22 ` Anshuman Khandual
0 siblings, 1 reply; 23+ messages in thread
From: Mark Rutland @ 2024-06-17 9:39 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users, Oliver Upton, James Morse, kvmarm
On Mon, Jun 17, 2024 at 12:15:15PM +0530, Anshuman Khandual wrote:
>
>
> On 6/14/24 20:53, Mark Rutland wrote:
> > On Thu, Jun 13, 2024 at 11:47:28AM +0530, Anshuman Khandual wrote:
> >> Disable the BRBE before we enter the guest, saving the status and enable it
> >> back once we get out of the guest. This avoids capturing branch records in
> >> the guest kernel or userspace, which would be confusing the host samples.
> >
> > It'd be good to explain why we need to do this for nVHE, but not for
> > VHE. I *think* that you're relying on BRBCR_EL2.EL0HBRE being ignored
> > when HCR_EL2.TGE == 0, and BRBCR_EL1.E{1,0}BRE being initialized to 0
> > out-of-reset.
>
> That's right, there is no possibility for the host and guest BRBE config
> to overlap.
>
> > What should a user do if they *want* samples from a guest? Is that
>
> That is not supported currently. But in order to enable capturing guest
> branch samples from inside the host - BRBCR_EL2 configs need to migrate
> into BRBCR_EL1 when the guest runs on the cpu.
>
> > possible to do on other architectures, or do is that always prevented?
>
> I am not sure about other architectures, but for now this falls within
> guest support which might be looked into later. But is not the proposed
> patch complete in itself without any further guest support ?
My concern here is ABI rather than actual support.
It's not clear to me how this works across architectures, and we should
have some idea of how this would work (e.g. if we're going to require
new ABI or not), so that we don't have to break ABI later on.
Mark.
>
> >
> > Mark.
> >
> >>
> >> Cc: Marc Zyngier <maz@kernel.org>
> >> Cc: Oliver Upton <oliver.upton@linux.dev>
> >> Cc: James Morse <james.morse@arm.com>
> >> Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
> >> Cc: Catalin Marinas <catalin.marinas@arm.com>
> >> Cc: Will Deacon <will@kernel.org>
> >> Cc: kvmarm@lists.linux.dev
> >> Cc: linux-arm-kernel@lists.infradead.org
> >> CC: linux-kernel@vger.kernel.org
> >> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> >> ----
> >> Changes in V18:
> >>
> >> - Used host_data_ptr() to access host_debug_state.brbcr_el1 register
> >> - Changed DEBUG_STATE_SAVE_BRBE to use BIT(7)
> >> - Reverted back iflags as u8
> >>
> >> arch/arm64/include/asm/kvm_host.h | 3 +++
> >> arch/arm64/kvm/debug.c | 5 +++++
> >> arch/arm64/kvm/hyp/nvhe/debug-sr.c | 31 ++++++++++++++++++++++++++++++
> >> 3 files changed, 39 insertions(+)
> >>
> >> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> >> index 36b8e97bf49e..db922c10bd2a 100644
> >> --- a/arch/arm64/include/asm/kvm_host.h
> >> +++ b/arch/arm64/include/asm/kvm_host.h
> >> @@ -579,6 +579,7 @@ struct kvm_host_data {
> >> u64 trfcr_el1;
> >> /* Values of trap registers for the host before guest entry. */
> >> u64 mdcr_el2;
> >> + u64 brbcr_el1;
> >> } host_debug_state;
> >> };
> >>
> >> @@ -842,6 +843,8 @@ struct kvm_vcpu_arch {
> >> #define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))
> >> /* Save TRBE context if active */
> >> #define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6))
> >> +/* Save BRBE context if active */
> >> +#define DEBUG_STATE_SAVE_BRBE __vcpu_single_flag(iflags, BIT(7))
> >>
> >> /* SVE enabled for host EL0 */
> >> #define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
> >> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
> >> index ce8886122ed3..8fa648943f0f 100644
> >> --- a/arch/arm64/kvm/debug.c
> >> +++ b/arch/arm64/kvm/debug.c
> >> @@ -336,10 +336,15 @@ void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu)
> >> if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
> >> !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
> >> vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
> >> +
> >> + /* Check if we have BRBE implemented and available at the host */
> >> + if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
> >> + vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
> >> }
> >>
> >> void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
> >> {
> >> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
> >> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
> >> + vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_BRBE);
> >> }
> >> diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
> >> index 53efda0235cf..97e861df1b45 100644
> >> --- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c
> >> +++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c
> >> @@ -79,6 +79,32 @@ static void __debug_restore_trace(u64 trfcr_el1)
> >> write_sysreg_el1(trfcr_el1, SYS_TRFCR);
> >> }
> >>
> >> +static void __debug_save_brbe(u64 *brbcr_el1)
> >> +{
> >> + *brbcr_el1 = 0;
> >> +
> >> + /* Check if the BRBE is enabled */
> >> + if (!(read_sysreg_el1(SYS_BRBCR) & (BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE)))
> >> + return;
> >> +
> >> + /*
> >> + * Prohibit branch record generation while we are in guest.
> >> + * Since access to BRBCR_EL1 is trapped, the guest can't
> >> + * modify the filtering set by the host.
> >> + */
> >> + *brbcr_el1 = read_sysreg_el1(SYS_BRBCR);
> >> + write_sysreg_el1(0, SYS_BRBCR);
> >> +}
> >> +
> >> +static void __debug_restore_brbe(u64 brbcr_el1)
> >> +{
> >> + if (!brbcr_el1)
> >> + return;
> >> +
> >> + /* Restore BRBE controls */
> >> + write_sysreg_el1(brbcr_el1, SYS_BRBCR);
> >> +}
> >> +
> >> void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
> >> {
> >> /* Disable and flush SPE data generation */
> >> @@ -87,6 +113,9 @@ void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
> >> /* Disable and flush Self-Hosted Trace generation */
> >> if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
> >> __debug_save_trace(host_data_ptr(host_debug_state.trfcr_el1));
> >> + /* Disable BRBE branch records */
> >> + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
> >> + __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1));
> >> }
> >>
> >> void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
> >> @@ -100,6 +129,8 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
> >> __debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1));
> >> if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
> >> __debug_restore_trace(*host_data_ptr(host_debug_state.trfcr_el1));
> >> + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_BRBE))
> >> + __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1));
> >> }
> >>
> >> void __debug_switch_to_host(struct kvm_vcpu *vcpu)
> >> --
> >> 2.25.1
> >>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests
2024-06-17 9:39 ` Mark Rutland
@ 2024-06-20 4:22 ` Anshuman Khandual
2024-06-21 13:12 ` Mark Rutland
0 siblings, 1 reply; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-20 4:22 UTC (permalink / raw)
To: Mark Rutland
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users, Oliver Upton, James Morse, kvmarm
On 6/17/24 15:09, Mark Rutland wrote:
> On Mon, Jun 17, 2024 at 12:15:15PM +0530, Anshuman Khandual wrote:
>>
>>
>> On 6/14/24 20:53, Mark Rutland wrote:
>>> On Thu, Jun 13, 2024 at 11:47:28AM +0530, Anshuman Khandual wrote:
>>>> Disable the BRBE before we enter the guest, saving the status and enable it
>>>> back once we get out of the guest. This avoids capturing branch records in
>>>> the guest kernel or userspace, which would be confusing the host samples.
>>>
>>> It'd be good to explain why we need to do this for nVHE, but not for
>>> VHE. I *think* that you're relying on BRBCR_EL2.EL0HBRE being ignored
>>> when HCR_EL2.TGE == 0, and BRBCR_EL1.E{1,0}BRE being initialized to 0
>>> out-of-reset.
>>
>> That's right, there is no possibility for the host and guest BRBE config
>> to overlap.
>>
>>> What should a user do if they *want* samples from a guest? Is that
>>
>> That is not supported currently. But in order to enable capturing guest
>> branch samples from inside the host - BRBCR_EL2 configs need to migrate
>> into BRBCR_EL1 when the guest runs on the cpu.
>>
>>> possible to do on other architectures, or do is that always prevented?
>>
>> I am not sure about other architectures, but for now this falls within
>> guest support which might be looked into later. But is not the proposed
>> patch complete in itself without any further guest support ?
>
> My concern here is ABI rather than actual support.
I am trying to understand how this is an ABI problem. Because perf debug
tools could be described as - a best effort based sample collection. All
samples that could be collected for a given perf_event_attr request might
change if the underlying assumptions change later on. AFAICT semantics of
expectations for a given attribute request is not a hard ABI requirement.
>
> It's not clear to me how this works across architectures, and we should
> have some idea of how this would work (e.g. if we're going to require
> new ABI or not), so that we don't have to break ABI later on.
BRBE HW does not have any guest filter in itself, unless BRBCR_EL2 gets
migrated across BRBCR_EL1 during guest transition, guest branch records
would not be captured.
event->attr.exclude_guest = 0 could have been denied during armpmu_add()
for preventing events with guest branch sample requests being scheduled
on the PMU. But it turns out to be not a very reliable parameter in that
sense as well.
event->attr.exclude_guest = 0 remains clear even for a standard session.
./perf record -e instructions:k -j any_call,save_type ls
perf tools will need some changes in order to avoid the above scenarios
as a default behaviour which would not be desirable as well.
These semantics could be worked out later on when BRBE guest support gets
included, and the current proposal would not prevent any potential future
changes in this regard.
>
> Mark.
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests
2024-06-20 4:22 ` Anshuman Khandual
@ 2024-06-21 13:12 ` Mark Rutland
0 siblings, 0 replies; 23+ messages in thread
From: Mark Rutland @ 2024-06-21 13:12 UTC (permalink / raw)
To: Anshuman Khandual
Cc: linux-arm-kernel, linux-kernel, will, catalin.marinas, Mark Brown,
James Clark, Rob Herring, Marc Zyngier, Suzuki Poulose,
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
linux-perf-users, Oliver Upton, James Morse, kvmarm
On Thu, Jun 20, 2024 at 09:52:05AM +0530, Anshuman Khandual wrote:
> On 6/17/24 15:09, Mark Rutland wrote:
> > On Mon, Jun 17, 2024 at 12:15:15PM +0530, Anshuman Khandual wrote:
> >> On 6/14/24 20:53, Mark Rutland wrote:
> >>> On Thu, Jun 13, 2024 at 11:47:28AM +0530, Anshuman Khandual wrote:
> >>>> Disable the BRBE before we enter the guest, saving the status and enable it
> >>>> back once we get out of the guest. This avoids capturing branch records in
> >>>> the guest kernel or userspace, which would be confusing the host samples.
> >>>
> >>> It'd be good to explain why we need to do this for nVHE, but not for
> >>> VHE. I *think* that you're relying on BRBCR_EL2.EL0HBRE being ignored
> >>> when HCR_EL2.TGE == 0, and BRBCR_EL1.E{1,0}BRE being initialized to 0
> >>> out-of-reset.
> >>
> >> That's right, there is no possibility for the host and guest BRBE config
> >> to overlap.
> >>
> >>> What should a user do if they *want* samples from a guest? Is that
> >>
> >> That is not supported currently. But in order to enable capturing guest
> >> branch samples from inside the host - BRBCR_EL2 configs need to migrate
> >> into BRBCR_EL1 when the guest runs on the cpu.
> >>
> >>> possible to do on other architectures, or do is that always prevented?
> >>
> >> I am not sure about other architectures, but for now this falls within
> >> guest support which might be looked into later. But is not the proposed
> >> patch complete in itself without any further guest support ?
> >
> > My concern here is ABI rather than actual support.
> I am trying to understand how this is an ABI problem. Because perf debug
> tools could be described as - a best effort based sample collection. All
> samples that could be collected for a given perf_event_attr request might
> change if the underlying assumptions change later on. AFAICT semantics of
> expectations for a given attribute request is not a hard ABI requirement.
The ABI requirements are certainly unclear, but people get *very* upset
when behaviour changes, so I think we need to have some certainty that
we're not backing ourselves into a corner where we have to make
substantial behavioural changes later.
Surely we can figure out how this works on other architectures today?
There's a substantial argument for aligning with x86, so can we figure
out under which conditions x86 would provide guest samples? e.g. is that
always, never, or when certain attr options are configured?
> > It's not clear to me how this works across architectures, and we should
> > have some idea of how this would work (e.g. if we're going to require
> > new ABI or not), so that we don't have to break ABI later on.
>
> BRBE HW does not have any guest filter in itself, unless BRBCR_EL2 gets
> migrated across BRBCR_EL1 during guest transition, guest branch records
> would not be captured.
>
> event->attr.exclude_guest = 0 could have been denied during armpmu_add()
> for preventing events with guest branch sample requests being scheduled
> on the PMU. But it turns out to be not a very reliable parameter in that
> sense as well.
>
> event->attr.exclude_guest = 0 remains clear even for a standard session.
>
> ./perf record -e instructions:k -j any_call,save_type ls
>
> perf tools will need some changes in order to avoid the above scenarios
> as a default behaviour which would not be desirable as well.
If we're liable to need perf tool changes, then we *definitely* need to
understand this better.
> These semantics could be worked out later on when BRBE guest support gets
> included, and the current proposal would not prevent any potential future
> changes in this regard.
That depends entirely on what changes we'd expect would be necessary in
the perf tools. We need to be certain that we don't enable some use case
that subseuqent changes break.
Mark.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH V18 7/9] perf: test: Speed up running brstack test on an Arm model
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
` (5 preceding siblings ...)
2024-06-13 6:17 ` [PATCH V18 6/9] KVM: arm64: nvhe: Disable branch generation in nVHE guests Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 8/9] perf: test: Remove empty lines from branch filter test output Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 9/9] perf: test: Extend branch stack sampling test for Arm64 BRBE Anshuman Khandual
8 siblings, 0 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users
From: James Clark <james.clark@arm.com>
The test runs quite slowly in the model, so replace "xargs -n1" with
"tr ' ' '\n'" which does the same thing but in single digit minutes
instead of double digit minutes.
Also reduce the number of loops in the test application. Unfortunately
this causes intermittent failures on x86, presumably because the
sampling interval is too big to pickup any loops, so keep it the same
there.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: linux-perf-users@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: James Clark <james.clark@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
tools/perf/tests/shell/test_brstack.sh | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh
index 5f14d0cb013f..5ea64d0c4a6f 100755
--- a/tools/perf/tests/shell/test_brstack.sh
+++ b/tools/perf/tests/shell/test_brstack.sh
@@ -18,7 +18,6 @@ fi
skip_test_missing_symbol brstack_bench
TMPDIR=$(mktemp -d /tmp/__perf_test.program.XXXXX)
-TESTPROG="perf test -w brstack"
cleanup() {
rm -rf $TMPDIR
@@ -26,11 +25,21 @@ cleanup() {
trap cleanup EXIT TERM INT
+is_arm64() {
+ uname -m | grep -q aarch64
+}
+
+if is_arm64; then
+ TESTPROG="perf test -w brstack 5000"
+else
+ TESTPROG="perf test -w brstack"
+fi
+
test_user_branches() {
echo "Testing user branch stack sampling"
perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u -- ${TESTPROG} > /dev/null 2>&1
- perf script -i $TMPDIR/perf.data --fields brstacksym | xargs -n1 > $TMPDIR/perf.script
+ perf script -i $TMPDIR/perf.data --fields brstacksym | tr ' ' '\n' > $TMPDIR/perf.script
# example of branch entries:
# brstack_foo+0x14/brstack_bar+0x40/P/-/-/0/CALL
@@ -59,7 +68,7 @@ test_filter() {
echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)"
perf record -o $TMPDIR/perf.data --branch-filter $test_filter_filter,save_type,u -- ${TESTPROG} > /dev/null 2>&1
- perf script -i $TMPDIR/perf.data --fields brstack | xargs -n1 > $TMPDIR/perf.script
+ perf script -i $TMPDIR/perf.data --fields brstack | tr ' ' '\n' > $TMPDIR/perf.script
# fail if we find any branch type that doesn't match any of the expected ones
# also consider UNKNOWN branch types (-)
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V18 8/9] perf: test: Remove empty lines from branch filter test output
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
` (6 preceding siblings ...)
2024-06-13 6:17 ` [PATCH V18 7/9] perf: test: Speed up running brstack test on an Arm model Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
2024-06-13 6:17 ` [PATCH V18 9/9] perf: test: Extend branch stack sampling test for Arm64 BRBE Anshuman Khandual
8 siblings, 0 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users
From: James Clark <james.clark@arm.com>
In the perf script command, spaces are turned into newlines. But when
there is a double space this results in empty lines which fail the
following inverse grep test, so strip the empty lines.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: linux-perf-users@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: James Clark <james.clark@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
tools/perf/tests/shell/test_brstack.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh
index 5ea64d0c4a6f..928790f35747 100755
--- a/tools/perf/tests/shell/test_brstack.sh
+++ b/tools/perf/tests/shell/test_brstack.sh
@@ -68,7 +68,7 @@ test_filter() {
echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)"
perf record -o $TMPDIR/perf.data --branch-filter $test_filter_filter,save_type,u -- ${TESTPROG} > /dev/null 2>&1
- perf script -i $TMPDIR/perf.data --fields brstack | tr ' ' '\n' > $TMPDIR/perf.script
+ perf script -i $TMPDIR/perf.data --fields brstack | tr ' ' '\n' | sed '/^[[:space:]]*$/d' > $TMPDIR/perf.script
# fail if we find any branch type that doesn't match any of the expected ones
# also consider UNKNOWN branch types (-)
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH V18 9/9] perf: test: Extend branch stack sampling test for Arm64 BRBE
2024-06-13 6:17 [PATCH V18 0/9] arm64/perf: Enable branch stack sampling Anshuman Khandual
` (7 preceding siblings ...)
2024-06-13 6:17 ` [PATCH V18 8/9] perf: test: Remove empty lines from branch filter test output Anshuman Khandual
@ 2024-06-13 6:17 ` Anshuman Khandual
8 siblings, 0 replies; 23+ messages in thread
From: Anshuman Khandual @ 2024-06-13 6:17 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, will, catalin.marinas,
mark.rutland
Cc: Anshuman Khandual, Mark Brown, James Clark, Rob Herring,
Marc Zyngier, Suzuki Poulose, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-perf-users, German Gomez
From: James Clark <james.clark@arm.com>
Add Arm64 BRBE-specific testing to the existing branch stack sampling test.
The test currently passes on the Arm FVP RevC model, but no hardware has
been tested yet.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: linux-perf-users@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Co-developed-by: German Gomez <german.gomez@arm.com>
Signed-off-by: German Gomez <german.gomez@arm.com>
Signed-off-by: James Clark <james.clark@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/shell/test_brstack.sh | 42 ++++++++++++++++++++++++--
tools/perf/tests/tests.h | 1 +
tools/perf/tests/workloads/Build | 2 ++
tools/perf/tests/workloads/traploop.c | 39 ++++++++++++++++++++++++
5 files changed, 82 insertions(+), 3 deletions(-)
create mode 100644 tools/perf/tests/workloads/traploop.c
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index c3d84b67ca8e..471adbf5fa26 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -152,6 +152,7 @@ static struct test_workload *workloads[] = {
&workload__sqrtloop,
&workload__brstack,
&workload__datasym,
+ &workload__traploop
};
static int num_subtests(const struct test_suite *t)
diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh
index 928790f35747..6a4069c930e8 100755
--- a/tools/perf/tests/shell/test_brstack.sh
+++ b/tools/perf/tests/shell/test_brstack.sh
@@ -53,12 +53,43 @@ test_user_branches() {
grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bench\+[^ ]*/RET/.*$" $TMPDIR/perf.script
grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bench\+[^ ]*/COND/.*$" $TMPDIR/perf.script
grep -E -m1 "^brstack\+[^ ]*/brstack\+[^ ]*/UNCOND/.*$" $TMPDIR/perf.script
+
+ if is_arm64; then
+ # in arm64 with BRBE, we get IRQ entries that correspond
+ # to any point in the process
+ grep -m1 "/IRQ/" $TMPDIR/perf.script
+ fi
set +x
# some branch types are still not being tested:
# IND COND_CALL COND_RET SYSCALL SYSRET IRQ SERROR NO_TX
}
+test_arm64_trap_eret_branches() {
+ echo "Testing trap & eret branches (arm64 brbe)"
+ perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u -- \
+ perf test -w traploop 250
+ perf script -i $TMPDIR/perf.data --fields brstacksym | tr ' ' '\n' > $TMPDIR/perf.script
+ set -x
+ # BRBINF<n>.TYPE == TRAP are mapped to PERF_BR_SYSCALL by the BRBE driver
+ grep -E -m1 "^trap_bench\+[^ ]*/\[unknown\][^ ]*/SYSCALL/" $TMPDIR/perf.script
+ grep -E -m1 "^\[unknown\][^ ]*/trap_bench\+[^ ]*/ERET/" $TMPDIR/perf.script
+ set +x
+}
+
+test_arm64_kernel_branches() {
+ echo "Testing kernel branches (arm64 brbe)"
+ # skip if perf doesn't have enough privileges
+ if ! perf record --branch-filter any,k -o- -- true > /dev/null; then
+ echo "[skipped: not enough privileges]"
+ return 0
+ fi
+ perf record -o $TMPDIR/perf.data --branch-filter any,k -- uname -a
+ perf script -i $TMPDIR/perf.data --fields brstack | tr ' ' '\n' > $TMPDIR/perf.script
+ grep -E -m1 "0xffff[0-9a-f]{12}" $TMPDIR/perf.script
+ ! egrep -E -m1 "0x0000[0-9a-f]{12}" $TMPDIR/perf.script
+}
+
# first argument <arg0> is the argument passed to "--branch-stack <arg0>,save_type,u"
# second argument are the expected branch types for the given filter
test_filter() {
@@ -81,11 +112,16 @@ set -e
test_user_branches
-test_filter "any_call" "CALL|IND_CALL|COND_CALL|SYSCALL|IRQ"
+if is_arm64; then
+ test_arm64_trap_eret_branches
+ test_arm64_kernel_branches
+fi
+
+test_filter "any_call" "CALL|IND_CALL|COND_CALL|SYSCALL|IRQ|FAULT_DATA|FAULT_INST"
test_filter "call" "CALL|SYSCALL"
test_filter "cond" "COND"
test_filter "any_ret" "RET|COND_RET|SYSRET|ERET"
test_filter "call,cond" "CALL|SYSCALL|COND"
-test_filter "any_call,cond" "CALL|IND_CALL|COND_CALL|IRQ|SYSCALL|COND"
-test_filter "cond,any_call,any_ret" "COND|CALL|IND_CALL|COND_CALL|SYSCALL|IRQ|RET|COND_RET|SYSRET|ERET"
+test_filter "any_call,cond" "CALL|IND_CALL|COND_CALL|IRQ|SYSCALL|COND|FAULT_DATA|FAULT_INST"
+test_filter "cond,any_call,any_ret" "COND|CALL|IND_CALL|COND_CALL|SYSCALL|IRQ|RET|COND_RET|SYSRET|ERET|FAULT_DATA|FAULT_INST"
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 3aa7701ee0e9..e4c677adccb6 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -205,6 +205,7 @@ DECLARE_WORKLOAD(leafloop);
DECLARE_WORKLOAD(sqrtloop);
DECLARE_WORKLOAD(brstack);
DECLARE_WORKLOAD(datasym);
+DECLARE_WORKLOAD(traploop);
extern const char *dso_to_test;
extern const char *test_objdump_path;
diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
index a1f34d5861e3..a9dc93d8468b 100644
--- a/tools/perf/tests/workloads/Build
+++ b/tools/perf/tests/workloads/Build
@@ -6,8 +6,10 @@ perf-y += leafloop.o
perf-y += sqrtloop.o
perf-y += brstack.o
perf-y += datasym.o
+perf-y += traploop.o
CFLAGS_sqrtloop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_leafloop.o = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE
CFLAGS_brstack.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_datasym.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
+CFLAGS_traploop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
diff --git a/tools/perf/tests/workloads/traploop.c b/tools/perf/tests/workloads/traploop.c
new file mode 100644
index 000000000000..7dac94897e49
--- /dev/null
+++ b/tools/perf/tests/workloads/traploop.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdlib.h>
+#include "../tests.h"
+
+#define BENCH_RUNS 999999
+
+static volatile int cnt;
+
+#ifdef __aarch64__
+static void trap_bench(void)
+{
+ unsigned long val;
+
+ asm("mrs %0, ID_AA64ISAR0_EL1" : "=r" (val)); /* TRAP + ERET */
+}
+#else
+static void trap_bench(void)
+{
+
+}
+#endif
+
+static int traploop(int argc, const char **argv)
+{
+ int num_loops = BENCH_RUNS;
+
+ if (argc > 0)
+ num_loops = atoi(argv[0]);
+
+ while (1) {
+ if ((cnt++) > num_loops)
+ break;
+
+ trap_bench();
+ }
+ return 0;
+}
+
+DEFINE_WORKLOAD(traploop);
--
2.25.1
^ permalink raw reply related [flat|nested] 23+ messages in thread