0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/kernel.h>
0010 #include <linux/jump_label.h>
0011 #include <asm/msr.h>
0012 #include <asm/cpufeature.h>
0013
0014 #include "../perf_event.h"
0015
0016 #define BRS_POISON 0xFFFFFFFFFFFFFFFEULL
0017
0018
0019 union amd_debug_extn_cfg {
0020 __u64 val;
0021 struct {
0022 __u64 rsvd0:2,
0023 brsmen:1,
0024 rsvd4_3:2,
0025 vb:1,
0026 rsvd2:10,
0027 msroff:4,
0028 rsvd3:4,
0029 pmc:3,
0030 rsvd4:37;
0031 };
0032 };
0033
0034 static inline unsigned int brs_from(int idx)
0035 {
0036 return MSR_AMD_SAMP_BR_FROM + 2 * idx;
0037 }
0038
0039 static inline unsigned int brs_to(int idx)
0040 {
0041 return MSR_AMD_SAMP_BR_FROM + 2 * idx + 1;
0042 }
0043
0044 static inline void set_debug_extn_cfg(u64 val)
0045 {
0046
0047 wrmsrl(MSR_AMD_DBG_EXTN_CFG, val | 3ULL << 3);
0048 }
0049
0050 static inline u64 get_debug_extn_cfg(void)
0051 {
0052 u64 val;
0053
0054 rdmsrl(MSR_AMD_DBG_EXTN_CFG, val);
0055 return val;
0056 }
0057
0058 static bool __init amd_brs_detect(void)
0059 {
0060 if (!cpu_feature_enabled(X86_FEATURE_BRS))
0061 return false;
0062
0063 switch (boot_cpu_data.x86) {
0064 case 0x19:
0065 x86_pmu.lbr_nr = 16;
0066
0067
0068 x86_pmu.lbr_sel_map = NULL;
0069 x86_pmu.lbr_sel_mask = 0;
0070 break;
0071 default:
0072 return false;
0073 }
0074
0075 return true;
0076 }
0077
0078
0079
0080
0081
0082
0083
0084 int amd_brs_setup_filter(struct perf_event *event)
0085 {
0086 u64 type = event->attr.branch_sample_type;
0087
0088
0089 if (!x86_pmu.lbr_nr)
0090 return -EOPNOTSUPP;
0091
0092
0093 if ((type & ~PERF_SAMPLE_BRANCH_PLM_ALL) != PERF_SAMPLE_BRANCH_ANY)
0094 return -EINVAL;
0095
0096 return 0;
0097 }
0098
0099
0100 static inline int amd_brs_get_tos(union amd_debug_extn_cfg *cfg)
0101 {
0102
0103
0104
0105
0106 return (cfg->msroff ? cfg->msroff : x86_pmu.lbr_nr) - 1;
0107 }
0108
0109
0110
0111
0112
0113 void amd_brs_reset(void)
0114 {
0115 if (!cpu_feature_enabled(X86_FEATURE_BRS))
0116 return;
0117
0118
0119
0120
0121 set_debug_extn_cfg(0);
0122
0123
0124
0125
0126 wrmsrl(brs_to(0), BRS_POISON);
0127 }
0128
0129 int __init amd_brs_init(void)
0130 {
0131 if (!amd_brs_detect())
0132 return -EOPNOTSUPP;
0133
0134 pr_cont("%d-deep BRS, ", x86_pmu.lbr_nr);
0135
0136 return 0;
0137 }
0138
0139 void amd_brs_enable(void)
0140 {
0141 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0142 union amd_debug_extn_cfg cfg;
0143
0144
0145 if (++cpuc->brs_active > 1)
0146 return;
0147
0148 cfg.val = 0;
0149 cfg.brsmen = 1;
0150
0151
0152 set_debug_extn_cfg(cfg.val);
0153 }
0154
0155 void amd_brs_enable_all(void)
0156 {
0157 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0158 if (cpuc->lbr_users)
0159 amd_brs_enable();
0160 }
0161
0162 void amd_brs_disable(void)
0163 {
0164 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0165 union amd_debug_extn_cfg cfg;
0166
0167
0168 if (!cpuc->brs_active)
0169 return;
0170
0171
0172 if (--cpuc->brs_active)
0173 return;
0174
0175
0176
0177
0178
0179 cfg.val = get_debug_extn_cfg();
0180
0181
0182
0183
0184
0185 if (cfg.brsmen) {
0186 cfg.brsmen = 0;
0187 set_debug_extn_cfg(cfg.val);
0188 }
0189 }
0190
0191 void amd_brs_disable_all(void)
0192 {
0193 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0194 if (cpuc->lbr_users)
0195 amd_brs_disable();
0196 }
0197
0198 static bool amd_brs_match_plm(struct perf_event *event, u64 to)
0199 {
0200 int type = event->attr.branch_sample_type;
0201 int plm_k = PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_HV;
0202 int plm_u = PERF_SAMPLE_BRANCH_USER;
0203
0204 if (!(type & plm_k) && kernel_ip(to))
0205 return 0;
0206
0207 if (!(type & plm_u) && !kernel_ip(to))
0208 return 0;
0209
0210 return 1;
0211 }
0212
0213
0214
0215
0216
0217 void amd_brs_drain(void)
0218 {
0219 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0220 struct perf_event *event = cpuc->events[0];
0221 struct perf_branch_entry *br = cpuc->lbr_entries;
0222 union amd_debug_extn_cfg cfg;
0223 u32 i, nr = 0, num, tos, start;
0224 u32 shift = 64 - boot_cpu_data.x86_virt_bits;
0225
0226
0227
0228
0229
0230
0231
0232 if (!event)
0233 goto empty;
0234
0235 cfg.val = get_debug_extn_cfg();
0236
0237
0238 if (WARN_ON_ONCE(cfg.msroff >= x86_pmu.lbr_nr))
0239 goto empty;
0240
0241
0242 if (cfg.vb == 0)
0243 goto empty;
0244
0245
0246
0247
0248
0249
0250
0251 start = 0;
0252 tos = amd_brs_get_tos(&cfg);
0253
0254 num = tos - start + 1;
0255
0256
0257
0258
0259
0260 for (i = 0; i < num; i++) {
0261 u32 brs_idx = tos - i;
0262 u64 from, to;
0263
0264 rdmsrl(brs_to(brs_idx), to);
0265
0266
0267 if (to == BRS_POISON)
0268 break;
0269
0270
0271
0272
0273
0274
0275 to = (u64)(((s64)to << shift) >> shift);
0276
0277 if (!amd_brs_match_plm(event, to))
0278 continue;
0279
0280 rdmsrl(brs_from(brs_idx), from);
0281
0282 perf_clear_branch_entry_bitfields(br+nr);
0283
0284 br[nr].from = from;
0285 br[nr].to = to;
0286
0287 nr++;
0288 }
0289 empty:
0290
0291 cpuc->lbr_stack.nr = nr;
0292 }
0293
0294
0295
0296
0297
0298 static void amd_brs_poison_buffer(void)
0299 {
0300 union amd_debug_extn_cfg cfg;
0301 unsigned int idx;
0302
0303
0304 cfg.val = get_debug_extn_cfg();
0305
0306
0307 idx = amd_brs_get_tos(&cfg);
0308
0309
0310 wrmsrl(brs_to(idx), BRS_POISON);
0311 }
0312
0313
0314
0315
0316
0317
0318
0319
0320 void amd_pmu_brs_sched_task(struct perf_event_context *ctx, bool sched_in)
0321 {
0322 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0323
0324
0325 if (!cpuc->lbr_users)
0326 return;
0327
0328
0329
0330
0331
0332
0333 if (sched_in)
0334 amd_brs_poison_buffer();
0335 }
0336
0337
0338
0339
0340
0341 void perf_amd_brs_lopwr_cb(bool lopwr_in)
0342 {
0343 struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
0344 union amd_debug_extn_cfg cfg;
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354 if (cpuc->brs_active) {
0355 cfg.val = get_debug_extn_cfg();
0356 cfg.brsmen = !lopwr_in;
0357 set_debug_extn_cfg(cfg.val);
0358 }
0359 }
0360
0361 DEFINE_STATIC_CALL_NULL(perf_lopwr_cb, perf_amd_brs_lopwr_cb);
0362 EXPORT_STATIC_CALL_TRAMP_GPL(perf_lopwr_cb);
0363
0364 void __init amd_brs_lopwr_init(void)
0365 {
0366 static_call_update(perf_lopwr_cb, perf_amd_brs_lopwr_cb);
0367 }