162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/perf_event.h> 362306a36Sopenharmony_ci#include <asm/perf_event.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "../perf_event.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* LBR Branch Select valid bits */ 862306a36Sopenharmony_ci#define LBR_SELECT_MASK 0x1ff 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * LBR Branch Select filter bits which when set, ensures that the 1262306a36Sopenharmony_ci * corresponding type of branches are not recorded 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#define LBR_SELECT_KERNEL 0 /* Branches ending in CPL = 0 */ 1562306a36Sopenharmony_ci#define LBR_SELECT_USER 1 /* Branches ending in CPL > 0 */ 1662306a36Sopenharmony_ci#define LBR_SELECT_JCC 2 /* Conditional branches */ 1762306a36Sopenharmony_ci#define LBR_SELECT_CALL_NEAR_REL 3 /* Near relative calls */ 1862306a36Sopenharmony_ci#define LBR_SELECT_CALL_NEAR_IND 4 /* Indirect relative calls */ 1962306a36Sopenharmony_ci#define LBR_SELECT_RET_NEAR 5 /* Near returns */ 2062306a36Sopenharmony_ci#define LBR_SELECT_JMP_NEAR_IND 6 /* Near indirect jumps (excl. calls and returns) */ 2162306a36Sopenharmony_ci#define LBR_SELECT_JMP_NEAR_REL 7 /* Near relative jumps (excl. calls) */ 2262306a36Sopenharmony_ci#define LBR_SELECT_FAR_BRANCH 8 /* Far branches */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define LBR_KERNEL BIT(LBR_SELECT_KERNEL) 2562306a36Sopenharmony_ci#define LBR_USER BIT(LBR_SELECT_USER) 2662306a36Sopenharmony_ci#define LBR_JCC BIT(LBR_SELECT_JCC) 2762306a36Sopenharmony_ci#define LBR_REL_CALL BIT(LBR_SELECT_CALL_NEAR_REL) 2862306a36Sopenharmony_ci#define LBR_IND_CALL BIT(LBR_SELECT_CALL_NEAR_IND) 2962306a36Sopenharmony_ci#define LBR_RETURN BIT(LBR_SELECT_RET_NEAR) 3062306a36Sopenharmony_ci#define LBR_REL_JMP BIT(LBR_SELECT_JMP_NEAR_REL) 3162306a36Sopenharmony_ci#define LBR_IND_JMP BIT(LBR_SELECT_JMP_NEAR_IND) 3262306a36Sopenharmony_ci#define LBR_FAR BIT(LBR_SELECT_FAR_BRANCH) 3362306a36Sopenharmony_ci#define LBR_NOT_SUPP -1 /* unsupported filter */ 3462306a36Sopenharmony_ci#define LBR_IGNORE 0 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define LBR_ANY \ 3762306a36Sopenharmony_ci (LBR_JCC | LBR_REL_CALL | LBR_IND_CALL | LBR_RETURN | \ 3862306a36Sopenharmony_ci LBR_REL_JMP | LBR_IND_JMP | LBR_FAR) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct branch_entry { 4162306a36Sopenharmony_ci union { 4262306a36Sopenharmony_ci struct { 4362306a36Sopenharmony_ci u64 ip:58; 4462306a36Sopenharmony_ci u64 ip_sign_ext:5; 4562306a36Sopenharmony_ci u64 mispredict:1; 4662306a36Sopenharmony_ci } split; 4762306a36Sopenharmony_ci u64 full; 4862306a36Sopenharmony_ci } from; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci union { 5162306a36Sopenharmony_ci struct { 5262306a36Sopenharmony_ci u64 ip:58; 5362306a36Sopenharmony_ci u64 ip_sign_ext:3; 5462306a36Sopenharmony_ci u64 reserved:1; 5562306a36Sopenharmony_ci u64 spec:1; 5662306a36Sopenharmony_ci u64 valid:1; 5762306a36Sopenharmony_ci } split; 5862306a36Sopenharmony_ci u64 full; 5962306a36Sopenharmony_ci } to; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic __always_inline void amd_pmu_lbr_set_from(unsigned int idx, u64 val) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci wrmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2, val); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic __always_inline void amd_pmu_lbr_set_to(unsigned int idx, u64 val) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci wrmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic __always_inline u64 amd_pmu_lbr_get_from(unsigned int idx) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci u64 val; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci rdmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2, val); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return val; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic __always_inline u64 amd_pmu_lbr_get_to(unsigned int idx) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u64 val; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci rdmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return val; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic __always_inline u64 sign_ext_branch_ip(u64 ip) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u32 shift = 64 - boot_cpu_data.x86_virt_bits; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return (u64)(((s64)ip << shift) >> shift); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void amd_pmu_lbr_filter(void) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 10062306a36Sopenharmony_ci int br_sel = cpuc->br_sel, offset, type, i, j; 10162306a36Sopenharmony_ci bool compress = false; 10262306a36Sopenharmony_ci bool fused_only = false; 10362306a36Sopenharmony_ci u64 from, to; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* If sampling all branches, there is nothing to filter */ 10662306a36Sopenharmony_ci if (((br_sel & X86_BR_ALL) == X86_BR_ALL) && 10762306a36Sopenharmony_ci ((br_sel & X86_BR_TYPE_SAVE) != X86_BR_TYPE_SAVE)) 10862306a36Sopenharmony_ci fused_only = true; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 0; i < cpuc->lbr_stack.nr; i++) { 11162306a36Sopenharmony_ci from = cpuc->lbr_entries[i].from; 11262306a36Sopenharmony_ci to = cpuc->lbr_entries[i].to; 11362306a36Sopenharmony_ci type = branch_type_fused(from, to, 0, &offset); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * Adjust the branch from address in case of instruction 11762306a36Sopenharmony_ci * fusion where it points to an instruction preceding the 11862306a36Sopenharmony_ci * actual branch 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci if (offset) { 12162306a36Sopenharmony_ci cpuc->lbr_entries[i].from += offset; 12262306a36Sopenharmony_ci if (fused_only) 12362306a36Sopenharmony_ci continue; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* If type does not correspond, then discard */ 12762306a36Sopenharmony_ci if (type == X86_BR_NONE || (br_sel & type) != type) { 12862306a36Sopenharmony_ci cpuc->lbr_entries[i].from = 0; /* mark invalid */ 12962306a36Sopenharmony_ci compress = true; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if ((br_sel & X86_BR_TYPE_SAVE) == X86_BR_TYPE_SAVE) 13362306a36Sopenharmony_ci cpuc->lbr_entries[i].type = common_branch_type(type); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!compress) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Remove all invalid entries */ 14062306a36Sopenharmony_ci for (i = 0; i < cpuc->lbr_stack.nr; ) { 14162306a36Sopenharmony_ci if (!cpuc->lbr_entries[i].from) { 14262306a36Sopenharmony_ci j = i; 14362306a36Sopenharmony_ci while (++j < cpuc->lbr_stack.nr) 14462306a36Sopenharmony_ci cpuc->lbr_entries[j - 1] = cpuc->lbr_entries[j]; 14562306a36Sopenharmony_ci cpuc->lbr_stack.nr--; 14662306a36Sopenharmony_ci if (!cpuc->lbr_entries[i].from) 14762306a36Sopenharmony_ci continue; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci i++; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const int lbr_spec_map[PERF_BR_SPEC_MAX] = { 15462306a36Sopenharmony_ci PERF_BR_SPEC_NA, 15562306a36Sopenharmony_ci PERF_BR_SPEC_WRONG_PATH, 15662306a36Sopenharmony_ci PERF_BR_NON_SPEC_CORRECT_PATH, 15762306a36Sopenharmony_ci PERF_BR_SPEC_CORRECT_PATH, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid amd_pmu_lbr_read(void) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 16362306a36Sopenharmony_ci struct perf_branch_entry *br = cpuc->lbr_entries; 16462306a36Sopenharmony_ci struct branch_entry entry; 16562306a36Sopenharmony_ci int out = 0, idx, i; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!cpuc->lbr_users) 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci for (i = 0; i < x86_pmu.lbr_nr; i++) { 17162306a36Sopenharmony_ci entry.from.full = amd_pmu_lbr_get_from(i); 17262306a36Sopenharmony_ci entry.to.full = amd_pmu_lbr_get_to(i); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* 17562306a36Sopenharmony_ci * Check if a branch has been logged; if valid = 0, spec = 0 17662306a36Sopenharmony_ci * then no branch was recorded 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci if (!entry.to.split.valid && !entry.to.split.spec) 17962306a36Sopenharmony_ci continue; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci perf_clear_branch_entry_bitfields(br + out); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci br[out].from = sign_ext_branch_ip(entry.from.split.ip); 18462306a36Sopenharmony_ci br[out].to = sign_ext_branch_ip(entry.to.split.ip); 18562306a36Sopenharmony_ci br[out].mispred = entry.from.split.mispredict; 18662306a36Sopenharmony_ci br[out].predicted = !br[out].mispred; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Set branch speculation information using the status of 19062306a36Sopenharmony_ci * the valid and spec bits. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * When valid = 0, spec = 0, no branch was recorded and the 19362306a36Sopenharmony_ci * entry is discarded as seen above. 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * When valid = 0, spec = 1, the recorded branch was 19662306a36Sopenharmony_ci * speculative but took the wrong path. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * When valid = 1, spec = 0, the recorded branch was 19962306a36Sopenharmony_ci * non-speculative but took the correct path. 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * When valid = 1, spec = 1, the recorded branch was 20262306a36Sopenharmony_ci * speculative and took the correct path 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci idx = (entry.to.split.valid << 1) | entry.to.split.spec; 20562306a36Sopenharmony_ci br[out].spec = lbr_spec_map[idx]; 20662306a36Sopenharmony_ci out++; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci cpuc->lbr_stack.nr = out; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * Internal register renaming always ensures that LBR From[0] and 21362306a36Sopenharmony_ci * LBR To[0] always represent the TOS 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci cpuc->lbr_stack.hw_idx = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Perform further software filtering */ 21862306a36Sopenharmony_ci amd_pmu_lbr_filter(); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic const int lbr_select_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { 22262306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, 22362306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, 22462306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGNORE, 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, 22762306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL | LBR_FAR, 22862306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_FAR, 22962306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL, 23062306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT] = LBR_NOT_SUPP, 23162306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_IN_TX_SHIFT] = LBR_NOT_SUPP, 23262306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_NO_TX_SHIFT] = LBR_NOT_SUPP, 23362306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT] = LBR_NOT_SUPP, 23662306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT] = LBR_IND_JMP, 23762306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_CALL_SHIFT] = LBR_REL_CALL, 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT] = LBR_NOT_SUPP, 24062306a36Sopenharmony_ci [PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT] = LBR_NOT_SUPP, 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int amd_pmu_lbr_setup_filter(struct perf_event *event) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct hw_perf_event_extra *reg = &event->hw.branch_reg; 24662306a36Sopenharmony_ci u64 br_type = event->attr.branch_sample_type; 24762306a36Sopenharmony_ci u64 mask = 0, v; 24862306a36Sopenharmony_ci int i; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* No LBR support */ 25162306a36Sopenharmony_ci if (!x86_pmu.lbr_nr) 25262306a36Sopenharmony_ci return -EOPNOTSUPP; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_USER) 25562306a36Sopenharmony_ci mask |= X86_BR_USER; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_KERNEL) 25862306a36Sopenharmony_ci mask |= X86_BR_KERNEL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Ignore BRANCH_HV here */ 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_ANY) 26362306a36Sopenharmony_ci mask |= X86_BR_ANY; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_ANY_CALL) 26662306a36Sopenharmony_ci mask |= X86_BR_ANY_CALL; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_ANY_RETURN) 26962306a36Sopenharmony_ci mask |= X86_BR_RET | X86_BR_IRET | X86_BR_SYSRET; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_IND_CALL) 27262306a36Sopenharmony_ci mask |= X86_BR_IND_CALL; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_COND) 27562306a36Sopenharmony_ci mask |= X86_BR_JCC; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_IND_JUMP) 27862306a36Sopenharmony_ci mask |= X86_BR_IND_JMP; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_CALL) 28162306a36Sopenharmony_ci mask |= X86_BR_CALL | X86_BR_ZERO_CALL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (br_type & PERF_SAMPLE_BRANCH_TYPE_SAVE) 28462306a36Sopenharmony_ci mask |= X86_BR_TYPE_SAVE; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci reg->reg = mask; 28762306a36Sopenharmony_ci mask = 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci for (i = 0; i < PERF_SAMPLE_BRANCH_MAX_SHIFT; i++) { 29062306a36Sopenharmony_ci if (!(br_type & BIT_ULL(i))) 29162306a36Sopenharmony_ci continue; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci v = lbr_select_map[i]; 29462306a36Sopenharmony_ci if (v == LBR_NOT_SUPP) 29562306a36Sopenharmony_ci return -EOPNOTSUPP; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (v != LBR_IGNORE) 29862306a36Sopenharmony_ci mask |= v; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Filter bits operate in suppress mode */ 30262306a36Sopenharmony_ci reg->config = mask ^ LBR_SELECT_MASK; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciint amd_pmu_lbr_hw_config(struct perf_event *event) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int ret = 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* LBR is not recommended in counting mode */ 31262306a36Sopenharmony_ci if (!is_sampling_event(event)) 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = amd_pmu_lbr_setup_filter(event); 31662306a36Sopenharmony_ci if (!ret) 31762306a36Sopenharmony_ci event->attach_state |= PERF_ATTACH_SCHED_CB; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_civoid amd_pmu_lbr_reset(void) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 32562306a36Sopenharmony_ci int i; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!x86_pmu.lbr_nr) 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Reset all branch records individually */ 33162306a36Sopenharmony_ci for (i = 0; i < x86_pmu.lbr_nr; i++) { 33262306a36Sopenharmony_ci amd_pmu_lbr_set_from(i, 0); 33362306a36Sopenharmony_ci amd_pmu_lbr_set_to(i, 0); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci cpuc->last_task_ctx = NULL; 33762306a36Sopenharmony_ci cpuc->last_log_id = 0; 33862306a36Sopenharmony_ci wrmsrl(MSR_AMD64_LBR_SELECT, 0); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_civoid amd_pmu_lbr_add(struct perf_event *event) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 34462306a36Sopenharmony_ci struct hw_perf_event_extra *reg = &event->hw.branch_reg; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!x86_pmu.lbr_nr) 34762306a36Sopenharmony_ci return; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (has_branch_stack(event)) { 35062306a36Sopenharmony_ci cpuc->lbr_select = 1; 35162306a36Sopenharmony_ci cpuc->lbr_sel->config = reg->config; 35262306a36Sopenharmony_ci cpuc->br_sel = reg->reg; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci perf_sched_cb_inc(event->pmu); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!cpuc->lbr_users++ && !event->total_time_running) 35862306a36Sopenharmony_ci amd_pmu_lbr_reset(); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_civoid amd_pmu_lbr_del(struct perf_event *event) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!x86_pmu.lbr_nr) 36662306a36Sopenharmony_ci return; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (has_branch_stack(event)) 36962306a36Sopenharmony_ci cpuc->lbr_select = 0; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci cpuc->lbr_users--; 37262306a36Sopenharmony_ci WARN_ON_ONCE(cpuc->lbr_users < 0); 37362306a36Sopenharmony_ci perf_sched_cb_dec(event->pmu); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_civoid amd_pmu_lbr_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * A context switch can flip the address space and LBR entries are 38262306a36Sopenharmony_ci * not tagged with an identifier. Hence, branches cannot be resolved 38362306a36Sopenharmony_ci * from the old address space and the LBR records should be wiped. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci if (cpuc->lbr_users && sched_in) 38662306a36Sopenharmony_ci amd_pmu_lbr_reset(); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_civoid amd_pmu_lbr_enable_all(void) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 39262306a36Sopenharmony_ci u64 lbr_select, dbg_ctl, dbg_extn_cfg; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!cpuc->lbr_users || !x86_pmu.lbr_nr) 39562306a36Sopenharmony_ci return; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Set hardware branch filter */ 39862306a36Sopenharmony_ci if (cpuc->lbr_select) { 39962306a36Sopenharmony_ci lbr_select = cpuc->lbr_sel->config & LBR_SELECT_MASK; 40062306a36Sopenharmony_ci wrmsrl(MSR_AMD64_LBR_SELECT, lbr_select); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci rdmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl); 40462306a36Sopenharmony_ci rdmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci wrmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); 40762306a36Sopenharmony_ci wrmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg | DBG_EXTN_CFG_LBRV2EN); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_civoid amd_pmu_lbr_disable_all(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 41362306a36Sopenharmony_ci u64 dbg_ctl, dbg_extn_cfg; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!cpuc->lbr_users || !x86_pmu.lbr_nr) 41662306a36Sopenharmony_ci return; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci rdmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg); 41962306a36Sopenharmony_ci rdmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci wrmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg & ~DBG_EXTN_CFG_LBRV2EN); 42262306a36Sopenharmony_ci wrmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl & ~DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci__init int amd_pmu_lbr_init(void) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci union cpuid_0x80000022_ebx ebx; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (x86_pmu.version < 2 || !boot_cpu_has(X86_FEATURE_AMD_LBR_V2)) 43062306a36Sopenharmony_ci return -EOPNOTSUPP; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Set number of entries */ 43362306a36Sopenharmony_ci ebx.full = cpuid_ebx(EXT_PERFMON_DEBUG_FEATURES); 43462306a36Sopenharmony_ci x86_pmu.lbr_nr = ebx.split.lbr_v2_stack_sz; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci pr_cont("%d-deep LBR, ", x86_pmu.lbr_nr); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 440