Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
53648ed3
Commit
53648ed3
authored
Apr 08, 2021
by
Marc Zyngier
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'coresight/next-ETE-TRBE' into kvmarm-master/next
Signed-off-by:
Marc Zyngier
<
maz@kernel.org
>
parents
1e28eed1
4fb13790
Changes
30
Show whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
2055 additions
and
77 deletions
+2055
-77
Documentation/ABI/testing/sysfs-bus-coresight-devices-trbe
Documentation/ABI/testing/sysfs-bus-coresight-devices-trbe
+14
-0
Documentation/devicetree/bindings/arm/ete.yaml
Documentation/devicetree/bindings/arm/ete.yaml
+75
-0
Documentation/devicetree/bindings/arm/trbe.yaml
Documentation/devicetree/bindings/arm/trbe.yaml
+49
-0
Documentation/trace/coresight/coresight-trbe.rst
Documentation/trace/coresight/coresight-trbe.rst
+38
-0
MAINTAINERS
MAINTAINERS
+2
-0
arch/arm64/include/asm/barrier.h
arch/arm64/include/asm/barrier.h
+1
-0
arch/arm64/include/asm/el2_setup.h
arch/arm64/include/asm/el2_setup.h
+13
-0
arch/arm64/include/asm/kvm_arm.h
arch/arm64/include/asm/kvm_arm.h
+3
-0
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_host.h
+8
-0
arch/arm64/include/asm/sysreg.h
arch/arm64/include/asm/sysreg.h
+50
-0
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/cpufeature.c
+0
-1
arch/arm64/kernel/hyp-stub.S
arch/arm64/kernel/hyp-stub.S
+2
-1
arch/arm64/kvm/arm.c
arch/arm64/kvm/arm.c
+2
-0
arch/arm64/kvm/debug.c
arch/arm64/kvm/debug.c
+33
-2
arch/arm64/kvm/hyp/nvhe/debug-sr.c
arch/arm64/kvm/hyp/nvhe/debug-sr.c
+43
-13
arch/arm64/kvm/hyp/nvhe/switch.c
arch/arm64/kvm/hyp/nvhe/switch.c
+1
-0
arch/arm64/kvm/sys_regs.c
arch/arm64/kvm/sys_regs.c
+1
-0
drivers/hwtracing/coresight/Kconfig
drivers/hwtracing/coresight/Kconfig
+19
-5
drivers/hwtracing/coresight/Makefile
drivers/hwtracing/coresight/Makefile
+1
-0
drivers/hwtracing/coresight/coresight-core.c
drivers/hwtracing/coresight/coresight-core.c
+27
-2
drivers/hwtracing/coresight/coresight-etm-perf.c
drivers/hwtracing/coresight/coresight-etm-perf.c
+106
-13
drivers/hwtracing/coresight/coresight-etm4x-core.c
drivers/hwtracing/coresight/coresight-etm4x-core.c
+137
-24
drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+16
-3
drivers/hwtracing/coresight/coresight-etm4x.h
drivers/hwtracing/coresight/coresight-etm4x.h
+74
-9
drivers/hwtracing/coresight/coresight-platform.c
drivers/hwtracing/coresight/coresight-platform.c
+6
-0
drivers/hwtracing/coresight/coresight-priv.h
drivers/hwtracing/coresight/coresight-priv.h
+3
-0
drivers/hwtracing/coresight/coresight-trbe.c
drivers/hwtracing/coresight/coresight-trbe.c
+1157
-0
drivers/hwtracing/coresight/coresight-trbe.h
drivers/hwtracing/coresight/coresight-trbe.h
+152
-0
include/linux/coresight.h
include/linux/coresight.h
+13
-0
include/uapi/linux/perf_event.h
include/uapi/linux/perf_event.h
+9
-4
No files found.
Documentation/ABI/testing/sysfs-bus-coresight-devices-trbe
0 → 100644
View file @
53648ed3
What: /sys/bus/coresight/devices/trbe<cpu>/align
Date: March 2021
KernelVersion: 5.13
Contact: Anshuman Khandual <anshuman.khandual@arm.com>
Description: (Read) Shows the TRBE write pointer alignment. This value
is fetched from the TRBIDR register.
What: /sys/bus/coresight/devices/trbe<cpu>/flag
Date: March 2021
KernelVersion: 5.13
Contact: Anshuman Khandual <anshuman.khandual@arm.com>
Description: (Read) Shows if TRBE updates in the memory are with access
and dirty flag updates as well. This value is fetched from
the TRBIDR register.
Documentation/devicetree/bindings/arm/ete.yaml
0 → 100644
View file @
53648ed3
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
# Copyright 2021, Arm Ltd
%YAML
1.2
---
$id
:
"
http://devicetree.org/schemas/arm/ete.yaml#"
$schema
:
"
http://devicetree.org/meta-schemas/core.yaml#"
title
:
ARM Embedded Trace Extensions
maintainers
:
-
Suzuki K Poulose <suzuki.poulose@arm.com>
-
Mathieu Poirier <mathieu.poirier@linaro.org>
description
:
|
Arm Embedded Trace Extension(ETE) is a per CPU trace component that
allows tracing the CPU execution. It overlaps with the CoreSight ETMv4
architecture and has extended support for future architecture changes.
The trace generated by the ETE could be stored via legacy CoreSight
components (e.g, TMC-ETR) or other means (e.g, using a per CPU buffer
Arm Trace Buffer Extension (TRBE)). Since the ETE can be connected to
legacy CoreSight components, a node must be listed per instance, along
with any optional connection graph as per the coresight bindings.
See bindings/arm/coresight.txt.
properties
:
$nodename
:
pattern
:
"
^ete([0-9a-f]+)$"
compatible
:
items
:
-
const
:
arm,embedded-trace-extension
cpu
:
description
:
|
Handle to the cpu this ETE is bound to.
$ref
:
/schemas/types.yaml#/definitions/phandle
out-ports
:
description
:
|
Output connections from the ETE to legacy CoreSight trace bus.
$ref
:
/schemas/graph.yaml#/properties/ports
properties
:
port
:
description
:
Output connection from the ETE to legacy CoreSight Trace bus.
$ref
:
/schemas/graph.yaml#/properties/port
required
:
-
compatible
-
cpu
additionalProperties
:
false
examples
:
# An ETE node without legacy CoreSight connections
-
|
ete0 {
compatible = "arm,embedded-trace-extension";
cpu = <&cpu_0>;
};
# An ETE node with legacy CoreSight connections
-
|
ete1 {
compatible = "arm,embedded-trace-extension";
cpu = <&cpu_1>;
out-ports { /* legacy coresight connection */
port {
ete1_out_port: endpoint {
remote-endpoint = <&funnel_in_port0>;
};
};
};
};
...
Documentation/devicetree/bindings/arm/trbe.yaml
0 → 100644
View file @
53648ed3
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
# Copyright 2021, Arm Ltd
%YAML
1.2
---
$id
:
"
http://devicetree.org/schemas/arm/trbe.yaml#"
$schema
:
"
http://devicetree.org/meta-schemas/core.yaml#"
title
:
ARM Trace Buffer Extensions
maintainers
:
-
Anshuman Khandual <anshuman.khandual@arm.com>
description
:
|
Arm Trace Buffer Extension (TRBE) is a per CPU component
for storing trace generated on the CPU to memory. It is
accessed via CPU system registers. The software can verify
if it is permitted to use the component by checking the
TRBIDR register.
properties
:
$nodename
:
const
:
"
trbe"
compatible
:
items
:
-
const
:
arm,trace-buffer-extension
interrupts
:
description
:
|
Exactly 1 PPI must be listed. For heterogeneous systems where
TRBE is only supported on a subset of the CPUs, please consult
the arm,gic-v3 binding for details on describing a PPI partition.
maxItems
:
1
required
:
-
compatible
-
interrupts
additionalProperties
:
false
examples
:
-
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
trbe {
compatible = "arm,trace-buffer-extension";
interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
};
...
Documentation/trace/coresight/coresight-trbe.rst
0 → 100644
View file @
53648ed3
.. SPDX-License-Identifier: GPL-2.0
==============================
Trace Buffer Extension (TRBE).
==============================
:Author: Anshuman Khandual <anshuman.khandual@arm.com>
:Date: November 2020
Hardware Description
--------------------
Trace Buffer Extension (TRBE) is a percpu hardware which captures in system
memory, CPU traces generated from a corresponding percpu tracing unit. This
gets plugged in as a coresight sink device because the corresponding trace
generators (ETE), are plugged in as source device.
The TRBE is not compliant to CoreSight architecture specifications, but is
driven via the CoreSight driver framework to support the ETE (which is
CoreSight compliant) integration.
Sysfs files and directories
---------------------------
The TRBE devices appear on the existing coresight bus alongside the other
coresight devices::
>$ ls /sys/bus/coresight/devices
trbe0 trbe1 trbe2 trbe3
The ``trbe<N>`` named TRBEs are associated with a CPU.::
>$ ls /sys/bus/coresight/devices/trbe0/
align flag
*Key file items are:-*
* ``align``: TRBE write pointer alignment
* ``flag``: TRBE updates memory with access and dirty flags
MAINTAINERS
View file @
53648ed3
...
...
@@ -1761,6 +1761,8 @@ F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
F: Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt
F: Documentation/devicetree/bindings/arm/coresight-cti.yaml
F: Documentation/devicetree/bindings/arm/coresight.txt
F: Documentation/devicetree/bindings/arm/ete.yaml
F: Documentation/devicetree/bindings/arm/trbe.yaml
F: Documentation/trace/coresight/*
F: drivers/hwtracing/coresight/*
F: include/dt-bindings/arm/coresight-cti-dt.h
...
...
arch/arm64/include/asm/barrier.h
View file @
53648ed3
...
...
@@ -23,6 +23,7 @@
#define dsb(opt) asm volatile("dsb " #opt : : : "memory")
#define psb_csync() asm volatile("hint #17" : : : "memory")
#define tsb_csync() asm volatile("hint #18" : : : "memory")
#define csdb() asm volatile("hint #20" : : : "memory")
#define spec_bar() asm volatile(ALTERNATIVE("dsb nsh\nisb\n", \
...
...
arch/arm64/include/asm/el2_setup.h
View file @
53648ed3
...
...
@@ -65,6 +65,19 @@
// use EL1&0 translation.
.
Lskip_spe_
\@
:
/* Trace buffer */
ubfx
x0
,
x1
,
#
ID_AA64DFR0_TRBE_SHIFT
,
#
4
cbz
x0
,
.
Lskip_trace_
\@
// Skip if TraceBuffer is not present
mrs_s
x0
,
SYS_TRBIDR_EL1
and
x0
,
x0
,
TRBIDR_PROG
cbnz
x0
,
.
Lskip_trace_
\@
// If TRBE is available at EL2
mov
x0
,
#
(
MDCR_EL2_E2TB_MASK
<<
MDCR_EL2_E2TB_SHIFT
)
orr
x2
,
x2
,
x0
// allow the EL1&0 translation
// to own it.
.
Lskip_trace_
\@
:
msr
mdcr_el2
,
x2
// Configure debug traps
.
endm
...
...
arch/arm64/include/asm/kvm_arm.h
View file @
53648ed3
...
...
@@ -278,6 +278,9 @@
#define CPTR_EL2_DEFAULT CPTR_EL2_RES1
/* Hyp Debug Configuration Register bits */
#define MDCR_EL2_E2TB_MASK (UL(0x3))
#define MDCR_EL2_E2TB_SHIFT (UL(24))
#define MDCR_EL2_TTRF (1 << 19)
#define MDCR_EL2_TPMS (1 << 14)
#define MDCR_EL2_E2PB_MASK (UL(0x3))
#define MDCR_EL2_E2PB_SHIFT (UL(12))
...
...
arch/arm64/include/asm/kvm_host.h
View file @
53648ed3
...
...
@@ -315,6 +315,8 @@ struct kvm_vcpu_arch {
struct
kvm_guest_debug_arch
regs
;
/* Statistical profiling extension */
u64
pmscr_el1
;
/* Self-hosted trace */
u64
trfcr_el1
;
}
host_debug_state
;
/* VGIC state */
...
...
@@ -400,6 +402,8 @@ struct kvm_vcpu_arch {
#define KVM_ARM64_GUEST_HAS_PTRAUTH (1 << 7)
/* PTRAUTH exposed to guest */
#define KVM_ARM64_PENDING_EXCEPTION (1 << 8)
/* Exception pending */
#define KVM_ARM64_EXCEPT_MASK (7 << 9)
/* Target EL/MODE */
#define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12)
/* Save SPE context if active */
#define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13)
/* Save TRBE context if active */
/*
* When KVM_ARM64_PENDING_EXCEPTION is set, KVM_ARM64_EXCEPT_MASK can
...
...
@@ -734,6 +738,10 @@ static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr)
return
(
!
has_vhe
()
&&
attr
->
exclude_host
);
}
/* Flags for host debug state */
void
kvm_arch_vcpu_load_debug_state_flags
(
struct
kvm_vcpu
*
vcpu
);
void
kvm_arch_vcpu_put_debug_state_flags
(
struct
kvm_vcpu
*
vcpu
);
#ifdef CONFIG_KVM
/* Avoid conflicts with core headers if CONFIG_KVM=n */
static
inline
int
kvm_arch_vcpu_run_pid_change
(
struct
kvm_vcpu
*
vcpu
)
{
...
...
arch/arm64/include/asm/sysreg.h
View file @
53648ed3
...
...
@@ -333,6 +333,55 @@
/*** End of Statistical Profiling Extension ***/
/*
* TRBE Registers
*/
#define SYS_TRBLIMITR_EL1 sys_reg(3, 0, 9, 11, 0)
#define SYS_TRBPTR_EL1 sys_reg(3, 0, 9, 11, 1)
#define SYS_TRBBASER_EL1 sys_reg(3, 0, 9, 11, 2)
#define SYS_TRBSR_EL1 sys_reg(3, 0, 9, 11, 3)
#define SYS_TRBMAR_EL1 sys_reg(3, 0, 9, 11, 4)
#define SYS_TRBTRG_EL1 sys_reg(3, 0, 9, 11, 6)
#define SYS_TRBIDR_EL1 sys_reg(3, 0, 9, 11, 7)
#define TRBLIMITR_LIMIT_MASK GENMASK_ULL(51, 0)
#define TRBLIMITR_LIMIT_SHIFT 12
#define TRBLIMITR_NVM BIT(5)
#define TRBLIMITR_TRIG_MODE_MASK GENMASK(1, 0)
#define TRBLIMITR_TRIG_MODE_SHIFT 3
#define TRBLIMITR_FILL_MODE_MASK GENMASK(1, 0)
#define TRBLIMITR_FILL_MODE_SHIFT 1
#define TRBLIMITR_ENABLE BIT(0)
#define TRBPTR_PTR_MASK GENMASK_ULL(63, 0)
#define TRBPTR_PTR_SHIFT 0
#define TRBBASER_BASE_MASK GENMASK_ULL(51, 0)
#define TRBBASER_BASE_SHIFT 12
#define TRBSR_EC_MASK GENMASK(5, 0)
#define TRBSR_EC_SHIFT 26
#define TRBSR_IRQ BIT(22)
#define TRBSR_TRG BIT(21)
#define TRBSR_WRAP BIT(20)
#define TRBSR_ABORT BIT(18)
#define TRBSR_STOP BIT(17)
#define TRBSR_MSS_MASK GENMASK(15, 0)
#define TRBSR_MSS_SHIFT 0
#define TRBSR_BSC_MASK GENMASK(5, 0)
#define TRBSR_BSC_SHIFT 0
#define TRBSR_FSC_MASK GENMASK(5, 0)
#define TRBSR_FSC_SHIFT 0
#define TRBMAR_SHARE_MASK GENMASK(1, 0)
#define TRBMAR_SHARE_SHIFT 8
#define TRBMAR_OUTER_MASK GENMASK(3, 0)
#define TRBMAR_OUTER_SHIFT 4
#define TRBMAR_INNER_MASK GENMASK(3, 0)
#define TRBMAR_INNER_SHIFT 0
#define TRBTRG_TRG_MASK GENMASK(31, 0)
#define TRBTRG_TRG_SHIFT 0
#define TRBIDR_FLAG BIT(5)
#define TRBIDR_PROG BIT(4)
#define TRBIDR_ALIGN_MASK GENMASK(3, 0)
#define TRBIDR_ALIGN_SHIFT 0
#define SYS_PMINTENSET_EL1 sys_reg(3, 0, 9, 14, 1)
#define SYS_PMINTENCLR_EL1 sys_reg(3, 0, 9, 14, 2)
...
...
@@ -840,6 +889,7 @@
#define ID_AA64MMFR2_CNP_SHIFT 0
/* id_aa64dfr0 */
#define ID_AA64DFR0_TRBE_SHIFT 44
#define ID_AA64DFR0_TRACE_FILT_SHIFT 40
#define ID_AA64DFR0_DOUBLELOCK_SHIFT 36
#define ID_AA64DFR0_PMSVER_SHIFT 32
...
...
arch/arm64/kernel/cpufeature.c
View file @
53648ed3
...
...
@@ -383,7 +383,6 @@ static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
* of support.
*/
S_ARM64_FTR_BITS
(
FTR_HIDDEN
,
FTR_NONSTRICT
,
FTR_EXACT
,
ID_AA64DFR0_PMUVER_SHIFT
,
4
,
0
),
ARM64_FTR_BITS
(
FTR_HIDDEN
,
FTR_STRICT
,
FTR_EXACT
,
ID_AA64DFR0_TRACEVER_SHIFT
,
4
,
0
),
ARM64_FTR_BITS
(
FTR_HIDDEN
,
FTR_STRICT
,
FTR_EXACT
,
ID_AA64DFR0_DEBUGVER_SHIFT
,
4
,
0x6
),
ARM64_FTR_END
,
};
...
...
arch/arm64/kernel/hyp-stub.S
View file @
53648ed3
...
...
@@ -115,9 +115,10 @@ SYM_CODE_START_LOCAL(mutate_to_vhe)
mrs_s
x0
,
SYS_VBAR_EL12
msr
vbar_el1
,
x0
//
Use
EL2
translations
for
SPE
and
disable
access
from
EL1
//
Use
EL2
translations
for
SPE
&
TRBE
and
disable
access
from
EL1
mrs
x0
,
mdcr_el2
bic
x0
,
x0
,
#(
MDCR_EL2_E2PB_MASK
<<
MDCR_EL2_E2PB_SHIFT
)
bic
x0
,
x0
,
#(
MDCR_EL2_E2TB_MASK
<<
MDCR_EL2_E2TB_SHIFT
)
msr
mdcr_el2
,
x0
//
Transfer
the
MM
state
from
EL1
to
EL2
...
...
arch/arm64/kvm/arm.c
View file @
53648ed3
...
...
@@ -416,10 +416,12 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
if
(
vcpu_has_ptrauth
(
vcpu
))
vcpu_ptrauth_disable
(
vcpu
);
kvm_arch_vcpu_load_debug_state_flags
(
vcpu
);
}
void
kvm_arch_vcpu_put
(
struct
kvm_vcpu
*
vcpu
)
{
kvm_arch_vcpu_put_debug_state_flags
(
vcpu
);
kvm_arch_vcpu_put_fp
(
vcpu
);
if
(
has_vhe
())
kvm_vcpu_put_sysregs_vhe
(
vcpu
);
...
...
arch/arm64/kvm/debug.c
View file @
53648ed3
...
...
@@ -89,6 +89,7 @@ void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu)
* - Debug ROM Address (MDCR_EL2_TDRA)
* - OS related registers (MDCR_EL2_TDOSA)
* - Statistical profiler (MDCR_EL2_TPMS/MDCR_EL2_E2PB)
* - Self-hosted Trace (MDCR_EL2_TTRF/MDCR_EL2_E2TB)
*
* Additionally, KVM only traps guest accesses to the debug registers if
* the guest is not actively using them (see the KVM_ARM64_DEBUG_DIRTY
...
...
@@ -106,12 +107,13 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
trace_kvm_arm_setup_debug
(
vcpu
,
vcpu
->
guest_debug
);
/*
* This also clears MDCR_EL2_E2PB_MASK
to disable guest access
* to
the profiling buffer.
* This also clears MDCR_EL2_E2PB_MASK
and MDCR_EL2_E2TB_MASK
* to
disable guest access to the profiling and trace buffers
*/
vcpu
->
arch
.
mdcr_el2
=
__this_cpu_read
(
mdcr_el2
)
&
MDCR_EL2_HPMN_MASK
;
vcpu
->
arch
.
mdcr_el2
|=
(
MDCR_EL2_TPM
|
MDCR_EL2_TPMS
|
MDCR_EL2_TTRF
|
MDCR_EL2_TPMCR
|
MDCR_EL2_TDRA
|
MDCR_EL2_TDOSA
);
...
...
@@ -229,3 +231,32 @@ void kvm_arm_clear_debug(struct kvm_vcpu *vcpu)
}
}
}
void
kvm_arch_vcpu_load_debug_state_flags
(
struct
kvm_vcpu
*
vcpu
)
{
u64
dfr0
;
/* For VHE, there is nothing to do */
if
(
has_vhe
())
return
;
dfr0
=
read_sysreg
(
id_aa64dfr0_el1
);
/*
* If SPE is present on this CPU and is available at current EL,
* we may need to check if the host state needs to be saved.
*/
if
(
cpuid_feature_extract_unsigned_field
(
dfr0
,
ID_AA64DFR0_PMSVER_SHIFT
)
&&
!
(
read_sysreg_s
(
SYS_PMBIDR_EL1
)
&
BIT
(
SYS_PMBIDR_EL1_P_SHIFT
)))
vcpu
->
arch
.
flags
|=
KVM_ARM64_DEBUG_STATE_SAVE_SPE
;
/* Check if we have TRBE implemented and available at the host */
if
(
cpuid_feature_extract_unsigned_field
(
dfr0
,
ID_AA64DFR0_TRBE_SHIFT
)
&&
!
(
read_sysreg_s
(
SYS_TRBIDR_EL1
)
&
TRBIDR_PROG
))
vcpu
->
arch
.
flags
|=
KVM_ARM64_DEBUG_STATE_SAVE_TRBE
;
}
void
kvm_arch_vcpu_put_debug_state_flags
(
struct
kvm_vcpu
*
vcpu
)
{
vcpu
->
arch
.
flags
&=
~
(
KVM_ARM64_DEBUG_STATE_SAVE_SPE
|
KVM_ARM64_DEBUG_STATE_SAVE_TRBE
);
}
arch/arm64/kvm/hyp/nvhe/debug-sr.c
View file @
53648ed3
...
...
@@ -21,17 +21,11 @@ static void __debug_save_spe(u64 *pmscr_el1)
/* Clear pmscr in case of early return */
*
pmscr_el1
=
0
;
/* SPE present on this CPU? */
if
(
!
cpuid_feature_extract_unsigned_field
(
read_sysreg
(
id_aa64dfr0_el1
),
ID_AA64DFR0_PMSVER_SHIFT
))
return
;
/* Yes; is it owned by EL3? */
reg
=
read_sysreg_s
(
SYS_PMBIDR_EL1
);
if
(
reg
&
BIT
(
SYS_PMBIDR_EL1_P_SHIFT
))
return
;
/* No; is the host actually using the thing? */
/*
* At this point, we know that this CPU implements
* SPE and is available to the host.
* Check if the host is actually using it ?
*/
reg
=
read_sysreg_s
(
SYS_PMBLIMITR_EL1
);
if
(
!
(
reg
&
BIT
(
SYS_PMBLIMITR_EL1_E_SHIFT
)))
return
;
...
...
@@ -58,10 +52,43 @@ static void __debug_restore_spe(u64 pmscr_el1)
write_sysreg_s
(
pmscr_el1
,
SYS_PMSCR_EL1
);
}
static
void
__debug_save_trace
(
u64
*
trfcr_el1
)
{
*
trfcr_el1
=
0
;
/* Check if the TRBE is enabled */
if
(
!
(
read_sysreg_s
(
SYS_TRBLIMITR_EL1
)
&
TRBLIMITR_ENABLE
))
return
;
/*
* Prohibit trace generation while we are in guest.
* Since access to TRFCR_EL1 is trapped, the guest can't
* modify the filtering set by the host.
*/
*
trfcr_el1
=
read_sysreg_s
(
SYS_TRFCR_EL1
);
write_sysreg_s
(
0
,
SYS_TRFCR_EL1
);
isb
();
/* Drain the trace buffer to memory */
tsb_csync
();
dsb
(
nsh
);
}
static
void
__debug_restore_trace
(
u64
trfcr_el1
)
{
if
(
!
trfcr_el1
)
return
;
/* Restore trace filter controls */
write_sysreg_s
(
trfcr_el1
,
SYS_TRFCR_EL1
);
}
void
__debug_save_host_buffers_nvhe
(
struct
kvm_vcpu
*
vcpu
)
{
/* Disable and flush SPE data generation */
if
(
vcpu
->
arch
.
flags
&
KVM_ARM64_DEBUG_STATE_SAVE_SPE
)
__debug_save_spe
(
&
vcpu
->
arch
.
host_debug_state
.
pmscr_el1
);
/* Disable and flush Self-Hosted Trace generation */
if
(
vcpu
->
arch
.
flags
&
KVM_ARM64_DEBUG_STATE_SAVE_TRBE
)
__debug_save_trace
(
&
vcpu
->
arch
.
host_debug_state
.
trfcr_el1
);
}
void
__debug_switch_to_guest
(
struct
kvm_vcpu
*
vcpu
)
...
...
@@ -71,7 +98,10 @@ void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
void
__debug_restore_host_buffers_nvhe
(
struct
kvm_vcpu
*
vcpu
)
{
if
(
vcpu
->
arch
.
flags
&
KVM_ARM64_DEBUG_STATE_SAVE_SPE
)
__debug_restore_spe
(
vcpu
->
arch
.
host_debug_state
.
pmscr_el1
);
if
(
vcpu
->
arch
.
flags
&
KVM_ARM64_DEBUG_STATE_SAVE_TRBE
)
__debug_restore_trace
(
vcpu
->
arch
.
host_debug_state
.
trfcr_el1
);
}
void
__debug_switch_to_host
(
struct
kvm_vcpu
*
vcpu
)
...
...
arch/arm64/kvm/hyp/nvhe/switch.c
View file @
53648ed3
...
...
@@ -95,6 +95,7 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu)
mdcr_el2
&=
MDCR_EL2_HPMN_MASK
;
mdcr_el2
|=
MDCR_EL2_E2PB_MASK
<<
MDCR_EL2_E2PB_SHIFT
;
mdcr_el2
|=
MDCR_EL2_E2TB_MASK
<<
MDCR_EL2_E2TB_SHIFT
;
write_sysreg
(
mdcr_el2
,
mdcr_el2
);
if
(
is_protected_kvm_enabled
())
...
...
arch/arm64/kvm/sys_regs.c
View file @
53648ed3
...
...
@@ -1472,6 +1472,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{
SYS_DESC
(
SYS_GCR_EL1
),
undef_access
},
{
SYS_DESC
(
SYS_ZCR_EL1
),
NULL
,
reset_val
,
ZCR_EL1
,
0
,
.
visibility
=
sve_visibility
},
{
SYS_DESC
(
SYS_TRFCR_EL1
),
undef_access
},
{
SYS_DESC
(
SYS_TTBR0_EL1
),
access_vm_reg
,
reset_unknown
,
TTBR0_EL1
},
{
SYS_DESC
(
SYS_TTBR1_EL1
),
access_vm_reg
,
reset_unknown
,
TTBR1_EL1
},
{
SYS_DESC
(
SYS_TCR_EL1
),
access_vm_reg
,
reset_val
,
TCR_EL1
,
0
},
...
...
drivers/hwtracing/coresight/Kconfig
View file @
53648ed3
...
...
@@ -97,15 +97,15 @@ config CORESIGHT_SOURCE_ETM3X
module will be called coresight-etm3x.
config CORESIGHT_SOURCE_ETM4X
tristate "CoreSight E
mbedded Trace Macrocell 4.x
driver"
tristate "CoreSight E
TMv4.x / ETE
driver"
depends on ARM64
select CORESIGHT_LINKS_AND_SINKS
select PID_IN_CONTEXTIDR
help
This driver provides support for the
ETM4.x tracer module, tracing the
instructions that a processor is executing. This is primarily useful
for instruction level tracing. Depending on the implemented version
data tracing may also be available
.
This driver provides support for the
CoreSight Embedded Trace Macrocell
version 4.x and the Embedded Trace Extensions (ETE). Both are CPU tracer
modules, tracing the instructions that a processor is executing. This is
primarily useful for instruction level tracing
.
To compile this driver as a module, choose M here: the
module will be called coresight-etm4x.
...
...
@@ -173,4 +173,18 @@ config CORESIGHT_CTI_INTEGRATION_REGS
CTI trigger connections between this and other devices.These
registers are not used in normal operation and can leave devices in
an inconsistent state.
config CORESIGHT_TRBE
tristate "Trace Buffer Extension (TRBE) driver"
depends on ARM64 && CORESIGHT_SOURCE_ETM4X
help
This driver provides support for percpu Trace Buffer Extension (TRBE).
TRBE always needs to be used along with it's corresponding percpu ETE
component. ETE generates trace data which is then captured with TRBE.
Unlike traditional sink devices, TRBE is a CPU feature accessible via
system registers. But it's explicit dependency with trace unit (ETE)
requires it to be plugged in as a coresight sink device.
To compile this driver as a module, choose M here: the module will be
called coresight-trbe.
endif
drivers/hwtracing/coresight/Makefile
View file @
53648ed3
...
...
@@ -21,5 +21,6 @@ obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
obj-$(CONFIG_CORESIGHT_CPU_DEBUG)
+=
coresight-cpu-debug.o
obj-$(CONFIG_CORESIGHT_CATU)
+=
coresight-catu.o
obj-$(CONFIG_CORESIGHT_CTI)
+=
coresight-cti.o
obj-$(CONFIG_CORESIGHT_TRBE)
+=
coresight-trbe.o
coresight-cti-y
:=
coresight-cti-core.o coresight-cti-platform.o
\
coresight-cti-sysfs.o
drivers/hwtracing/coresight/coresight-core.c
View file @
53648ed3
...
...
@@ -23,6 +23,7 @@
#include "coresight-priv.h"
static
DEFINE_MUTEX
(
coresight_mutex
);
DEFINE_PER_CPU
(
struct
coresight_device
*
,
csdev_sink
);
/**
* struct coresight_node - elements of a path, from source to sink
...
...
@@ -70,6 +71,18 @@ void coresight_remove_cti_ops(void)
}
EXPORT_SYMBOL_GPL
(
coresight_remove_cti_ops
);
void
coresight_set_percpu_sink
(
int
cpu
,
struct
coresight_device
*
csdev
)
{
per_cpu
(
csdev_sink
,
cpu
)
=
csdev
;
}
EXPORT_SYMBOL_GPL
(
coresight_set_percpu_sink
);
struct
coresight_device
*
coresight_get_percpu_sink
(
int
cpu
)
{
return
per_cpu
(
csdev_sink
,
cpu
);
}
EXPORT_SYMBOL_GPL
(
coresight_get_percpu_sink
);
static
int
coresight_id_match
(
struct
device
*
dev
,
void
*
data
)
{
int
trace_id
,
i_trace_id
;
...
...
@@ -784,6 +797,14 @@ static int _coresight_build_path(struct coresight_device *csdev,
if
(
csdev
==
sink
)
goto
out
;
if
(
coresight_is_percpu_source
(
csdev
)
&&
coresight_is_percpu_sink
(
sink
)
&&
sink
==
per_cpu
(
csdev_sink
,
source_ops
(
csdev
)
->
cpu_id
(
csdev
)))
{
if
(
_coresight_build_path
(
sink
,
sink
,
path
)
==
0
)
{
found
=
true
;
goto
out
;
}
}
/* Not a sink - recursively explore each port found on this element */
for
(
i
=
0
;
i
<
csdev
->
pdata
->
nr_outport
;
i
++
)
{
struct
coresight_device
*
child_dev
;
...
...
@@ -999,8 +1020,12 @@ coresight_find_default_sink(struct coresight_device *csdev)
int
depth
=
0
;
/* look for a default sink if we have not found for this device */
if
(
!
csdev
->
def_sink
)
{
if
(
coresight_is_percpu_source
(
csdev
))
csdev
->
def_sink
=
per_cpu
(
csdev_sink
,
source_ops
(
csdev
)
->
cpu_id
(
csdev
));
if
(
!
csdev
->
def_sink
)
csdev
->
def_sink
=
coresight_find_sink
(
csdev
,
&
depth
);
}
return
csdev
->
def_sink
;
}
...
...
drivers/hwtracing/coresight/coresight-etm-perf.c
View file @
53648ed3
...
...
@@ -24,7 +24,26 @@
static
struct
pmu
etm_pmu
;
static
bool
etm_perf_up
;
static
DEFINE_PER_CPU
(
struct
perf_output_handle
,
ctx_handle
);
/*
* An ETM context for a running event includes the perf aux handle
* and aux_data. For ETM, the aux_data (etm_event_data), consists of
* the trace path and the sink configuration. The event data is accessible
* via perf_get_aux(handle). However, a sink could "end" a perf output
* handle via the IRQ handler. And if the "sink" encounters a failure
* to "begin" another session (e.g due to lack of space in the buffer),
* the handle will be cleared. Thus, the event_data may not be accessible
* from the handle when we get to the etm_event_stop(), which is required
* for stopping the trace path. The event_data is guaranteed to stay alive
* until "free_aux()", which cannot happen as long as the event is active on
* the ETM. Thus the event_data for the session must be part of the ETM context
* to make sure we can disable the trace path.
*/
struct
etm_ctxt
{
struct
perf_output_handle
handle
;
struct
etm_event_data
*
event_data
;
};
static
DEFINE_PER_CPU
(
struct
etm_ctxt
,
etm_ctxt
);
static
DEFINE_PER_CPU
(
struct
coresight_device
*
,
csdev_src
);
/*
...
...
@@ -232,6 +251,25 @@ static void etm_free_aux(void *data)
schedule_work
(
&
event_data
->
work
);
}
/*
* Check if two given sinks are compatible with each other,
* so that they can use the same sink buffers, when an event
* moves around.
*/
static
bool
sinks_compatible
(
struct
coresight_device
*
a
,
struct
coresight_device
*
b
)
{
if
(
!
a
||
!
b
)
return
false
;
/*
* If the sinks are of the same subtype and driven
* by the same driver, we can use the same buffer
* on these sinks.
*/
return
(
a
->
subtype
.
sink_subtype
==
b
->
subtype
.
sink_subtype
)
&&
(
sink_ops
(
a
)
==
sink_ops
(
b
));
}
static
void
*
etm_setup_aux
(
struct
perf_event
*
event
,
void
**
pages
,
int
nr_pages
,
bool
overwrite
)
{
...
...
@@ -239,6 +277,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
int
cpu
=
event
->
cpu
;
cpumask_t
*
mask
;
struct
coresight_device
*
sink
=
NULL
;
struct
coresight_device
*
user_sink
=
NULL
,
*
last_sink
=
NULL
;
struct
etm_event_data
*
event_data
=
NULL
;
event_data
=
alloc_event_data
(
cpu
);
...
...
@@ -249,7 +288,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
/* First get the selected sink from user space. */
if
(
event
->
attr
.
config2
)
{
id
=
(
u32
)
event
->
attr
.
config2
;
sink
=
coresight_get_sink_by_id
(
id
);
sink
=
user_sink
=
coresight_get_sink_by_id
(
id
);
}
mask
=
&
event_data
->
mask
;
...
...
@@ -277,14 +316,33 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
}
/*
* No sink provided - look for a default sink for one of the
* devices. At present we only support topology where all CPUs
* use the same sink [N:1], so only need to find one sink. The
* coresight_build_path later will remove any CPU that does not
* attach to the sink, or if we have not found a sink.
* No sink provided - look for a default sink for all the ETMs,
* where this event can be scheduled.
* We allocate the sink specific buffers only once for this
* event. If the ETMs have different default sink devices, we
* can only use a single "type" of sink as the event can carry
* only one sink specific buffer. Thus we have to make sure
* that the sinks are of the same type and driven by the same
* driver, as the one we allocate the buffer for. As such
* we choose the first sink and check if the remaining ETMs
* have a compatible default sink. We don't trace on a CPU
* if the sink is not compatible.
*/
if
(
!
sink
)
if
(
!
user_sink
)
{
/* Find the default sink for this ETM */
sink
=
coresight_find_default_sink
(
csdev
);
if
(
!
sink
)
{
cpumask_clear_cpu
(
cpu
,
mask
);
continue
;
}
/* Check if this sink compatible with the last sink */
if
(
last_sink
&&
!
sinks_compatible
(
last_sink
,
sink
))
{
cpumask_clear_cpu
(
cpu
,
mask
);
continue
;
}
last_sink
=
sink
;
}
/*
* Building a path doesn't enable it, it simply builds a
...
...
@@ -312,7 +370,12 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
if
(
!
sink_ops
(
sink
)
->
alloc_buffer
||
!
sink_ops
(
sink
)
->
free_buffer
)
goto
err
;
/* Allocate the sink buffer for this session */
/*
* Allocate the sink buffer for this session. All the sinks
* where this event can be scheduled are ensured to be of the
* same type. Thus the same sink configuration is used by the
* sinks.
*/
event_data
->
snk_config
=
sink_ops
(
sink
)
->
alloc_buffer
(
sink
,
event
,
pages
,
nr_pages
,
overwrite
);
...
...
@@ -332,13 +395,18 @@ static void etm_event_start(struct perf_event *event, int flags)
{
int
cpu
=
smp_processor_id
();
struct
etm_event_data
*
event_data
;
struct
perf_output_handle
*
handle
=
this_cpu_ptr
(
&
ctx_handle
);
struct
etm_ctxt
*
ctxt
=
this_cpu_ptr
(
&
etm_ctxt
);
struct
perf_output_handle
*
handle
=
&
ctxt
->
handle
;
struct
coresight_device
*
sink
,
*
csdev
=
per_cpu
(
csdev_src
,
cpu
);
struct
list_head
*
path
;
if
(
!
csdev
)
goto
fail
;
/* Have we messed up our tracking ? */
if
(
WARN_ON
(
ctxt
->
event_data
))
goto
fail
;
/*
* Deal with the ring buffer API and get a handle on the
* session's information.
...
...
@@ -374,6 +442,8 @@ static void etm_event_start(struct perf_event *event, int flags)
if
(
source_ops
(
csdev
)
->
enable
(
csdev
,
event
,
CS_MODE_PERF
))
goto
fail_disable_path
;
/* Save the event_data for this ETM */
ctxt
->
event_data
=
event_data
;
out:
return
;
...
...
@@ -392,13 +462,30 @@ static void etm_event_stop(struct perf_event *event, int mode)
int
cpu
=
smp_processor_id
();
unsigned
long
size
;
struct
coresight_device
*
sink
,
*
csdev
=
per_cpu
(
csdev_src
,
cpu
);
struct
perf_output_handle
*
handle
=
this_cpu_ptr
(
&
ctx_handle
);
struct
etm_event_data
*
event_data
=
perf_get_aux
(
handle
);
struct
etm_ctxt
*
ctxt
=
this_cpu_ptr
(
&
etm_ctxt
);
struct
perf_output_handle
*
handle
=
&
ctxt
->
handle
;
struct
etm_event_data
*
event_data
;
struct
list_head
*
path
;
/*
* If we still have access to the event_data via handle,
* confirm that we haven't messed up the tracking.
*/
if
(
handle
->
event
&&
WARN_ON
(
perf_get_aux
(
handle
)
!=
ctxt
->
event_data
))
return
;
event_data
=
ctxt
->
event_data
;
/* Clear the event_data as this ETM is stopping the trace. */
ctxt
->
event_data
=
NULL
;
if
(
event
->
hw
.
state
==
PERF_HES_STOPPED
)
return
;
/* We must have a valid event_data for a running event */
if
(
WARN_ON
(
!
event_data
))
return
;
if
(
!
csdev
)
return
;
...
...
@@ -416,7 +503,13 @@ static void etm_event_stop(struct perf_event *event, int mode)
/* tell the core */
event
->
hw
.
state
=
PERF_HES_STOPPED
;
if
(
mode
&
PERF_EF_UPDATE
)
{
/*
* If the handle is not bound to an event anymore
* (e.g, the sink driver was unable to restart the
* handle due to lack of buffer space), we don't
* have to do anything here.
*/
if
(
handle
->
event
&&
(
mode
&
PERF_EF_UPDATE
))
{
if
(
WARN_ON_ONCE
(
handle
->
event
!=
event
))
return
;
...
...
drivers/hwtracing/coresight/coresight-etm4x-core.c
View file @
53648ed3
...
...
@@ -31,6 +31,7 @@
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <asm/barrier.h>
#include <asm/sections.h>
#include <asm/sysreg.h>
#include <asm/local.h>
...
...
@@ -114,30 +115,91 @@ void etm4x_sysreg_write(u64 val, u32 offset, bool _relaxed, bool _64bit)
}
}
static
void
etm4_os_unlock_csa
(
struct
etmv4_drvdata
*
drvdata
,
struct
csdev_access
*
csa
)
static
u64
ete_sysreg_read
(
u32
offset
,
bool
_relaxed
,
bool
_64bit
)
{
/* Writing 0 to TRCOSLAR unlocks the trace registers */
etm4x_relaxed_write32
(
csa
,
0x0
,
TRCOSLAR
);
drvdata
->
os_unlock
=
true
;
u64
res
=
0
;
switch
(
offset
)
{
ETE_READ_CASES
(
res
)
default
:
pr_warn_ratelimited
(
"ete: trying to read unsupported register @%x
\n
"
,
offset
);
}
if
(
!
_relaxed
)
__iormb
(
res
);
/* Imitate the !relaxed I/O helpers */
return
res
;
}
static
void
ete_sysreg_write
(
u64
val
,
u32
offset
,
bool
_relaxed
,
bool
_64bit
)
{
if
(
!
_relaxed
)
__iowmb
();
/* Imitate the !relaxed I/O helpers */
if
(
!
_64bit
)
val
&=
GENMASK
(
31
,
0
);
switch
(
offset
)
{
ETE_WRITE_CASES
(
val
)
default
:
pr_warn_ratelimited
(
"ete: trying to write to unsupported register @%x
\n
"
,
offset
);
}
}
static
void
etm_detect_os_lock
(
struct
etmv4_drvdata
*
drvdata
,
struct
csdev_access
*
csa
)
{
u32
oslsr
=
etm4x_relaxed_read32
(
csa
,
TRCOSLSR
);
drvdata
->
os_lock_model
=
ETM_OSLSR_OSLM
(
oslsr
);
}
static
void
etm_write_os_lock
(
struct
etmv4_drvdata
*
drvdata
,
struct
csdev_access
*
csa
,
u32
val
)
{
val
=
!!
val
;
switch
(
drvdata
->
os_lock_model
)
{
case
ETM_OSLOCK_PRESENT
:
etm4x_relaxed_write32
(
csa
,
val
,
TRCOSLAR
);
break
;
case
ETM_OSLOCK_PE
:
write_sysreg_s
(
val
,
SYS_OSLAR_EL1
);
break
;
default:
pr_warn_once
(
"CPU%d: Unsupported Trace OSLock model: %x
\n
"
,
smp_processor_id
(),
drvdata
->
os_lock_model
);
fallthrough
;
case
ETM_OSLOCK_NI
:
return
;
}
isb
();
}
static
inline
void
etm4_os_unlock_csa
(
struct
etmv4_drvdata
*
drvdata
,
struct
csdev_access
*
csa
)
{
WARN_ON
(
drvdata
->
cpu
!=
smp_processor_id
());
/* Writing 0 to OS Lock unlocks the trace unit registers */
etm_write_os_lock
(
drvdata
,
csa
,
0x0
);
drvdata
->
os_unlock
=
true
;
}
static
void
etm4_os_unlock
(
struct
etmv4_drvdata
*
drvdata
)
{
if
(
!
WARN_ON
(
!
drvdata
->
csdev
))
etm4_os_unlock_csa
(
drvdata
,
&
drvdata
->
csdev
->
access
);
}
static
void
etm4_os_lock
(
struct
etmv4_drvdata
*
drvdata
)
{
if
(
WARN_ON
(
!
drvdata
->
csdev
))
return
;
/* Writing 0x1 to TRCOSLAR locks the trace registers */
etm4x_relaxed_write32
(
&
drvdata
->
csdev
->
access
,
0x1
,
TRCOSLAR
);
/* Writing 0x1 to OS Lock locks the trace registers */
etm_write_os_lock
(
drvdata
,
&
drvdata
->
csdev
->
access
,
0x1
);
drvdata
->
os_unlock
=
false
;
isb
();
}
static
void
etm4_cs_lock
(
struct
etmv4_drvdata
*
drvdata
,
...
...
@@ -371,6 +433,13 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32
(
csa
,
trcpdcr
|
TRCPDCR_PU
,
TRCPDCR
);
}
/*
* ETE mandates that the TRCRSR is written to before
* enabling it.
*/
if
(
etm4x_is_ete
(
drvdata
))
etm4x_relaxed_write32
(
csa
,
TRCRSR_TA
,
TRCRSR
);
/* Enable the trace unit */
etm4x_relaxed_write32
(
csa
,
1
,
TRCPRGCTLR
);
...
...
@@ -654,6 +723,7 @@ static int etm4_enable(struct coresight_device *csdev,
static
void
etm4_disable_hw
(
void
*
info
)
{
u32
control
;
u64
trfcr
;
struct
etmv4_drvdata
*
drvdata
=
info
;
struct
etmv4_config
*
config
=
&
drvdata
->
config
;
struct
coresight_device
*
csdev
=
drvdata
->
csdev
;
...
...
@@ -676,6 +746,16 @@ static void etm4_disable_hw(void *info)
/* EN, bit[0] Trace unit enable bit */
control
&=
~
0x1
;
/*
* If the CPU supports v8.4 Trace filter Control,
* set the ETM to trace prohibited region.
*/
if
(
drvdata
->
trfc
)
{
trfcr
=
read_sysreg_s
(
SYS_TRFCR_EL1
);
write_sysreg_s
(
trfcr
&
~
(
TRFCR_ELx_ExTRE
|
TRFCR_ELx_E0TRE
),
SYS_TRFCR_EL1
);
isb
();
}
/*
* Make sure everything completes before disabling, as recommended
* by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register,
...
...
@@ -683,12 +763,16 @@ static void etm4_disable_hw(void *info)
*/
dsb
(
sy
);
isb
();
/* Trace synchronization barrier, is a nop if not supported */
tsb_csync
();
etm4x_relaxed_write32
(
csa
,
control
,
TRCPRGCTLR
);
/* wait for TRCSTATR.PMSTABLE to go to '1' */
if
(
coresight_timeout
(
csa
,
TRCSTATR
,
TRCSTATR_PMSTABLE_BIT
,
1
))
dev_err
(
etm_dev
,
"timeout while waiting for PM stable Trace Status
\n
"
);
if
(
drvdata
->
trfc
)
write_sysreg_s
(
trfcr
,
SYS_TRFCR_EL1
);
/* read the status of the single shot comparators */
for
(
i
=
0
;
i
<
drvdata
->
nr_ss_cmp
;
i
++
)
{
...
...
@@ -817,13 +901,24 @@ static bool etm4_init_sysreg_access(struct etmv4_drvdata *drvdata,
* ETMs implementing sysreg access must implement TRCDEVARCH.
*/
devarch
=
read_etm4x_sysreg_const_offset
(
TRCDEVARCH
);
if
((
devarch
&
ETM_DEVARCH_ID_MASK
)
!=
ETM_DEVARCH_ETMv4x_ARCH
)
return
false
;
switch
(
devarch
&
ETM_DEVARCH_ID_MASK
)
{
case
ETM_DEVARCH_ETMv4x_ARCH
:
*
csa
=
(
struct
csdev_access
)
{
.
io_mem
=
false
,
.
read
=
etm4x_sysreg_read
,
.
write
=
etm4x_sysreg_write
,
};
break
;
case
ETM_DEVARCH_ETE_ARCH
:
*
csa
=
(
struct
csdev_access
)
{
.
io_mem
=
false
,
.
read
=
ete_sysreg_read
,
.
write
=
ete_sysreg_write
,
};
break
;
default:
return
false
;
}
drvdata
->
arch
=
etm_devarch_to_arch
(
devarch
);
return
true
;
...
...
@@ -873,7 +968,7 @@ static bool etm4_init_csdev_access(struct etmv4_drvdata *drvdata,
return
false
;
}
static
void
cpu_enable_tracing
(
void
)
static
void
cpu_enable_tracing
(
struct
etmv4_drvdata
*
drvdata
)
{
u64
dfr0
=
read_sysreg
(
id_aa64dfr0_el1
);
u64
trfcr
;
...
...
@@ -881,6 +976,7 @@ static void cpu_enable_tracing(void)
if
(
!
cpuid_feature_extract_unsigned_field
(
dfr0
,
ID_AA64DFR0_TRACE_FILT_SHIFT
))
return
;
drvdata
->
trfc
=
true
;
/*
* If the CPU supports v8.4 SelfHosted Tracing, enable
* tracing at the kernel EL and EL0, forcing to use the
...
...
@@ -920,6 +1016,9 @@ static void etm4_init_arch_data(void *info)
if
(
!
etm4_init_csdev_access
(
drvdata
,
csa
))
return
;
/* Detect the support for OS Lock before we actually use it */
etm_detect_os_lock
(
drvdata
,
csa
);
/* Make sure all registers are accessible */
etm4_os_unlock_csa
(
drvdata
,
csa
);
etm4_cs_unlock
(
drvdata
,
csa
);
...
...
@@ -1082,7 +1181,7 @@ static void etm4_init_arch_data(void *info)
/* NUMCNTR, bits[30:28] number of counters available for tracing */
drvdata
->
nr_cntr
=
BMVAL
(
etmidr5
,
28
,
30
);
etm4_cs_lock
(
drvdata
,
csa
);
cpu_enable_tracing
();
cpu_enable_tracing
(
drvdata
);
}
static
inline
u32
etm4_get_victlr_access_type
(
struct
etmv4_config
*
config
)
...
...
@@ -1760,6 +1859,8 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
struct
etmv4_drvdata
*
drvdata
;
struct
coresight_desc
desc
=
{
0
};
struct
etm4_init_arg
init_arg
=
{
0
};
u8
major
,
minor
;
char
*
type_name
;
drvdata
=
devm_kzalloc
(
dev
,
sizeof
(
*
drvdata
),
GFP_KERNEL
);
if
(
!
drvdata
)
...
...
@@ -1786,10 +1887,6 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
if
(
drvdata
->
cpu
<
0
)
return
drvdata
->
cpu
;
desc
.
name
=
devm_kasprintf
(
dev
,
GFP_KERNEL
,
"etm%d"
,
drvdata
->
cpu
);
if
(
!
desc
.
name
)
return
-
ENOMEM
;
init_arg
.
drvdata
=
drvdata
;
init_arg
.
csa
=
&
desc
.
access
;
init_arg
.
pid
=
etm_pid
;
...
...
@@ -1806,6 +1903,22 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
fwnode_property_present
(
dev_fwnode
(
dev
),
"qcom,skip-power-up"
))
drvdata
->
skip_power_up
=
true
;
major
=
ETM_ARCH_MAJOR_VERSION
(
drvdata
->
arch
);
minor
=
ETM_ARCH_MINOR_VERSION
(
drvdata
->
arch
);
if
(
etm4x_is_ete
(
drvdata
))
{
type_name
=
"ete"
;
/* ETE v1 has major version == 0b101. Adjust this for logging.*/
major
-=
4
;
}
else
{
type_name
=
"etm"
;
}
desc
.
name
=
devm_kasprintf
(
dev
,
GFP_KERNEL
,
"%s%d"
,
type_name
,
drvdata
->
cpu
);
if
(
!
desc
.
name
)
return
-
ENOMEM
;
etm4_init_trace_id
(
drvdata
);
etm4_set_default
(
&
drvdata
->
config
);
...
...
@@ -1833,9 +1946,8 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
etmdrvdata
[
drvdata
->
cpu
]
=
drvdata
;
dev_info
(
&
drvdata
->
csdev
->
dev
,
"CPU%d: ETM v%d.%d initialized
\n
"
,
drvdata
->
cpu
,
ETM_ARCH_MAJOR_VERSION
(
drvdata
->
arch
),
ETM_ARCH_MINOR_VERSION
(
drvdata
->
arch
));
dev_info
(
&
drvdata
->
csdev
->
dev
,
"CPU%d: %s v%d.%d initialized
\n
"
,
drvdata
->
cpu
,
type_name
,
major
,
minor
);
if
(
boot_enable
)
{
coresight_enable
(
drvdata
->
csdev
);
...
...
@@ -1978,6 +2090,7 @@ static struct amba_driver etm4x_amba_driver = {
static
const
struct
of_device_id
etm4_sysreg_match
[]
=
{
{
.
compatible
=
"arm,coresight-etm4x-sysreg"
},
{
.
compatible
=
"arm,embedded-trace-extension"
},
{}
};
...
...
drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
View file @
53648ed3
...
...
@@ -2374,12 +2374,20 @@ static inline bool
etm4x_register_implemented
(
struct
etmv4_drvdata
*
drvdata
,
u32
offset
)
{
switch
(
offset
)
{
ETM
4x
_SYSREG_LIST_CASES
ETM
_COMMON
_SYSREG_LIST_CASES
/*
*
Registers accessible via system instructions are always
* implemented.
*
Common registers to ETE & ETM4x accessible via system
* i
nstructions are always i
mplemented.
*/
return
true
;
ETM4x_ONLY_SYSREG_LIST_CASES
/*
* We only support etm4x and ete. So if the device is not
* ETE, it must be ETMv4x.
*/
return
!
etm4x_is_ete
(
drvdata
);
ETM4x_MMAP_LIST_CASES
/*
* Registers accessible only via memory-mapped registers
...
...
@@ -2389,8 +2397,13 @@ etm4x_register_implemented(struct etmv4_drvdata *drvdata, u32 offset)
* coresight_register() and the csdev is not initialized
* until that is done. So rely on the drvdata->base to
* detect if we have a memory mapped access.
* Also ETE doesn't implement memory mapped access, thus
* it is sufficient to check that we are using mmio.
*/
return
!!
drvdata
->
base
;
ETE_ONLY_SYSREG_LIST_CASES
return
etm4x_is_ete
(
drvdata
);
}
return
false
;
...
...
drivers/hwtracing/coresight/coresight-etm4x.h
View file @
53648ed3
...
...
@@ -29,6 +29,7 @@
#define TRCAUXCTLR 0x018
#define TRCEVENTCTL0R 0x020
#define TRCEVENTCTL1R 0x024
#define TRCRSR 0x028
#define TRCSTALLCTLR 0x02C
#define TRCTSCTLR 0x030
#define TRCSYNCPR 0x034
...
...
@@ -49,6 +50,7 @@
#define TRCSEQRSTEVR 0x118
#define TRCSEQSTR 0x11C
#define TRCEXTINSELR 0x120
#define TRCEXTINSELRn(n) (0x120 + (n * 4))
/* n = 0-3 */
#define TRCCNTRLDVRn(n) (0x140 + (n * 4))
/* n = 0-3 */
#define TRCCNTCTLRn(n) (0x150 + (n * 4))
/* n = 0-3 */
#define TRCCNTVRn(n) (0x160 + (n * 4))
/* n = 0-3 */
...
...
@@ -126,6 +128,8 @@
#define TRCCIDR2 0xFF8
#define TRCCIDR3 0xFFC
#define TRCRSR_TA BIT(12)
/*
* System instructions to access ETM registers.
* See ETMv4.4 spec ARM IHI0064F section 4.3.6 System instructions
...
...
@@ -160,10 +164,22 @@
#define CASE_NOP(__unused, x) \
case (x):
/* fall through */
#define ETE_ONLY_SYSREG_LIST(op, val) \
CASE_##op((val), TRCRSR) \
CASE_##op((val), TRCEXTINSELRn(1)) \
CASE_##op((val), TRCEXTINSELRn(2)) \
CASE_##op((val), TRCEXTINSELRn(3))
/* List of registers accessible via System instructions */
#define ETM_SYSREG_LIST(op, val) \
CASE_##op((val), TRCPRGCTLR) \
#define ETM4x_ONLY_SYSREG_LIST(op, val) \
CASE_##op((val), TRCPROCSELR) \
CASE_##op((val), TRCVDCTLR) \
CASE_##op((val), TRCVDSACCTLR) \
CASE_##op((val), TRCVDARCCTLR) \
CASE_##op((val), TRCOSLAR)
#define ETM_COMMON_SYSREG_LIST(op, val) \
CASE_##op((val), TRCPRGCTLR) \
CASE_##op((val), TRCSTATR) \
CASE_##op((val), TRCCONFIGR) \
CASE_##op((val), TRCAUXCTLR) \
...
...
@@ -180,9 +196,6 @@
CASE_##op((val), TRCVIIECTLR) \
CASE_##op((val), TRCVISSCTLR) \
CASE_##op((val), TRCVIPCSSCTLR) \
CASE_##op((val), TRCVDCTLR) \
CASE_##op((val), TRCVDSACCTLR) \
CASE_##op((val), TRCVDARCCTLR) \
CASE_##op((val), TRCSEQEVRn(0)) \
CASE_##op((val), TRCSEQEVRn(1)) \
CASE_##op((val), TRCSEQEVRn(2)) \
...
...
@@ -277,7 +290,6 @@
CASE_##op((val), TRCSSPCICRn(5)) \
CASE_##op((val), TRCSSPCICRn(6)) \
CASE_##op((val), TRCSSPCICRn(7)) \
CASE_##op((val), TRCOSLAR) \
CASE_##op((val), TRCOSLSR) \
CASE_##op((val), TRCACVRn(0)) \
CASE_##op((val), TRCACVRn(1)) \
...
...
@@ -369,12 +381,38 @@
CASE_##op((val), TRCPIDR2) \
CASE_##op((val), TRCPIDR3)
#define ETM4x_READ_SYSREG_CASES(res) ETM_SYSREG_LIST(READ, (res))
#define ETM4x_WRITE_SYSREG_CASES(val) ETM_SYSREG_LIST(WRITE, (val))
#define ETM4x_READ_SYSREG_CASES(res) \
ETM_COMMON_SYSREG_LIST(READ, (res)) \
ETM4x_ONLY_SYSREG_LIST(READ, (res))
#define ETM4x_WRITE_SYSREG_CASES(val) \
ETM_COMMON_SYSREG_LIST(WRITE, (val)) \
ETM4x_ONLY_SYSREG_LIST(WRITE, (val))
#define ETM_COMMON_SYSREG_LIST_CASES \
ETM_COMMON_SYSREG_LIST(NOP, __unused)
#define ETM4x_ONLY_SYSREG_LIST_CASES \
ETM4x_ONLY_SYSREG_LIST(NOP, __unused)
#define ETM4x_SYSREG_LIST_CASES \
ETM_COMMON_SYSREG_LIST_CASES \
ETM4x_ONLY_SYSREG_LIST(NOP, __unused)
#define ETM4x_SYSREG_LIST_CASES ETM_SYSREG_LIST(NOP, __unused)
#define ETM4x_MMAP_LIST_CASES ETM_MMAP_LIST(NOP, __unused)
/* ETE only supports system register access */
#define ETE_READ_CASES(res) \
ETM_COMMON_SYSREG_LIST(READ, (res)) \
ETE_ONLY_SYSREG_LIST(READ, (res))
#define ETE_WRITE_CASES(val) \
ETM_COMMON_SYSREG_LIST(WRITE, (val)) \
ETE_ONLY_SYSREG_LIST(WRITE, (val))
#define ETE_ONLY_SYSREG_LIST_CASES \
ETE_ONLY_SYSREG_LIST(NOP, __unused)
#define read_etm4x_sysreg_offset(offset, _64bit) \
({ \
u64 __val; \
...
...
@@ -505,6 +543,20 @@
ETM_MODE_EXCL_KERN | \
ETM_MODE_EXCL_USER)
/*
* TRCOSLSR.OSLM advertises the OS Lock model.
* OSLM[2:0] = TRCOSLSR[4:3,0]
*
* 0b000 - Trace OS Lock is not implemented.
* 0b010 - Trace OS Lock is implemented.
* 0b100 - Trace OS Lock is not implemented, unit is controlled by PE OS Lock.
*/
#define ETM_OSLOCK_NI 0b000
#define ETM_OSLOCK_PRESENT 0b010
#define ETM_OSLOCK_PE 0b100
#define ETM_OSLSR_OSLM(oslsr) ((((oslsr) & GENMASK(4, 3)) >> 2) | (oslsr & 0x1))
/*
* TRCDEVARCH Bit field definitions
* Bits[31:21] - ARCHITECT = Always Arm Ltd.
...
...
@@ -541,11 +593,14 @@
((ETM_DEVARCH_MAKE_ARCHID_ARCH_VER(major)) | ETM_DEVARCH_ARCHID_ARCH_PART(0xA13))
#define ETM_DEVARCH_ARCHID_ETMv4x ETM_DEVARCH_MAKE_ARCHID(0x4)
#define ETM_DEVARCH_ARCHID_ETE ETM_DEVARCH_MAKE_ARCHID(0x5)
#define ETM_DEVARCH_ID_MASK \
(ETM_DEVARCH_ARCHITECT_MASK | ETM_DEVARCH_ARCHID_MASK | ETM_DEVARCH_PRESENT)
#define ETM_DEVARCH_ETMv4x_ARCH \
(ETM_DEVARCH_ARCHITECT_ARM | ETM_DEVARCH_ARCHID_ETMv4x | ETM_DEVARCH_PRESENT)
#define ETM_DEVARCH_ETE_ARCH \
(ETM_DEVARCH_ARCHITECT_ARM | ETM_DEVARCH_ARCHID_ETE | ETM_DEVARCH_PRESENT)
#define TRCSTATR_IDLE_BIT 0
#define TRCSTATR_PMSTABLE_BIT 1
...
...
@@ -635,6 +690,8 @@
#define ETM_ARCH_MINOR_VERSION(arch) ((arch) & 0xfU)
#define ETM_ARCH_V4 ETM_ARCH_VERSION(4, 0)
#define ETM_ARCH_ETE ETM_ARCH_VERSION(5, 0)
/* Interpretation of resource numbers change at ETM v4.3 architecture */
#define ETM_ARCH_V4_3 ETM_ARCH_VERSION(4, 3)
...
...
@@ -862,6 +919,7 @@ struct etmv4_save_state {
* @nooverflow: Indicate if overflow prevention is supported.
* @atbtrig: If the implementation can support ATB triggers
* @lpoverride: If the implementation can support low-power state over.
* @trfc: If the implementation supports Arm v8.4 trace filter controls.
* @config: structure holding configuration parameters.
* @save_state: State to be preserved across power loss
* @state_needs_restore: True when there is context to restore after PM exit
...
...
@@ -897,6 +955,7 @@ struct etmv4_drvdata {
u8
s_ex_level
;
u8
ns_ex_level
;
u8
q_support
;
u8
os_lock_model
;
bool
sticky_enable
;
bool
boot_enable
;
bool
os_unlock
;
...
...
@@ -912,6 +971,7 @@ struct etmv4_drvdata {
bool
nooverflow
;
bool
atbtrig
;
bool
lpoverride
;
bool
trfc
;
struct
etmv4_config
config
;
struct
etmv4_save_state
*
save_state
;
bool
state_needs_restore
;
...
...
@@ -940,4 +1000,9 @@ void etm4_config_trace_mode(struct etmv4_config *config);
u64
etm4x_sysreg_read
(
u32
offset
,
bool
_relaxed
,
bool
_64bit
);
void
etm4x_sysreg_write
(
u64
val
,
u32
offset
,
bool
_relaxed
,
bool
_64bit
);
static
inline
bool
etm4x_is_ete
(
struct
etmv4_drvdata
*
drvdata
)
{
return
drvdata
->
arch
>=
ETM_ARCH_ETE
;
}
#endif
drivers/hwtracing/coresight/coresight-platform.c
View file @
53648ed3
...
...
@@ -90,6 +90,12 @@ static void of_coresight_get_ports_legacy(const struct device_node *node,
struct
of_endpoint
endpoint
;
int
in
=
0
,
out
=
0
;
/*
* Avoid warnings in of_graph_get_next_endpoint()
* if the device doesn't have any graph connections
*/
if
(
!
of_graph_is_present
(
node
))
return
;
do
{
ep
=
of_graph_get_next_endpoint
(
node
,
ep
);
if
(
!
ep
)
...
...
drivers/hwtracing/coresight/coresight-priv.h
View file @
53648ed3
...
...
@@ -232,4 +232,7 @@ coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode);
void
coresight_set_assoc_ectdev_mutex
(
struct
coresight_device
*
csdev
,
struct
coresight_device
*
ect_csdev
);
void
coresight_set_percpu_sink
(
int
cpu
,
struct
coresight_device
*
csdev
);
struct
coresight_device
*
coresight_get_percpu_sink
(
int
cpu
);
#endif
drivers/hwtracing/coresight/coresight-trbe.c
0 → 100644
View file @
53648ed3
// SPDX-License-Identifier: GPL-2.0
/*
* This driver enables Trace Buffer Extension (TRBE) as a per-cpu coresight
* sink device could then pair with an appropriate per-cpu coresight source
* device (ETE) thus generating required trace data. Trace can be enabled
* via the perf framework.
*
* The AUX buffer handling is inspired from Arm SPE PMU driver.
*
* Copyright (C) 2020 ARM Ltd.
*
* Author: Anshuman Khandual <anshuman.khandual@arm.com>
*/
#define DRVNAME "arm_trbe"
#define pr_fmt(fmt) DRVNAME ": " fmt
#include <asm/barrier.h>
#include "coresight-trbe.h"
#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT))
/*
* A padding packet that will help the user space tools
* in skipping relevant sections in the captured trace
* data which could not be decoded. TRBE doesn't support
* formatting the trace data, unlike the legacy CoreSight
* sinks and thus we use ETE trace packets to pad the
* sections of the buffer.
*/
#define ETE_IGNORE_PACKET 0x70
/*
* Minimum amount of meaningful trace will contain:
* A-Sync, Trace Info, Trace On, Address, Atom.
* This is about 44bytes of ETE trace. To be on
* the safer side, we assume 64bytes is the minimum
* space required for a meaningful session, before
* we hit a "WRAP" event.
*/
#define TRBE_TRACE_MIN_BUF_SIZE 64
enum
trbe_fault_action
{
TRBE_FAULT_ACT_WRAP
,
TRBE_FAULT_ACT_SPURIOUS
,
TRBE_FAULT_ACT_FATAL
,
};
struct
trbe_buf
{
/*
* Even though trbe_base represents vmap()
* mapped allocated buffer's start address,
* it's being as unsigned long for various
* arithmetic and comparision operations &
* also to be consistent with trbe_write &
* trbe_limit sibling pointers.
*/
unsigned
long
trbe_base
;
unsigned
long
trbe_limit
;
unsigned
long
trbe_write
;
int
nr_pages
;
void
**
pages
;
bool
snapshot
;
struct
trbe_cpudata
*
cpudata
;
};
struct
trbe_cpudata
{
bool
trbe_flag
;
u64
trbe_align
;
int
cpu
;
enum
cs_mode
mode
;
struct
trbe_buf
*
buf
;
struct
trbe_drvdata
*
drvdata
;
};
struct
trbe_drvdata
{
struct
trbe_cpudata
__percpu
*
cpudata
;
struct
perf_output_handle
*
__percpu
*
handle
;
struct
hlist_node
hotplug_node
;
int
irq
;
cpumask_t
supported_cpus
;
enum
cpuhp_state
trbe_online
;
struct
platform_device
*
pdev
;
};
static
int
trbe_alloc_node
(
struct
perf_event
*
event
)
{
if
(
event
->
cpu
==
-
1
)
return
NUMA_NO_NODE
;
return
cpu_to_node
(
event
->
cpu
);
}
static
void
trbe_drain_buffer
(
void
)
{
tsb_csync
();
dsb
(
nsh
);
}
static
void
trbe_drain_and_disable_local
(
void
)
{
u64
trblimitr
=
read_sysreg_s
(
SYS_TRBLIMITR_EL1
);
trbe_drain_buffer
();
/*
* Disable the TRBE without clearing LIMITPTR which
* might be required for fetching the buffer limits.
*/
trblimitr
&=
~
TRBLIMITR_ENABLE
;
write_sysreg_s
(
trblimitr
,
SYS_TRBLIMITR_EL1
);
isb
();
}
static
void
trbe_reset_local
(
void
)
{
trbe_drain_and_disable_local
();
write_sysreg_s
(
0
,
SYS_TRBLIMITR_EL1
);
write_sysreg_s
(
0
,
SYS_TRBPTR_EL1
);
write_sysreg_s
(
0
,
SYS_TRBBASER_EL1
);
write_sysreg_s
(
0
,
SYS_TRBSR_EL1
);
}
static
void
trbe_stop_and_truncate_event
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
/*
* We cannot proceed with the buffer collection and we
* do not have any data for the current session. The
* etm_perf driver expects to close out the aux_buffer
* at event_stop(). So disable the TRBE here and leave
* the update_buffer() to return a 0 size.
*/
trbe_drain_and_disable_local
();
perf_aux_output_flag
(
handle
,
PERF_AUX_FLAG_TRUNCATED
);
*
this_cpu_ptr
(
buf
->
cpudata
->
drvdata
->
handle
)
=
NULL
;
}
/*
* TRBE Buffer Management
*
* The TRBE buffer spans from the base pointer till the limit pointer. When enabled,
* it starts writing trace data from the write pointer onward till the limit pointer.
* When the write pointer reaches the address just before the limit pointer, it gets
* wrapped around again to the base pointer. This is called a TRBE wrap event, which
* generates a maintenance interrupt when operated in WRAP or FILL mode. This driver
* uses FILL mode, where the TRBE stops the trace collection at wrap event. The IRQ
* handler updates the AUX buffer and re-enables the TRBE with updated WRITE and
* LIMIT pointers.
*
* Wrap around with an IRQ
* ------ < ------ < ------- < ----- < -----
* | |
* ------ > ------ > ------- > ----- > -----
*
* +---------------+-----------------------+
* | | |
* +---------------+-----------------------+
* Base Pointer Write Pointer Limit Pointer
*
* The base and limit pointers always needs to be PAGE_SIZE aligned. But the write
* pointer can be aligned to the implementation defined TRBE trace buffer alignment
* as captured in trbe_cpudata->trbe_align.
*
*
* head tail wakeup
* +---------------------------------------+----- ~ ~ ------
* |$$$$$$$|################|$$$$$$$$$$$$$$| |
* +---------------------------------------+----- ~ ~ ------
* Base Pointer Write Pointer Limit Pointer
*
* The perf_output_handle indices (head, tail, wakeup) are monotonically increasing
* values which tracks all the driver writes and user reads from the perf auxiliary
* buffer. Generally [head..tail] is the area where the driver can write into unless
* the wakeup is behind the tail. Enabled TRBE buffer span needs to be adjusted and
* configured depending on the perf_output_handle indices, so that the driver does
* not override into areas in the perf auxiliary buffer which is being or yet to be
* consumed from the user space. The enabled TRBE buffer area is a moving subset of
* the allocated perf auxiliary buffer.
*/
static
void
trbe_pad_buf
(
struct
perf_output_handle
*
handle
,
int
len
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
u64
head
=
PERF_IDX2OFF
(
handle
->
head
,
buf
);
memset
((
void
*
)
buf
->
trbe_base
+
head
,
ETE_IGNORE_PACKET
,
len
);
if
(
!
buf
->
snapshot
)
perf_aux_output_skip
(
handle
,
len
);
}
static
unsigned
long
trbe_snapshot_offset
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
/*
* The ETE trace has alignment synchronization packets allowing
* the decoder to reset in case of an overflow or corruption.
* So we can use the entire buffer for the snapshot mode.
*/
return
buf
->
nr_pages
*
PAGE_SIZE
;
}
/*
* TRBE Limit Calculation
*
* The following markers are used to illustrate various TRBE buffer situations.
*
* $$$$ - Data area, unconsumed captured trace data, not to be overridden
* #### - Free area, enabled, trace will be written
* %%%% - Free area, disabled, trace will not be written
* ==== - Free area, padded with ETE_IGNORE_PACKET, trace will be skipped
*/
static
unsigned
long
__trbe_normal_offset
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
struct
trbe_cpudata
*
cpudata
=
buf
->
cpudata
;
const
u64
bufsize
=
buf
->
nr_pages
*
PAGE_SIZE
;
u64
limit
=
bufsize
;
u64
head
,
tail
,
wakeup
;
head
=
PERF_IDX2OFF
(
handle
->
head
,
buf
);
/*
* head
* ------->|
* |
* head TRBE align tail
* +----|-------|---------------|-------+
* |$$$$|=======|###############|$$$$$$$|
* +----|-------|---------------|-------+
* trbe_base trbe_base + nr_pages
*
* Perf aux buffer output head position can be misaligned depending on
* various factors including user space reads. In case misaligned, head
* needs to be aligned before TRBE can be configured. Pad the alignment
* gap with ETE_IGNORE_PACKET bytes that will be ignored by user tools
* and skip this section thus advancing the head.
*/
if
(
!
IS_ALIGNED
(
head
,
cpudata
->
trbe_align
))
{
unsigned
long
delta
=
roundup
(
head
,
cpudata
->
trbe_align
)
-
head
;
delta
=
min
(
delta
,
handle
->
size
);
trbe_pad_buf
(
handle
,
delta
);
head
=
PERF_IDX2OFF
(
handle
->
head
,
buf
);
}
/*
* head = tail (size = 0)
* +----|-------------------------------+
* |$$$$|$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ |
* +----|-------------------------------+
* trbe_base trbe_base + nr_pages
*
* Perf aux buffer does not have any space for the driver to write into.
* Just communicate trace truncation event to the user space by marking
* it with PERF_AUX_FLAG_TRUNCATED.
*/
if
(
!
handle
->
size
)
{
perf_aux_output_flag
(
handle
,
PERF_AUX_FLAG_TRUNCATED
);
return
0
;
}
/* Compute the tail and wakeup indices now that we've aligned head */
tail
=
PERF_IDX2OFF
(
handle
->
head
+
handle
->
size
,
buf
);
wakeup
=
PERF_IDX2OFF
(
handle
->
wakeup
,
buf
);
/*
* Lets calculate the buffer area which TRBE could write into. There
* are three possible scenarios here. Limit needs to be aligned with
* PAGE_SIZE per the TRBE requirement. Always avoid clobbering the
* unconsumed data.
*
* 1) head < tail
*
* head tail
* +----|-----------------------|-------+
* |$$$$|#######################|$$$$$$$|
* +----|-----------------------|-------+
* trbe_base limit trbe_base + nr_pages
*
* TRBE could write into [head..tail] area. Unless the tail is right at
* the end of the buffer, neither an wrap around nor an IRQ is expected
* while being enabled.
*
* 2) head == tail
*
* head = tail (size > 0)
* +----|-------------------------------+
* |%%%%|###############################|
* +----|-------------------------------+
* trbe_base limit = trbe_base + nr_pages
*
* TRBE should just write into [head..base + nr_pages] area even though
* the entire buffer is empty. Reason being, when the trace reaches the
* end of the buffer, it will just wrap around with an IRQ giving an
* opportunity to reconfigure the buffer.
*
* 3) tail < head
*
* tail head
* +----|-----------------------|-------+
* |%%%%|$$$$$$$$$$$$$$$$$$$$$$$|#######|
* +----|-----------------------|-------+
* trbe_base limit = trbe_base + nr_pages
*
* TRBE should just write into [head..base + nr_pages] area even though
* the [trbe_base..tail] is also empty. Reason being, when the trace
* reaches the end of the buffer, it will just wrap around with an IRQ
* giving an opportunity to reconfigure the buffer.
*/
if
(
head
<
tail
)
limit
=
round_down
(
tail
,
PAGE_SIZE
);
/*
* Wakeup may be arbitrarily far into the future. If it's not in the
* current generation, either we'll wrap before hitting it, or it's
* in the past and has been handled already.
*
* If there's a wakeup before we wrap, arrange to be woken up by the
* page boundary following it. Keep the tail boundary if that's lower.
*
* head wakeup tail
* +----|---------------|-------|-------+
* |$$$$|###############|%%%%%%%|$$$$$$$|
* +----|---------------|-------|-------+
* trbe_base limit trbe_base + nr_pages
*/
if
(
handle
->
wakeup
<
(
handle
->
head
+
handle
->
size
)
&&
head
<=
wakeup
)
limit
=
min
(
limit
,
round_up
(
wakeup
,
PAGE_SIZE
));
/*
* There are two situation when this can happen i.e limit is before
* the head and hence TRBE cannot be configured.
*
* 1) head < tail (aligned down with PAGE_SIZE) and also they are both
* within the same PAGE size range.
*
* PAGE_SIZE
* |----------------------|
*
* limit head tail
* +------------|------|--------|-------+
* |$$$$$$$$$$$$$$$$$$$|========|$$$$$$$|
* +------------|------|--------|-------+
* trbe_base trbe_base + nr_pages
*
* 2) head < wakeup (aligned up with PAGE_SIZE) < tail and also both
* head and wakeup are within same PAGE size range.
*
* PAGE_SIZE
* |----------------------|
*
* limit head wakeup tail
* +----|------|-------|--------|-------+
* |$$$$$$$$$$$|=======|========|$$$$$$$|
* +----|------|-------|--------|-------+
* trbe_base trbe_base + nr_pages
*/
if
(
limit
>
head
)
return
limit
;
trbe_pad_buf
(
handle
,
handle
->
size
);
perf_aux_output_flag
(
handle
,
PERF_AUX_FLAG_TRUNCATED
);
return
0
;
}
static
unsigned
long
trbe_normal_offset
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
perf_get_aux
(
handle
);
u64
limit
=
__trbe_normal_offset
(
handle
);
u64
head
=
PERF_IDX2OFF
(
handle
->
head
,
buf
);
/*
* If the head is too close to the limit and we don't
* have space for a meaningful run, we rather pad it
* and start fresh.
*/
if
(
limit
&&
(
limit
-
head
<
TRBE_TRACE_MIN_BUF_SIZE
))
{
trbe_pad_buf
(
handle
,
limit
-
head
);
limit
=
__trbe_normal_offset
(
handle
);
}
return
limit
;
}
static
unsigned
long
compute_trbe_buffer_limit
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
unsigned
long
offset
;
if
(
buf
->
snapshot
)
offset
=
trbe_snapshot_offset
(
handle
);
else
offset
=
trbe_normal_offset
(
handle
);
return
buf
->
trbe_base
+
offset
;
}
static
void
clr_trbe_status
(
void
)
{
u64
trbsr
=
read_sysreg_s
(
SYS_TRBSR_EL1
);
WARN_ON
(
is_trbe_enabled
());
trbsr
&=
~
TRBSR_IRQ
;
trbsr
&=
~
TRBSR_TRG
;
trbsr
&=
~
TRBSR_WRAP
;
trbsr
&=
~
(
TRBSR_EC_MASK
<<
TRBSR_EC_SHIFT
);
trbsr
&=
~
(
TRBSR_BSC_MASK
<<
TRBSR_BSC_SHIFT
);
trbsr
&=
~
TRBSR_STOP
;
write_sysreg_s
(
trbsr
,
SYS_TRBSR_EL1
);
}
static
void
set_trbe_limit_pointer_enabled
(
unsigned
long
addr
)
{
u64
trblimitr
=
read_sysreg_s
(
SYS_TRBLIMITR_EL1
);
WARN_ON
(
!
IS_ALIGNED
(
addr
,
(
1UL
<<
TRBLIMITR_LIMIT_SHIFT
)));
WARN_ON
(
!
IS_ALIGNED
(
addr
,
PAGE_SIZE
));
trblimitr
&=
~
TRBLIMITR_NVM
;
trblimitr
&=
~
(
TRBLIMITR_FILL_MODE_MASK
<<
TRBLIMITR_FILL_MODE_SHIFT
);
trblimitr
&=
~
(
TRBLIMITR_TRIG_MODE_MASK
<<
TRBLIMITR_TRIG_MODE_SHIFT
);
trblimitr
&=
~
(
TRBLIMITR_LIMIT_MASK
<<
TRBLIMITR_LIMIT_SHIFT
);
/*
* Fill trace buffer mode is used here while configuring the
* TRBE for trace capture. In this particular mode, the trace
* collection is stopped and a maintenance interrupt is raised
* when the current write pointer wraps. This pause in trace
* collection gives the software an opportunity to capture the
* trace data in the interrupt handler, before reconfiguring
* the TRBE.
*/
trblimitr
|=
(
TRBE_FILL_MODE_FILL
&
TRBLIMITR_FILL_MODE_MASK
)
<<
TRBLIMITR_FILL_MODE_SHIFT
;
/*
* Trigger mode is not used here while configuring the TRBE for
* the trace capture. Hence just keep this in the ignore mode.
*/
trblimitr
|=
(
TRBE_TRIG_MODE_IGNORE
&
TRBLIMITR_TRIG_MODE_MASK
)
<<
TRBLIMITR_TRIG_MODE_SHIFT
;
trblimitr
|=
(
addr
&
PAGE_MASK
);
trblimitr
|=
TRBLIMITR_ENABLE
;
write_sysreg_s
(
trblimitr
,
SYS_TRBLIMITR_EL1
);
/* Synchronize the TRBE enable event */
isb
();
}
static
void
trbe_enable_hw
(
struct
trbe_buf
*
buf
)
{
WARN_ON
(
buf
->
trbe_write
<
buf
->
trbe_base
);
WARN_ON
(
buf
->
trbe_write
>=
buf
->
trbe_limit
);
set_trbe_disabled
();
isb
();
clr_trbe_status
();
set_trbe_base_pointer
(
buf
->
trbe_base
);
set_trbe_write_pointer
(
buf
->
trbe_write
);
/*
* Synchronize all the register updates
* till now before enabling the TRBE.
*/
isb
();
set_trbe_limit_pointer_enabled
(
buf
->
trbe_limit
);
}
static
enum
trbe_fault_action
trbe_get_fault_act
(
u64
trbsr
)
{
int
ec
=
get_trbe_ec
(
trbsr
);
int
bsc
=
get_trbe_bsc
(
trbsr
);
WARN_ON
(
is_trbe_running
(
trbsr
));
if
(
is_trbe_trg
(
trbsr
)
||
is_trbe_abort
(
trbsr
))
return
TRBE_FAULT_ACT_FATAL
;
if
((
ec
==
TRBE_EC_STAGE1_ABORT
)
||
(
ec
==
TRBE_EC_STAGE2_ABORT
))
return
TRBE_FAULT_ACT_FATAL
;
if
(
is_trbe_wrap
(
trbsr
)
&&
(
ec
==
TRBE_EC_OTHERS
)
&&
(
bsc
==
TRBE_BSC_FILLED
))
{
if
(
get_trbe_write_pointer
()
==
get_trbe_base_pointer
())
return
TRBE_FAULT_ACT_WRAP
;
}
return
TRBE_FAULT_ACT_SPURIOUS
;
}
static
void
*
arm_trbe_alloc_buffer
(
struct
coresight_device
*
csdev
,
struct
perf_event
*
event
,
void
**
pages
,
int
nr_pages
,
bool
snapshot
)
{
struct
trbe_buf
*
buf
;
struct
page
**
pglist
;
int
i
;
/*
* TRBE LIMIT and TRBE WRITE pointers must be page aligned. But with
* just a single page, there would not be any room left while writing
* into a partially filled TRBE buffer after the page size alignment.
* Hence restrict the minimum buffer size as two pages.
*/
if
(
nr_pages
<
2
)
return
NULL
;
buf
=
kzalloc_node
(
sizeof
(
*
buf
),
GFP_KERNEL
,
trbe_alloc_node
(
event
));
if
(
!
buf
)
return
ERR_PTR
(
-
ENOMEM
);
pglist
=
kcalloc
(
nr_pages
,
sizeof
(
*
pglist
),
GFP_KERNEL
);
if
(
!
pglist
)
{
kfree
(
buf
);
return
ERR_PTR
(
-
ENOMEM
);
}
for
(
i
=
0
;
i
<
nr_pages
;
i
++
)
pglist
[
i
]
=
virt_to_page
(
pages
[
i
]);
buf
->
trbe_base
=
(
unsigned
long
)
vmap
(
pglist
,
nr_pages
,
VM_MAP
,
PAGE_KERNEL
);
if
(
!
buf
->
trbe_base
)
{
kfree
(
pglist
);
kfree
(
buf
);
return
ERR_PTR
(
-
ENOMEM
);
}
buf
->
trbe_limit
=
buf
->
trbe_base
+
nr_pages
*
PAGE_SIZE
;
buf
->
trbe_write
=
buf
->
trbe_base
;
buf
->
snapshot
=
snapshot
;
buf
->
nr_pages
=
nr_pages
;
buf
->
pages
=
pages
;
kfree
(
pglist
);
return
buf
;
}
static
void
arm_trbe_free_buffer
(
void
*
config
)
{
struct
trbe_buf
*
buf
=
config
;
vunmap
((
void
*
)
buf
->
trbe_base
);
kfree
(
buf
);
}
static
unsigned
long
arm_trbe_update_buffer
(
struct
coresight_device
*
csdev
,
struct
perf_output_handle
*
handle
,
void
*
config
)
{
struct
trbe_drvdata
*
drvdata
=
dev_get_drvdata
(
csdev
->
dev
.
parent
);
struct
trbe_cpudata
*
cpudata
=
dev_get_drvdata
(
&
csdev
->
dev
);
struct
trbe_buf
*
buf
=
config
;
enum
trbe_fault_action
act
;
unsigned
long
size
,
offset
;
unsigned
long
write
,
base
,
status
;
unsigned
long
flags
;
WARN_ON
(
buf
->
cpudata
!=
cpudata
);
WARN_ON
(
cpudata
->
cpu
!=
smp_processor_id
());
WARN_ON
(
cpudata
->
drvdata
!=
drvdata
);
if
(
cpudata
->
mode
!=
CS_MODE_PERF
)
return
0
;
perf_aux_output_flag
(
handle
,
PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW
);
/*
* We are about to disable the TRBE. And this could in turn
* fill up the buffer triggering, an IRQ. This could be consumed
* by the PE asynchronously, causing a race here against
* the IRQ handler in closing out the handle. So, let us
* make sure the IRQ can't trigger while we are collecting
* the buffer. We also make sure that a WRAP event is handled
* accordingly.
*/
local_irq_save
(
flags
);
/*
* If the TRBE was disabled due to lack of space in the AUX buffer or a
* spurious fault, the driver leaves it disabled, truncating the buffer.
* Since the etm_perf driver expects to close out the AUX buffer, the
* driver skips it. Thus, just pass in 0 size here to indicate that the
* buffer was truncated.
*/
if
(
!
is_trbe_enabled
())
{
size
=
0
;
goto
done
;
}
/*
* perf handle structure needs to be shared with the TRBE IRQ handler for
* capturing trace data and restarting the handle. There is a probability
* of an undefined reference based crash when etm event is being stopped
* while a TRBE IRQ also getting processed. This happens due the release
* of perf handle via perf_aux_output_end() in etm_event_stop(). Stopping
* the TRBE here will ensure that no IRQ could be generated when the perf
* handle gets freed in etm_event_stop().
*/
trbe_drain_and_disable_local
();
write
=
get_trbe_write_pointer
();
base
=
get_trbe_base_pointer
();
/* Check if there is a pending interrupt and handle it here */
status
=
read_sysreg_s
(
SYS_TRBSR_EL1
);
if
(
is_trbe_irq
(
status
))
{
/*
* Now that we are handling the IRQ here, clear the IRQ
* from the status, to let the irq handler know that it
* is taken care of.
*/
clr_trbe_irq
();
isb
();
act
=
trbe_get_fault_act
(
status
);
/*
* If this was not due to a WRAP event, we have some
* errors and as such buffer is empty.
*/
if
(
act
!=
TRBE_FAULT_ACT_WRAP
)
{
size
=
0
;
goto
done
;
}
/*
* Otherwise, the buffer is full and the write pointer
* has reached base. Adjust this back to the Limit pointer
* for correct size. Also, mark the buffer truncated.
*/
write
=
get_trbe_limit_pointer
();
perf_aux_output_flag
(
handle
,
PERF_AUX_FLAG_TRUNCATED
);
}
offset
=
write
-
base
;
if
(
WARN_ON_ONCE
(
offset
<
PERF_IDX2OFF
(
handle
->
head
,
buf
)))
size
=
0
;
else
size
=
offset
-
PERF_IDX2OFF
(
handle
->
head
,
buf
);
done:
local_irq_restore
(
flags
);
if
(
buf
->
snapshot
)
handle
->
head
+=
size
;
return
size
;
}
static
int
arm_trbe_enable
(
struct
coresight_device
*
csdev
,
u32
mode
,
void
*
data
)
{
struct
trbe_drvdata
*
drvdata
=
dev_get_drvdata
(
csdev
->
dev
.
parent
);
struct
trbe_cpudata
*
cpudata
=
dev_get_drvdata
(
&
csdev
->
dev
);
struct
perf_output_handle
*
handle
=
data
;
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
WARN_ON
(
cpudata
->
cpu
!=
smp_processor_id
());
WARN_ON
(
cpudata
->
drvdata
!=
drvdata
);
if
(
mode
!=
CS_MODE_PERF
)
return
-
EINVAL
;
*
this_cpu_ptr
(
drvdata
->
handle
)
=
handle
;
cpudata
->
buf
=
buf
;
cpudata
->
mode
=
mode
;
buf
->
cpudata
=
cpudata
;
buf
->
trbe_limit
=
compute_trbe_buffer_limit
(
handle
);
buf
->
trbe_write
=
buf
->
trbe_base
+
PERF_IDX2OFF
(
handle
->
head
,
buf
);
if
(
buf
->
trbe_limit
==
buf
->
trbe_base
)
{
trbe_stop_and_truncate_event
(
handle
);
return
0
;
}
trbe_enable_hw
(
buf
);
return
0
;
}
static
int
arm_trbe_disable
(
struct
coresight_device
*
csdev
)
{
struct
trbe_drvdata
*
drvdata
=
dev_get_drvdata
(
csdev
->
dev
.
parent
);
struct
trbe_cpudata
*
cpudata
=
dev_get_drvdata
(
&
csdev
->
dev
);
struct
trbe_buf
*
buf
=
cpudata
->
buf
;
WARN_ON
(
buf
->
cpudata
!=
cpudata
);
WARN_ON
(
cpudata
->
cpu
!=
smp_processor_id
());
WARN_ON
(
cpudata
->
drvdata
!=
drvdata
);
if
(
cpudata
->
mode
!=
CS_MODE_PERF
)
return
-
EINVAL
;
trbe_drain_and_disable_local
();
buf
->
cpudata
=
NULL
;
cpudata
->
buf
=
NULL
;
cpudata
->
mode
=
CS_MODE_DISABLED
;
return
0
;
}
static
void
trbe_handle_spurious
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
buf
->
trbe_limit
=
compute_trbe_buffer_limit
(
handle
);
buf
->
trbe_write
=
buf
->
trbe_base
+
PERF_IDX2OFF
(
handle
->
head
,
buf
);
if
(
buf
->
trbe_limit
==
buf
->
trbe_base
)
{
trbe_drain_and_disable_local
();
return
;
}
trbe_enable_hw
(
buf
);
}
static
void
trbe_handle_overflow
(
struct
perf_output_handle
*
handle
)
{
struct
perf_event
*
event
=
handle
->
event
;
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
unsigned
long
offset
,
size
;
struct
etm_event_data
*
event_data
;
offset
=
get_trbe_limit_pointer
()
-
get_trbe_base_pointer
();
size
=
offset
-
PERF_IDX2OFF
(
handle
->
head
,
buf
);
if
(
buf
->
snapshot
)
handle
->
head
+=
size
;
/*
* Mark the buffer as truncated, as we have stopped the trace
* collection upon the WRAP event, without stopping the source.
*/
perf_aux_output_flag
(
handle
,
PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW
|
PERF_AUX_FLAG_TRUNCATED
);
perf_aux_output_end
(
handle
,
size
);
event_data
=
perf_aux_output_begin
(
handle
,
event
);
if
(
!
event_data
)
{
/*
* We are unable to restart the trace collection,
* thus leave the TRBE disabled. The etm-perf driver
* is able to detect this with a disconnected handle
* (handle->event = NULL).
*/
trbe_drain_and_disable_local
();
*
this_cpu_ptr
(
buf
->
cpudata
->
drvdata
->
handle
)
=
NULL
;
return
;
}
buf
->
trbe_limit
=
compute_trbe_buffer_limit
(
handle
);
buf
->
trbe_write
=
buf
->
trbe_base
+
PERF_IDX2OFF
(
handle
->
head
,
buf
);
if
(
buf
->
trbe_limit
==
buf
->
trbe_base
)
{
trbe_stop_and_truncate_event
(
handle
);
return
;
}
*
this_cpu_ptr
(
buf
->
cpudata
->
drvdata
->
handle
)
=
handle
;
trbe_enable_hw
(
buf
);
}
static
bool
is_perf_trbe
(
struct
perf_output_handle
*
handle
)
{
struct
trbe_buf
*
buf
=
etm_perf_sink_config
(
handle
);
struct
trbe_cpudata
*
cpudata
=
buf
->
cpudata
;
struct
trbe_drvdata
*
drvdata
=
cpudata
->
drvdata
;
int
cpu
=
smp_processor_id
();
WARN_ON
(
buf
->
trbe_base
!=
get_trbe_base_pointer
());
WARN_ON
(
buf
->
trbe_limit
!=
get_trbe_limit_pointer
());
if
(
cpudata
->
mode
!=
CS_MODE_PERF
)
return
false
;
if
(
cpudata
->
cpu
!=
cpu
)
return
false
;
if
(
!
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
return
false
;
return
true
;
}
static
irqreturn_t
arm_trbe_irq_handler
(
int
irq
,
void
*
dev
)
{
struct
perf_output_handle
**
handle_ptr
=
dev
;
struct
perf_output_handle
*
handle
=
*
handle_ptr
;
enum
trbe_fault_action
act
;
u64
status
;
/*
* Ensure the trace is visible to the CPUs and
* any external aborts have been resolved.
*/
trbe_drain_and_disable_local
();
status
=
read_sysreg_s
(
SYS_TRBSR_EL1
);
/*
* If the pending IRQ was handled by update_buffer callback
* we have nothing to do here.
*/
if
(
!
is_trbe_irq
(
status
))
return
IRQ_NONE
;
clr_trbe_irq
();
isb
();
if
(
WARN_ON_ONCE
(
!
handle
)
||
!
perf_get_aux
(
handle
))
return
IRQ_NONE
;
if
(
!
is_perf_trbe
(
handle
))
return
IRQ_NONE
;
/*
* Ensure perf callbacks have completed, which may disable
* the trace buffer in response to a TRUNCATION flag.
*/
irq_work_run
();
act
=
trbe_get_fault_act
(
status
);
switch
(
act
)
{
case
TRBE_FAULT_ACT_WRAP
:
trbe_handle_overflow
(
handle
);
break
;
case
TRBE_FAULT_ACT_SPURIOUS
:
trbe_handle_spurious
(
handle
);
break
;
case
TRBE_FAULT_ACT_FATAL
:
trbe_stop_and_truncate_event
(
handle
);
break
;
}
return
IRQ_HANDLED
;
}
static
const
struct
coresight_ops_sink
arm_trbe_sink_ops
=
{
.
enable
=
arm_trbe_enable
,
.
disable
=
arm_trbe_disable
,
.
alloc_buffer
=
arm_trbe_alloc_buffer
,
.
free_buffer
=
arm_trbe_free_buffer
,
.
update_buffer
=
arm_trbe_update_buffer
,
};
static
const
struct
coresight_ops
arm_trbe_cs_ops
=
{
.
sink_ops
=
&
arm_trbe_sink_ops
,
};
static
ssize_t
align_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
trbe_cpudata
*
cpudata
=
dev_get_drvdata
(
dev
);
return
sprintf
(
buf
,
"%llx
\n
"
,
cpudata
->
trbe_align
);
}
static
DEVICE_ATTR_RO
(
align
);
static
ssize_t
flag_show
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
trbe_cpudata
*
cpudata
=
dev_get_drvdata
(
dev
);
return
sprintf
(
buf
,
"%d
\n
"
,
cpudata
->
trbe_flag
);
}
static
DEVICE_ATTR_RO
(
flag
);
static
struct
attribute
*
arm_trbe_attrs
[]
=
{
&
dev_attr_align
.
attr
,
&
dev_attr_flag
.
attr
,
NULL
,
};
static
const
struct
attribute_group
arm_trbe_group
=
{
.
attrs
=
arm_trbe_attrs
,
};
static
const
struct
attribute_group
*
arm_trbe_groups
[]
=
{
&
arm_trbe_group
,
NULL
,
};
static
void
arm_trbe_enable_cpu
(
void
*
info
)
{
struct
trbe_drvdata
*
drvdata
=
info
;
trbe_reset_local
();
enable_percpu_irq
(
drvdata
->
irq
,
IRQ_TYPE_NONE
);
}
static
void
arm_trbe_register_coresight_cpu
(
struct
trbe_drvdata
*
drvdata
,
int
cpu
)
{
struct
trbe_cpudata
*
cpudata
=
per_cpu_ptr
(
drvdata
->
cpudata
,
cpu
);
struct
coresight_device
*
trbe_csdev
=
coresight_get_percpu_sink
(
cpu
);
struct
coresight_desc
desc
=
{
0
};
struct
device
*
dev
;
if
(
WARN_ON
(
trbe_csdev
))
return
;
dev
=
&
cpudata
->
drvdata
->
pdev
->
dev
;
desc
.
name
=
devm_kasprintf
(
dev
,
GFP_KERNEL
,
"trbe%d"
,
cpu
);
if
(
IS_ERR
(
desc
.
name
))
goto
cpu_clear
;
desc
.
type
=
CORESIGHT_DEV_TYPE_SINK
;
desc
.
subtype
.
sink_subtype
=
CORESIGHT_DEV_SUBTYPE_SINK_PERCPU_SYSMEM
;
desc
.
ops
=
&
arm_trbe_cs_ops
;
desc
.
pdata
=
dev_get_platdata
(
dev
);
desc
.
groups
=
arm_trbe_groups
;
desc
.
dev
=
dev
;
trbe_csdev
=
coresight_register
(
&
desc
);
if
(
IS_ERR
(
trbe_csdev
))
goto
cpu_clear
;
dev_set_drvdata
(
&
trbe_csdev
->
dev
,
cpudata
);
coresight_set_percpu_sink
(
cpu
,
trbe_csdev
);
return
;
cpu_clear:
cpumask_clear_cpu
(
cpu
,
&
drvdata
->
supported_cpus
);
}
static
void
arm_trbe_probe_cpu
(
void
*
info
)
{
struct
trbe_drvdata
*
drvdata
=
info
;
int
cpu
=
smp_processor_id
();
struct
trbe_cpudata
*
cpudata
=
per_cpu_ptr
(
drvdata
->
cpudata
,
cpu
);
u64
trbidr
;
if
(
WARN_ON
(
!
cpudata
))
goto
cpu_clear
;
if
(
!
is_trbe_available
())
{
pr_err
(
"TRBE is not implemented on cpu %d
\n
"
,
cpu
);
goto
cpu_clear
;
}
trbidr
=
read_sysreg_s
(
SYS_TRBIDR_EL1
);
if
(
!
is_trbe_programmable
(
trbidr
))
{
pr_err
(
"TRBE is owned in higher exception level on cpu %d
\n
"
,
cpu
);
goto
cpu_clear
;
}
cpudata
->
trbe_align
=
1ULL
<<
get_trbe_address_align
(
trbidr
);
if
(
cpudata
->
trbe_align
>
SZ_2K
)
{
pr_err
(
"Unsupported alignment on cpu %d
\n
"
,
cpu
);
goto
cpu_clear
;
}
cpudata
->
trbe_flag
=
get_trbe_flag_update
(
trbidr
);
cpudata
->
cpu
=
cpu
;
cpudata
->
drvdata
=
drvdata
;
return
;
cpu_clear:
cpumask_clear_cpu
(
cpu
,
&
drvdata
->
supported_cpus
);
}
static
void
arm_trbe_remove_coresight_cpu
(
void
*
info
)
{
int
cpu
=
smp_processor_id
();
struct
trbe_drvdata
*
drvdata
=
info
;
struct
trbe_cpudata
*
cpudata
=
per_cpu_ptr
(
drvdata
->
cpudata
,
cpu
);
struct
coresight_device
*
trbe_csdev
=
coresight_get_percpu_sink
(
cpu
);
disable_percpu_irq
(
drvdata
->
irq
);
trbe_reset_local
();
if
(
trbe_csdev
)
{
coresight_unregister
(
trbe_csdev
);
cpudata
->
drvdata
=
NULL
;
coresight_set_percpu_sink
(
cpu
,
NULL
);
}
}
static
int
arm_trbe_probe_coresight
(
struct
trbe_drvdata
*
drvdata
)
{
int
cpu
;
drvdata
->
cpudata
=
alloc_percpu
(
typeof
(
*
drvdata
->
cpudata
));
if
(
!
drvdata
->
cpudata
)
return
-
ENOMEM
;
for_each_cpu
(
cpu
,
&
drvdata
->
supported_cpus
)
{
smp_call_function_single
(
cpu
,
arm_trbe_probe_cpu
,
drvdata
,
1
);
if
(
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
arm_trbe_register_coresight_cpu
(
drvdata
,
cpu
);
if
(
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
smp_call_function_single
(
cpu
,
arm_trbe_enable_cpu
,
drvdata
,
1
);
}
return
0
;
}
static
int
arm_trbe_remove_coresight
(
struct
trbe_drvdata
*
drvdata
)
{
int
cpu
;
for_each_cpu
(
cpu
,
&
drvdata
->
supported_cpus
)
smp_call_function_single
(
cpu
,
arm_trbe_remove_coresight_cpu
,
drvdata
,
1
);
free_percpu
(
drvdata
->
cpudata
);
return
0
;
}
static
int
arm_trbe_cpu_startup
(
unsigned
int
cpu
,
struct
hlist_node
*
node
)
{
struct
trbe_drvdata
*
drvdata
=
hlist_entry_safe
(
node
,
struct
trbe_drvdata
,
hotplug_node
);
if
(
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
{
/*
* If this CPU was not probed for TRBE,
* initialize it now.
*/
if
(
!
coresight_get_percpu_sink
(
cpu
))
{
arm_trbe_probe_cpu
(
drvdata
);
if
(
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
arm_trbe_register_coresight_cpu
(
drvdata
,
cpu
);
if
(
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
arm_trbe_enable_cpu
(
drvdata
);
}
else
{
arm_trbe_enable_cpu
(
drvdata
);
}
}
return
0
;
}
static
int
arm_trbe_cpu_teardown
(
unsigned
int
cpu
,
struct
hlist_node
*
node
)
{
struct
trbe_drvdata
*
drvdata
=
hlist_entry_safe
(
node
,
struct
trbe_drvdata
,
hotplug_node
);
if
(
cpumask_test_cpu
(
cpu
,
&
drvdata
->
supported_cpus
))
{
disable_percpu_irq
(
drvdata
->
irq
);
trbe_reset_local
();
}
return
0
;
}
static
int
arm_trbe_probe_cpuhp
(
struct
trbe_drvdata
*
drvdata
)
{
enum
cpuhp_state
trbe_online
;
int
ret
;
trbe_online
=
cpuhp_setup_state_multi
(
CPUHP_AP_ONLINE_DYN
,
DRVNAME
,
arm_trbe_cpu_startup
,
arm_trbe_cpu_teardown
);
if
(
trbe_online
<
0
)
return
trbe_online
;
ret
=
cpuhp_state_add_instance
(
trbe_online
,
&
drvdata
->
hotplug_node
);
if
(
ret
)
{
cpuhp_remove_multi_state
(
trbe_online
);
return
ret
;
}
drvdata
->
trbe_online
=
trbe_online
;
return
0
;
}
static
void
arm_trbe_remove_cpuhp
(
struct
trbe_drvdata
*
drvdata
)
{
cpuhp_remove_multi_state
(
drvdata
->
trbe_online
);
}
static
int
arm_trbe_probe_irq
(
struct
platform_device
*
pdev
,
struct
trbe_drvdata
*
drvdata
)
{
int
ret
;
drvdata
->
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
drvdata
->
irq
<
0
)
{
pr_err
(
"IRQ not found for the platform device
\n
"
);
return
drvdata
->
irq
;
}
if
(
!
irq_is_percpu
(
drvdata
->
irq
))
{
pr_err
(
"IRQ is not a PPI
\n
"
);
return
-
EINVAL
;
}
if
(
irq_get_percpu_devid_partition
(
drvdata
->
irq
,
&
drvdata
->
supported_cpus
))
return
-
EINVAL
;
drvdata
->
handle
=
alloc_percpu
(
struct
perf_output_handle
*
);
if
(
!
drvdata
->
handle
)
return
-
ENOMEM
;
ret
=
request_percpu_irq
(
drvdata
->
irq
,
arm_trbe_irq_handler
,
DRVNAME
,
drvdata
->
handle
);
if
(
ret
)
{
free_percpu
(
drvdata
->
handle
);
return
ret
;
}
return
0
;
}
static
void
arm_trbe_remove_irq
(
struct
trbe_drvdata
*
drvdata
)
{
free_percpu_irq
(
drvdata
->
irq
,
drvdata
->
handle
);
free_percpu
(
drvdata
->
handle
);
}
static
int
arm_trbe_device_probe
(
struct
platform_device
*
pdev
)
{
struct
coresight_platform_data
*
pdata
;
struct
trbe_drvdata
*
drvdata
;
struct
device
*
dev
=
&
pdev
->
dev
;
int
ret
;
drvdata
=
devm_kzalloc
(
dev
,
sizeof
(
*
drvdata
),
GFP_KERNEL
);
if
(
!
drvdata
)
return
-
ENOMEM
;
pdata
=
coresight_get_platform_data
(
dev
);
if
(
IS_ERR
(
pdata
))
return
PTR_ERR
(
pdata
);
dev_set_drvdata
(
dev
,
drvdata
);
dev
->
platform_data
=
pdata
;
drvdata
->
pdev
=
pdev
;
ret
=
arm_trbe_probe_irq
(
pdev
,
drvdata
);
if
(
ret
)
return
ret
;
ret
=
arm_trbe_probe_coresight
(
drvdata
);
if
(
ret
)
goto
probe_failed
;
ret
=
arm_trbe_probe_cpuhp
(
drvdata
);
if
(
ret
)
goto
cpuhp_failed
;
return
0
;
cpuhp_failed:
arm_trbe_remove_coresight
(
drvdata
);
probe_failed:
arm_trbe_remove_irq
(
drvdata
);
return
ret
;
}
static
int
arm_trbe_device_remove
(
struct
platform_device
*
pdev
)
{
struct
trbe_drvdata
*
drvdata
=
platform_get_drvdata
(
pdev
);
arm_trbe_remove_cpuhp
(
drvdata
);
arm_trbe_remove_coresight
(
drvdata
);
arm_trbe_remove_irq
(
drvdata
);
return
0
;
}
static
const
struct
of_device_id
arm_trbe_of_match
[]
=
{
{
.
compatible
=
"arm,trace-buffer-extension"
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
arm_trbe_of_match
);
static
struct
platform_driver
arm_trbe_driver
=
{
.
driver
=
{
.
name
=
DRVNAME
,
.
of_match_table
=
of_match_ptr
(
arm_trbe_of_match
),
.
suppress_bind_attrs
=
true
,
},
.
probe
=
arm_trbe_device_probe
,
.
remove
=
arm_trbe_device_remove
,
};
static
int
__init
arm_trbe_init
(
void
)
{
int
ret
;
if
(
arm64_kernel_unmapped_at_el0
())
{
pr_err
(
"TRBE wouldn't work if kernel gets unmapped at EL0
\n
"
);
return
-
EOPNOTSUPP
;
}
ret
=
platform_driver_register
(
&
arm_trbe_driver
);
if
(
!
ret
)
return
0
;
pr_err
(
"Error registering %s platform driver
\n
"
,
DRVNAME
);
return
ret
;
}
static
void
__exit
arm_trbe_exit
(
void
)
{
platform_driver_unregister
(
&
arm_trbe_driver
);
}
module_init
(
arm_trbe_init
);
module_exit
(
arm_trbe_exit
);
MODULE_AUTHOR
(
"Anshuman Khandual <anshuman.khandual@arm.com>"
);
MODULE_DESCRIPTION
(
"Arm Trace Buffer Extension (TRBE) driver"
);
MODULE_LICENSE
(
"GPL v2"
);
drivers/hwtracing/coresight/coresight-trbe.h
0 → 100644
View file @
53648ed3
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This contains all required hardware related helper functions for
* Trace Buffer Extension (TRBE) driver in the coresight framework.
*
* Copyright (C) 2020 ARM Ltd.
*
* Author: Anshuman Khandual <anshuman.khandual@arm.com>
*/
#include <linux/coresight.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/smp.h>
#include "coresight-etm-perf.h"
static
inline
bool
is_trbe_available
(
void
)
{
u64
aa64dfr0
=
read_sysreg_s
(
SYS_ID_AA64DFR0_EL1
);
unsigned
int
trbe
=
cpuid_feature_extract_unsigned_field
(
aa64dfr0
,
ID_AA64DFR0_TRBE_SHIFT
);
return
trbe
>=
0
b0001
;
}
static
inline
bool
is_trbe_enabled
(
void
)
{
u64
trblimitr
=
read_sysreg_s
(
SYS_TRBLIMITR_EL1
);
return
trblimitr
&
TRBLIMITR_ENABLE
;
}
#define TRBE_EC_OTHERS 0
#define TRBE_EC_STAGE1_ABORT 36
#define TRBE_EC_STAGE2_ABORT 37
static
inline
int
get_trbe_ec
(
u64
trbsr
)
{
return
(
trbsr
>>
TRBSR_EC_SHIFT
)
&
TRBSR_EC_MASK
;
}
#define TRBE_BSC_NOT_STOPPED 0
#define TRBE_BSC_FILLED 1
#define TRBE_BSC_TRIGGERED 2
static
inline
int
get_trbe_bsc
(
u64
trbsr
)
{
return
(
trbsr
>>
TRBSR_BSC_SHIFT
)
&
TRBSR_BSC_MASK
;
}
static
inline
void
clr_trbe_irq
(
void
)
{
u64
trbsr
=
read_sysreg_s
(
SYS_TRBSR_EL1
);
trbsr
&=
~
TRBSR_IRQ
;
write_sysreg_s
(
trbsr
,
SYS_TRBSR_EL1
);
}
static
inline
bool
is_trbe_irq
(
u64
trbsr
)
{
return
trbsr
&
TRBSR_IRQ
;
}
static
inline
bool
is_trbe_trg
(
u64
trbsr
)
{
return
trbsr
&
TRBSR_TRG
;
}
static
inline
bool
is_trbe_wrap
(
u64
trbsr
)
{
return
trbsr
&
TRBSR_WRAP
;
}
static
inline
bool
is_trbe_abort
(
u64
trbsr
)
{
return
trbsr
&
TRBSR_ABORT
;
}
static
inline
bool
is_trbe_running
(
u64
trbsr
)
{
return
!
(
trbsr
&
TRBSR_STOP
);
}
#define TRBE_TRIG_MODE_STOP 0
#define TRBE_TRIG_MODE_IRQ 1
#define TRBE_TRIG_MODE_IGNORE 3
#define TRBE_FILL_MODE_FILL 0
#define TRBE_FILL_MODE_WRAP 1
#define TRBE_FILL_MODE_CIRCULAR_BUFFER 3
static
inline
void
set_trbe_disabled
(
void
)
{
u64
trblimitr
=
read_sysreg_s
(
SYS_TRBLIMITR_EL1
);
trblimitr
&=
~
TRBLIMITR_ENABLE
;
write_sysreg_s
(
trblimitr
,
SYS_TRBLIMITR_EL1
);
}
static
inline
bool
get_trbe_flag_update
(
u64
trbidr
)
{
return
trbidr
&
TRBIDR_FLAG
;
}
static
inline
bool
is_trbe_programmable
(
u64
trbidr
)
{
return
!
(
trbidr
&
TRBIDR_PROG
);
}
static
inline
int
get_trbe_address_align
(
u64
trbidr
)
{
return
(
trbidr
>>
TRBIDR_ALIGN_SHIFT
)
&
TRBIDR_ALIGN_MASK
;
}
static
inline
unsigned
long
get_trbe_write_pointer
(
void
)
{
return
read_sysreg_s
(
SYS_TRBPTR_EL1
);
}
static
inline
void
set_trbe_write_pointer
(
unsigned
long
addr
)
{
WARN_ON
(
is_trbe_enabled
());
write_sysreg_s
(
addr
,
SYS_TRBPTR_EL1
);
}
static
inline
unsigned
long
get_trbe_limit_pointer
(
void
)
{
u64
trblimitr
=
read_sysreg_s
(
SYS_TRBLIMITR_EL1
);
unsigned
long
addr
=
trblimitr
&
(
TRBLIMITR_LIMIT_MASK
<<
TRBLIMITR_LIMIT_SHIFT
);
WARN_ON
(
!
IS_ALIGNED
(
addr
,
PAGE_SIZE
));
return
addr
;
}
static
inline
unsigned
long
get_trbe_base_pointer
(
void
)
{
u64
trbbaser
=
read_sysreg_s
(
SYS_TRBBASER_EL1
);
unsigned
long
addr
=
trbbaser
&
(
TRBBASER_BASE_MASK
<<
TRBBASER_BASE_SHIFT
);
WARN_ON
(
!
IS_ALIGNED
(
addr
,
PAGE_SIZE
));
return
addr
;
}
static
inline
void
set_trbe_base_pointer
(
unsigned
long
addr
)
{
WARN_ON
(
is_trbe_enabled
());
WARN_ON
(
!
IS_ALIGNED
(
addr
,
(
1UL
<<
TRBBASER_BASE_SHIFT
)));
WARN_ON
(
!
IS_ALIGNED
(
addr
,
PAGE_SIZE
));
write_sysreg_s
(
addr
,
SYS_TRBBASER_EL1
);
}
include/linux/coresight.h
View file @
53648ed3
...
...
@@ -50,6 +50,7 @@ enum coresight_dev_subtype_sink {
CORESIGHT_DEV_SUBTYPE_SINK_PORT
,
CORESIGHT_DEV_SUBTYPE_SINK_BUFFER
,
CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM
,
CORESIGHT_DEV_SUBTYPE_SINK_PERCPU_SYSMEM
,
};
enum
coresight_dev_subtype_link
{
...
...
@@ -455,6 +456,18 @@ static inline void csdev_access_write64(struct csdev_access *csa, u64 val, u32 o
}
#endif
/* CONFIG_64BIT */
static
inline
bool
coresight_is_percpu_source
(
struct
coresight_device
*
csdev
)
{
return
csdev
&&
(
csdev
->
type
==
CORESIGHT_DEV_TYPE_SOURCE
)
&&
(
csdev
->
subtype
.
source_subtype
==
CORESIGHT_DEV_SUBTYPE_SOURCE_PROC
);
}
static
inline
bool
coresight_is_percpu_sink
(
struct
coresight_device
*
csdev
)
{
return
csdev
&&
(
csdev
->
type
==
CORESIGHT_DEV_TYPE_SINK
)
&&
(
csdev
->
subtype
.
sink_subtype
==
CORESIGHT_DEV_SUBTYPE_SINK_PERCPU_SYSMEM
);
}
extern
struct
coresight_device
*
coresight_register
(
struct
coresight_desc
*
desc
);
extern
void
coresight_unregister
(
struct
coresight_device
*
csdev
);
...
...
include/uapi/linux/perf_event.h
View file @
53648ed3
...
...
@@ -1160,6 +1160,11 @@ enum perf_callchain_context {
#define PERF_AUX_FLAG_OVERWRITE 0x02
/* snapshot from overwrite mode */
#define PERF_AUX_FLAG_PARTIAL 0x04
/* record contains gaps */
#define PERF_AUX_FLAG_COLLISION 0x08
/* sample collided with another */
#define PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK 0xff00
/* PMU specific trace format type */
/* CoreSight PMU AUX buffer formats */
#define PERF_AUX_FLAG_CORESIGHT_FORMAT_CORESIGHT 0x0000
/* Default for backward compatibility */
#define PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW 0x0100
/* Raw format of the source */
#define PERF_FLAG_FD_NO_GROUP (1UL << 0)
#define PERF_FLAG_FD_OUTPUT (1UL << 1)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment