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