xref: /openbmc/linux/arch/x86/events/amd/lbr.c (revision 1e952e95843d437b8a904dbd5b48d72db8ac23ec)
1703fb765SSandipan Das // SPDX-License-Identifier: GPL-2.0
2703fb765SSandipan Das #include <linux/perf_event.h>
3703fb765SSandipan Das #include <asm/perf_event.h>
4703fb765SSandipan Das 
5703fb765SSandipan Das #include "../perf_event.h"
6703fb765SSandipan Das 
7f4f925daSSandipan Das /* LBR Branch Select valid bits */
8f4f925daSSandipan Das #define LBR_SELECT_MASK		0x1ff
9f4f925daSSandipan Das 
10f4f925daSSandipan Das /*
11f4f925daSSandipan Das  * LBR Branch Select filter bits which when set, ensures that the
12f4f925daSSandipan Das  * corresponding type of branches are not recorded
13f4f925daSSandipan Das  */
14f4f925daSSandipan Das #define LBR_SELECT_KERNEL		0	/* Branches ending in CPL = 0 */
15f4f925daSSandipan Das #define LBR_SELECT_USER			1	/* Branches ending in CPL > 0 */
16f4f925daSSandipan Das #define LBR_SELECT_JCC			2	/* Conditional branches */
17f4f925daSSandipan Das #define LBR_SELECT_CALL_NEAR_REL	3	/* Near relative calls */
18f4f925daSSandipan Das #define LBR_SELECT_CALL_NEAR_IND	4	/* Indirect relative calls */
19f4f925daSSandipan Das #define LBR_SELECT_RET_NEAR		5	/* Near returns */
20f4f925daSSandipan Das #define LBR_SELECT_JMP_NEAR_IND		6	/* Near indirect jumps (excl. calls and returns) */
21f4f925daSSandipan Das #define LBR_SELECT_JMP_NEAR_REL		7	/* Near relative jumps (excl. calls) */
22f4f925daSSandipan Das #define LBR_SELECT_FAR_BRANCH		8	/* Far branches */
23f4f925daSSandipan Das 
24f4f925daSSandipan Das #define LBR_KERNEL	BIT(LBR_SELECT_KERNEL)
25f4f925daSSandipan Das #define LBR_USER	BIT(LBR_SELECT_USER)
26f4f925daSSandipan Das #define LBR_JCC		BIT(LBR_SELECT_JCC)
27f4f925daSSandipan Das #define LBR_REL_CALL	BIT(LBR_SELECT_CALL_NEAR_REL)
28f4f925daSSandipan Das #define LBR_IND_CALL	BIT(LBR_SELECT_CALL_NEAR_IND)
29f4f925daSSandipan Das #define LBR_RETURN	BIT(LBR_SELECT_RET_NEAR)
30f4f925daSSandipan Das #define LBR_REL_JMP	BIT(LBR_SELECT_JMP_NEAR_REL)
31f4f925daSSandipan Das #define LBR_IND_JMP	BIT(LBR_SELECT_JMP_NEAR_IND)
32f4f925daSSandipan Das #define LBR_FAR		BIT(LBR_SELECT_FAR_BRANCH)
33f4f925daSSandipan Das #define LBR_NOT_SUPP	-1	/* unsupported filter */
34f4f925daSSandipan Das #define LBR_IGNORE	0
35f4f925daSSandipan Das 
36f4f925daSSandipan Das #define LBR_ANY		\
37f4f925daSSandipan Das 	(LBR_JCC | LBR_REL_CALL | LBR_IND_CALL | LBR_RETURN |	\
38f4f925daSSandipan Das 	 LBR_REL_JMP | LBR_IND_JMP | LBR_FAR)
39f4f925daSSandipan Das 
40ca5b7c0dSSandipan Das struct branch_entry {
41ca5b7c0dSSandipan Das 	union {
42ca5b7c0dSSandipan Das 		struct {
43ca5b7c0dSSandipan Das 			u64	ip:58;
44ca5b7c0dSSandipan Das 			u64	ip_sign_ext:5;
45ca5b7c0dSSandipan Das 			u64	mispredict:1;
46ca5b7c0dSSandipan Das 		} split;
47ca5b7c0dSSandipan Das 		u64		full;
48ca5b7c0dSSandipan Das 	} from;
49ca5b7c0dSSandipan Das 
50ca5b7c0dSSandipan Das 	union {
51ca5b7c0dSSandipan Das 		struct {
52ca5b7c0dSSandipan Das 			u64	ip:58;
53ca5b7c0dSSandipan Das 			u64	ip_sign_ext:3;
54ca5b7c0dSSandipan Das 			u64	reserved:1;
55ca5b7c0dSSandipan Das 			u64	spec:1;
56ca5b7c0dSSandipan Das 			u64	valid:1;
57ca5b7c0dSSandipan Das 		} split;
58ca5b7c0dSSandipan Das 		u64		full;
59ca5b7c0dSSandipan Das 	} to;
60ca5b7c0dSSandipan Das };
61ca5b7c0dSSandipan Das 
amd_pmu_lbr_set_from(unsigned int idx,u64 val)62ca5b7c0dSSandipan Das static __always_inline void amd_pmu_lbr_set_from(unsigned int idx, u64 val)
63ca5b7c0dSSandipan Das {
64ca5b7c0dSSandipan Das 	wrmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2, val);
65ca5b7c0dSSandipan Das }
66ca5b7c0dSSandipan Das 
amd_pmu_lbr_set_to(unsigned int idx,u64 val)67ca5b7c0dSSandipan Das static __always_inline void amd_pmu_lbr_set_to(unsigned int idx, u64 val)
68ca5b7c0dSSandipan Das {
69ca5b7c0dSSandipan Das 	wrmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val);
70ca5b7c0dSSandipan Das }
71ca5b7c0dSSandipan Das 
amd_pmu_lbr_get_from(unsigned int idx)72ca5b7c0dSSandipan Das static __always_inline u64 amd_pmu_lbr_get_from(unsigned int idx)
73ca5b7c0dSSandipan Das {
74ca5b7c0dSSandipan Das 	u64 val;
75ca5b7c0dSSandipan Das 
76ca5b7c0dSSandipan Das 	rdmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2, val);
77ca5b7c0dSSandipan Das 
78ca5b7c0dSSandipan Das 	return val;
79ca5b7c0dSSandipan Das }
80ca5b7c0dSSandipan Das 
amd_pmu_lbr_get_to(unsigned int idx)81ca5b7c0dSSandipan Das static __always_inline u64 amd_pmu_lbr_get_to(unsigned int idx)
82ca5b7c0dSSandipan Das {
83ca5b7c0dSSandipan Das 	u64 val;
84ca5b7c0dSSandipan Das 
85ca5b7c0dSSandipan Das 	rdmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val);
86ca5b7c0dSSandipan Das 
87ca5b7c0dSSandipan Das 	return val;
88ca5b7c0dSSandipan Das }
89ca5b7c0dSSandipan Das 
sign_ext_branch_ip(u64 ip)90ca5b7c0dSSandipan Das static __always_inline u64 sign_ext_branch_ip(u64 ip)
91ca5b7c0dSSandipan Das {
92ca5b7c0dSSandipan Das 	u32 shift = 64 - boot_cpu_data.x86_virt_bits;
93ca5b7c0dSSandipan Das 
94ca5b7c0dSSandipan Das 	return (u64)(((s64)ip << shift) >> shift);
95ca5b7c0dSSandipan Das }
96ca5b7c0dSSandipan Das 
amd_pmu_lbr_filter(void)97f9c73224SSandipan Das static void amd_pmu_lbr_filter(void)
98f9c73224SSandipan Das {
99f9c73224SSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
100245268c1SSandipan Das 	int br_sel = cpuc->br_sel, offset, type, i, j;
101f9c73224SSandipan Das 	bool compress = false;
1023f9a1b35SStephane Eranian 	bool fused_only = false;
103f9c73224SSandipan Das 	u64 from, to;
104f9c73224SSandipan Das 
105f9c73224SSandipan Das 	/* If sampling all branches, there is nothing to filter */
106f9c73224SSandipan Das 	if (((br_sel & X86_BR_ALL) == X86_BR_ALL) &&
107f9c73224SSandipan Das 	    ((br_sel & X86_BR_TYPE_SAVE) != X86_BR_TYPE_SAVE))
1083f9a1b35SStephane Eranian 		fused_only = true;
109f9c73224SSandipan Das 
110f9c73224SSandipan Das 	for (i = 0; i < cpuc->lbr_stack.nr; i++) {
111f9c73224SSandipan Das 		from = cpuc->lbr_entries[i].from;
112f9c73224SSandipan Das 		to = cpuc->lbr_entries[i].to;
113245268c1SSandipan Das 		type = branch_type_fused(from, to, 0, &offset);
114245268c1SSandipan Das 
115245268c1SSandipan Das 		/*
116245268c1SSandipan Das 		 * Adjust the branch from address in case of instruction
117245268c1SSandipan Das 		 * fusion where it points to an instruction preceding the
118245268c1SSandipan Das 		 * actual branch
119245268c1SSandipan Das 		 */
1203f9a1b35SStephane Eranian 		if (offset) {
121245268c1SSandipan Das 			cpuc->lbr_entries[i].from += offset;
1223f9a1b35SStephane Eranian 			if (fused_only)
1233f9a1b35SStephane Eranian 				continue;
1243f9a1b35SStephane Eranian 		}
125f9c73224SSandipan Das 
126f9c73224SSandipan Das 		/* If type does not correspond, then discard */
127f9c73224SSandipan Das 		if (type == X86_BR_NONE || (br_sel & type) != type) {
128f9c73224SSandipan Das 			cpuc->lbr_entries[i].from = 0;	/* mark invalid */
129f9c73224SSandipan Das 			compress = true;
130f9c73224SSandipan Das 		}
131f9c73224SSandipan Das 
132f9c73224SSandipan Das 		if ((br_sel & X86_BR_TYPE_SAVE) == X86_BR_TYPE_SAVE)
133f9c73224SSandipan Das 			cpuc->lbr_entries[i].type = common_branch_type(type);
134f9c73224SSandipan Das 	}
135f9c73224SSandipan Das 
136f9c73224SSandipan Das 	if (!compress)
137f9c73224SSandipan Das 		return;
138f9c73224SSandipan Das 
139f9c73224SSandipan Das 	/* Remove all invalid entries */
140f9c73224SSandipan Das 	for (i = 0; i < cpuc->lbr_stack.nr; ) {
141f9c73224SSandipan Das 		if (!cpuc->lbr_entries[i].from) {
142f9c73224SSandipan Das 			j = i;
143f9c73224SSandipan Das 			while (++j < cpuc->lbr_stack.nr)
144f9c73224SSandipan Das 				cpuc->lbr_entries[j - 1] = cpuc->lbr_entries[j];
145f9c73224SSandipan Das 			cpuc->lbr_stack.nr--;
146f9c73224SSandipan Das 			if (!cpuc->lbr_entries[i].from)
147f9c73224SSandipan Das 				continue;
148f9c73224SSandipan Das 		}
149f9c73224SSandipan Das 		i++;
150f9c73224SSandipan Das 	}
151f9c73224SSandipan Das }
152f9c73224SSandipan Das 
1530bc3be5bSSandipan Das static const int lbr_spec_map[PERF_BR_SPEC_MAX] = {
1540bc3be5bSSandipan Das 	PERF_BR_SPEC_NA,
1550bc3be5bSSandipan Das 	PERF_BR_SPEC_WRONG_PATH,
1560bc3be5bSSandipan Das 	PERF_BR_NON_SPEC_CORRECT_PATH,
1570bc3be5bSSandipan Das 	PERF_BR_SPEC_CORRECT_PATH,
1580bc3be5bSSandipan Das };
1590bc3be5bSSandipan Das 
amd_pmu_lbr_read(void)160ca5b7c0dSSandipan Das void amd_pmu_lbr_read(void)
161ca5b7c0dSSandipan Das {
162ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
163ca5b7c0dSSandipan Das 	struct perf_branch_entry *br = cpuc->lbr_entries;
164ca5b7c0dSSandipan Das 	struct branch_entry entry;
1650bc3be5bSSandipan Das 	int out = 0, idx, i;
166ca5b7c0dSSandipan Das 
167ca5b7c0dSSandipan Das 	if (!cpuc->lbr_users)
168ca5b7c0dSSandipan Das 		return;
169ca5b7c0dSSandipan Das 
170ca5b7c0dSSandipan Das 	for (i = 0; i < x86_pmu.lbr_nr; i++) {
171ca5b7c0dSSandipan Das 		entry.from.full	= amd_pmu_lbr_get_from(i);
172ca5b7c0dSSandipan Das 		entry.to.full	= amd_pmu_lbr_get_to(i);
173ca5b7c0dSSandipan Das 
1740bc3be5bSSandipan Das 		/*
1750bc3be5bSSandipan Das 		 * Check if a branch has been logged; if valid = 0, spec = 0
176*08aba129SSandipan Das 		 * then no branch was recorded; if reserved = 1 then an
177*08aba129SSandipan Das 		 * erroneous branch was recorded (see Erratum 1452)
1780bc3be5bSSandipan Das 		 */
179*08aba129SSandipan Das 		if ((!entry.to.split.valid && !entry.to.split.spec) ||
180*08aba129SSandipan Das 		    entry.to.split.reserved)
181ca5b7c0dSSandipan Das 			continue;
182ca5b7c0dSSandipan Das 
183ca5b7c0dSSandipan Das 		perf_clear_branch_entry_bitfields(br + out);
184ca5b7c0dSSandipan Das 
185ca5b7c0dSSandipan Das 		br[out].from	= sign_ext_branch_ip(entry.from.split.ip);
186ca5b7c0dSSandipan Das 		br[out].to	= sign_ext_branch_ip(entry.to.split.ip);
187ca5b7c0dSSandipan Das 		br[out].mispred	= entry.from.split.mispredict;
188ca5b7c0dSSandipan Das 		br[out].predicted = !br[out].mispred;
1890bc3be5bSSandipan Das 
1900bc3be5bSSandipan Das 		/*
1910bc3be5bSSandipan Das 		 * Set branch speculation information using the status of
1920bc3be5bSSandipan Das 		 * the valid and spec bits.
1930bc3be5bSSandipan Das 		 *
1940bc3be5bSSandipan Das 		 * When valid = 0, spec = 0, no branch was recorded and the
1950bc3be5bSSandipan Das 		 * entry is discarded as seen above.
1960bc3be5bSSandipan Das 		 *
1970bc3be5bSSandipan Das 		 * When valid = 0, spec = 1, the recorded branch was
1980bc3be5bSSandipan Das 		 * speculative but took the wrong path.
1990bc3be5bSSandipan Das 		 *
2000bc3be5bSSandipan Das 		 * When valid = 1, spec = 0, the recorded branch was
2010bc3be5bSSandipan Das 		 * non-speculative but took the correct path.
2020bc3be5bSSandipan Das 		 *
2030bc3be5bSSandipan Das 		 * When valid = 1, spec = 1, the recorded branch was
2040bc3be5bSSandipan Das 		 * speculative and took the correct path
2050bc3be5bSSandipan Das 		 */
2060bc3be5bSSandipan Das 		idx = (entry.to.split.valid << 1) | entry.to.split.spec;
2070bc3be5bSSandipan Das 		br[out].spec = lbr_spec_map[idx];
208ca5b7c0dSSandipan Das 		out++;
209ca5b7c0dSSandipan Das 	}
210ca5b7c0dSSandipan Das 
211ca5b7c0dSSandipan Das 	cpuc->lbr_stack.nr = out;
212ca5b7c0dSSandipan Das 
213ca5b7c0dSSandipan Das 	/*
214ca5b7c0dSSandipan Das 	 * Internal register renaming always ensures that LBR From[0] and
215ca5b7c0dSSandipan Das 	 * LBR To[0] always represent the TOS
216ca5b7c0dSSandipan Das 	 */
217ca5b7c0dSSandipan Das 	cpuc->lbr_stack.hw_idx = 0;
218f9c73224SSandipan Das 
219f9c73224SSandipan Das 	/* Perform further software filtering */
220f9c73224SSandipan Das 	amd_pmu_lbr_filter();
221ca5b7c0dSSandipan Das }
222ca5b7c0dSSandipan Das 
223f4f925daSSandipan Das static const int lbr_select_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
224f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_USER_SHIFT]		= LBR_USER,
225f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_KERNEL_SHIFT]	= LBR_KERNEL,
226f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_HV_SHIFT]		= LBR_IGNORE,
227f4f925daSSandipan Das 
228f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_ANY_SHIFT]		= LBR_ANY,
229f9c73224SSandipan Das 	[PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT]	= LBR_REL_CALL | LBR_IND_CALL | LBR_FAR,
230f9c73224SSandipan Das 	[PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT]	= LBR_RETURN | LBR_FAR,
231f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_IND_CALL_SHIFT]	= LBR_IND_CALL,
232f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT]	= LBR_NOT_SUPP,
233f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_IN_TX_SHIFT]	= LBR_NOT_SUPP,
234f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_NO_TX_SHIFT]	= LBR_NOT_SUPP,
235f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_COND_SHIFT]		= LBR_JCC,
236f4f925daSSandipan Das 
237f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT]	= LBR_NOT_SUPP,
238f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT]	= LBR_IND_JMP,
239f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_CALL_SHIFT]		= LBR_REL_CALL,
240f4f925daSSandipan Das 
241f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT]	= LBR_NOT_SUPP,
242f4f925daSSandipan Das 	[PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT]	= LBR_NOT_SUPP,
243f4f925daSSandipan Das };
244f4f925daSSandipan Das 
amd_pmu_lbr_setup_filter(struct perf_event * event)245ca5b7c0dSSandipan Das static int amd_pmu_lbr_setup_filter(struct perf_event *event)
246ca5b7c0dSSandipan Das {
247f4f925daSSandipan Das 	struct hw_perf_event_extra *reg = &event->hw.branch_reg;
248f4f925daSSandipan Das 	u64 br_type = event->attr.branch_sample_type;
249f4f925daSSandipan Das 	u64 mask = 0, v;
250f4f925daSSandipan Das 	int i;
251f4f925daSSandipan Das 
252ca5b7c0dSSandipan Das 	/* No LBR support */
253ca5b7c0dSSandipan Das 	if (!x86_pmu.lbr_nr)
254ca5b7c0dSSandipan Das 		return -EOPNOTSUPP;
255ca5b7c0dSSandipan Das 
256f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_USER)
257f9c73224SSandipan Das 		mask |= X86_BR_USER;
258f9c73224SSandipan Das 
259f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_KERNEL)
260f9c73224SSandipan Das 		mask |= X86_BR_KERNEL;
261f9c73224SSandipan Das 
262f9c73224SSandipan Das 	/* Ignore BRANCH_HV here */
263f9c73224SSandipan Das 
264f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_ANY)
265f9c73224SSandipan Das 		mask |= X86_BR_ANY;
266f9c73224SSandipan Das 
267f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_ANY_CALL)
268f9c73224SSandipan Das 		mask |= X86_BR_ANY_CALL;
269f9c73224SSandipan Das 
270f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_ANY_RETURN)
271f9c73224SSandipan Das 		mask |= X86_BR_RET | X86_BR_IRET | X86_BR_SYSRET;
272f9c73224SSandipan Das 
273f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_IND_CALL)
274f9c73224SSandipan Das 		mask |= X86_BR_IND_CALL;
275f9c73224SSandipan Das 
276f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_COND)
277f9c73224SSandipan Das 		mask |= X86_BR_JCC;
278f9c73224SSandipan Das 
279f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_IND_JUMP)
280f9c73224SSandipan Das 		mask |= X86_BR_IND_JMP;
281f9c73224SSandipan Das 
282f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_CALL)
283f9c73224SSandipan Das 		mask |= X86_BR_CALL | X86_BR_ZERO_CALL;
284f9c73224SSandipan Das 
285f9c73224SSandipan Das 	if (br_type & PERF_SAMPLE_BRANCH_TYPE_SAVE)
286f9c73224SSandipan Das 		mask |= X86_BR_TYPE_SAVE;
287f9c73224SSandipan Das 
288f9c73224SSandipan Das 	reg->reg = mask;
289f9c73224SSandipan Das 	mask = 0;
290f9c73224SSandipan Das 
291f4f925daSSandipan Das 	for (i = 0; i < PERF_SAMPLE_BRANCH_MAX_SHIFT; i++) {
292f4f925daSSandipan Das 		if (!(br_type & BIT_ULL(i)))
293f4f925daSSandipan Das 			continue;
294f4f925daSSandipan Das 
295f4f925daSSandipan Das 		v = lbr_select_map[i];
296f4f925daSSandipan Das 		if (v == LBR_NOT_SUPP)
297f4f925daSSandipan Das 			return -EOPNOTSUPP;
298f4f925daSSandipan Das 
299f4f925daSSandipan Das 		if (v != LBR_IGNORE)
300f4f925daSSandipan Das 			mask |= v;
301f4f925daSSandipan Das 	}
302f4f925daSSandipan Das 
303f4f925daSSandipan Das 	/* Filter bits operate in suppress mode */
304f4f925daSSandipan Das 	reg->config = mask ^ LBR_SELECT_MASK;
305f4f925daSSandipan Das 
306ca5b7c0dSSandipan Das 	return 0;
307ca5b7c0dSSandipan Das }
308ca5b7c0dSSandipan Das 
amd_pmu_lbr_hw_config(struct perf_event * event)309ca5b7c0dSSandipan Das int amd_pmu_lbr_hw_config(struct perf_event *event)
310ca5b7c0dSSandipan Das {
311ca5b7c0dSSandipan Das 	int ret = 0;
312ca5b7c0dSSandipan Das 
313ca5b7c0dSSandipan Das 	/* LBR is not recommended in counting mode */
314ca5b7c0dSSandipan Das 	if (!is_sampling_event(event))
315ca5b7c0dSSandipan Das 		return -EINVAL;
316ca5b7c0dSSandipan Das 
317ca5b7c0dSSandipan Das 	ret = amd_pmu_lbr_setup_filter(event);
318ca5b7c0dSSandipan Das 	if (!ret)
319ca5b7c0dSSandipan Das 		event->attach_state |= PERF_ATTACH_SCHED_CB;
320ca5b7c0dSSandipan Das 
321ca5b7c0dSSandipan Das 	return ret;
322ca5b7c0dSSandipan Das }
323ca5b7c0dSSandipan Das 
amd_pmu_lbr_reset(void)324ca5b7c0dSSandipan Das void amd_pmu_lbr_reset(void)
325ca5b7c0dSSandipan Das {
326ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
327ca5b7c0dSSandipan Das 	int i;
328ca5b7c0dSSandipan Das 
329ca5b7c0dSSandipan Das 	if (!x86_pmu.lbr_nr)
330ca5b7c0dSSandipan Das 		return;
331ca5b7c0dSSandipan Das 
332ca5b7c0dSSandipan Das 	/* Reset all branch records individually */
333ca5b7c0dSSandipan Das 	for (i = 0; i < x86_pmu.lbr_nr; i++) {
334ca5b7c0dSSandipan Das 		amd_pmu_lbr_set_from(i, 0);
335ca5b7c0dSSandipan Das 		amd_pmu_lbr_set_to(i, 0);
336ca5b7c0dSSandipan Das 	}
337ca5b7c0dSSandipan Das 
338ca5b7c0dSSandipan Das 	cpuc->last_task_ctx = NULL;
339ca5b7c0dSSandipan Das 	cpuc->last_log_id = 0;
340f4f925daSSandipan Das 	wrmsrl(MSR_AMD64_LBR_SELECT, 0);
341ca5b7c0dSSandipan Das }
342ca5b7c0dSSandipan Das 
amd_pmu_lbr_add(struct perf_event * event)343ca5b7c0dSSandipan Das void amd_pmu_lbr_add(struct perf_event *event)
344ca5b7c0dSSandipan Das {
345ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
346f9c73224SSandipan Das 	struct hw_perf_event_extra *reg = &event->hw.branch_reg;
347ca5b7c0dSSandipan Das 
348ca5b7c0dSSandipan Das 	if (!x86_pmu.lbr_nr)
349ca5b7c0dSSandipan Das 		return;
350ca5b7c0dSSandipan Das 
351f4f925daSSandipan Das 	if (has_branch_stack(event)) {
352f4f925daSSandipan Das 		cpuc->lbr_select = 1;
353f9c73224SSandipan Das 		cpuc->lbr_sel->config = reg->config;
354f9c73224SSandipan Das 		cpuc->br_sel = reg->reg;
355f4f925daSSandipan Das 	}
356f4f925daSSandipan Das 
357bd275681SPeter Zijlstra 	perf_sched_cb_inc(event->pmu);
358ca5b7c0dSSandipan Das 
359ca5b7c0dSSandipan Das 	if (!cpuc->lbr_users++ && !event->total_time_running)
360ca5b7c0dSSandipan Das 		amd_pmu_lbr_reset();
361ca5b7c0dSSandipan Das }
362ca5b7c0dSSandipan Das 
amd_pmu_lbr_del(struct perf_event * event)363ca5b7c0dSSandipan Das void amd_pmu_lbr_del(struct perf_event *event)
364ca5b7c0dSSandipan Das {
365ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
366ca5b7c0dSSandipan Das 
367ca5b7c0dSSandipan Das 	if (!x86_pmu.lbr_nr)
368ca5b7c0dSSandipan Das 		return;
369ca5b7c0dSSandipan Das 
370f4f925daSSandipan Das 	if (has_branch_stack(event))
371f4f925daSSandipan Das 		cpuc->lbr_select = 0;
372f4f925daSSandipan Das 
373ca5b7c0dSSandipan Das 	cpuc->lbr_users--;
374ca5b7c0dSSandipan Das 	WARN_ON_ONCE(cpuc->lbr_users < 0);
375bd275681SPeter Zijlstra 	perf_sched_cb_dec(event->pmu);
376ca5b7c0dSSandipan Das }
377ca5b7c0dSSandipan Das 
amd_pmu_lbr_sched_task(struct perf_event_pmu_context * pmu_ctx,bool sched_in)378bd275681SPeter Zijlstra void amd_pmu_lbr_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in)
379ca5b7c0dSSandipan Das {
380ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
381ca5b7c0dSSandipan Das 
382ca5b7c0dSSandipan Das 	/*
383ca5b7c0dSSandipan Das 	 * A context switch can flip the address space and LBR entries are
384ca5b7c0dSSandipan Das 	 * not tagged with an identifier. Hence, branches cannot be resolved
385ca5b7c0dSSandipan Das 	 * from the old address space and the LBR records should be wiped.
386ca5b7c0dSSandipan Das 	 */
387ca5b7c0dSSandipan Das 	if (cpuc->lbr_users && sched_in)
388ca5b7c0dSSandipan Das 		amd_pmu_lbr_reset();
389ca5b7c0dSSandipan Das }
390ca5b7c0dSSandipan Das 
amd_pmu_lbr_enable_all(void)391ca5b7c0dSSandipan Das void amd_pmu_lbr_enable_all(void)
392ca5b7c0dSSandipan Das {
393ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
394f4f925daSSandipan Das 	u64 lbr_select, dbg_ctl, dbg_extn_cfg;
395ca5b7c0dSSandipan Das 
396ca5b7c0dSSandipan Das 	if (!cpuc->lbr_users || !x86_pmu.lbr_nr)
397ca5b7c0dSSandipan Das 		return;
398ca5b7c0dSSandipan Das 
399f4f925daSSandipan Das 	/* Set hardware branch filter */
400f4f925daSSandipan Das 	if (cpuc->lbr_select) {
401f4f925daSSandipan Das 		lbr_select = cpuc->lbr_sel->config & LBR_SELECT_MASK;
402f4f925daSSandipan Das 		wrmsrl(MSR_AMD64_LBR_SELECT, lbr_select);
403f4f925daSSandipan Das 	}
404f4f925daSSandipan Das 
40555ed6c47SSandipan Das 	if (cpu_feature_enabled(X86_FEATURE_AMD_LBR_PMC_FREEZE)) {
406ca5b7c0dSSandipan Das 		rdmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl);
407ca5b7c0dSSandipan Das 		wrmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
40855ed6c47SSandipan Das 	}
40955ed6c47SSandipan Das 
41055ed6c47SSandipan Das 	rdmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg);
411ca5b7c0dSSandipan Das 	wrmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg | DBG_EXTN_CFG_LBRV2EN);
412ca5b7c0dSSandipan Das }
413ca5b7c0dSSandipan Das 
amd_pmu_lbr_disable_all(void)414ca5b7c0dSSandipan Das void amd_pmu_lbr_disable_all(void)
415ca5b7c0dSSandipan Das {
416ca5b7c0dSSandipan Das 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
417ca5b7c0dSSandipan Das 	u64 dbg_ctl, dbg_extn_cfg;
418ca5b7c0dSSandipan Das 
419ca5b7c0dSSandipan Das 	if (!cpuc->lbr_users || !x86_pmu.lbr_nr)
420ca5b7c0dSSandipan Das 		return;
421ca5b7c0dSSandipan Das 
422ca5b7c0dSSandipan Das 	rdmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg);
423ca5b7c0dSSandipan Das 	wrmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg & ~DBG_EXTN_CFG_LBRV2EN);
42455ed6c47SSandipan Das 
42555ed6c47SSandipan Das 	if (cpu_feature_enabled(X86_FEATURE_AMD_LBR_PMC_FREEZE)) {
42655ed6c47SSandipan Das 		rdmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl);
427ca5b7c0dSSandipan Das 		wrmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl & ~DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
428ca5b7c0dSSandipan Das 	}
42955ed6c47SSandipan Das }
430ca5b7c0dSSandipan Das 
amd_pmu_lbr_init(void)431703fb765SSandipan Das __init int amd_pmu_lbr_init(void)
432703fb765SSandipan Das {
433703fb765SSandipan Das 	union cpuid_0x80000022_ebx ebx;
434703fb765SSandipan Das 
435703fb765SSandipan Das 	if (x86_pmu.version < 2 || !boot_cpu_has(X86_FEATURE_AMD_LBR_V2))
436703fb765SSandipan Das 		return -EOPNOTSUPP;
437703fb765SSandipan Das 
438703fb765SSandipan Das 	/* Set number of entries */
439703fb765SSandipan Das 	ebx.full = cpuid_ebx(EXT_PERFMON_DEBUG_FEATURES);
440703fb765SSandipan Das 	x86_pmu.lbr_nr = ebx.split.lbr_v2_stack_sz;
441703fb765SSandipan Das 
442703fb765SSandipan Das 	pr_cont("%d-deep LBR, ", x86_pmu.lbr_nr);
443703fb765SSandipan Das 
444703fb765SSandipan Das 	return 0;
445703fb765SSandipan Das }
446