18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * @file op_model_amd.c 38c2ecf20Sopenharmony_ci * athlon / K7 / K8 / Family 10h model-specific MSR operations 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * @remark Copyright 2002-2009 OProfile authors 68c2ecf20Sopenharmony_ci * @remark Read the file COPYING 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * @author John Levon 98c2ecf20Sopenharmony_ci * @author Philippe Elie 108c2ecf20Sopenharmony_ci * @author Graydon Hoare 118c2ecf20Sopenharmony_ci * @author Robert Richter <robert.richter@amd.com> 128c2ecf20Sopenharmony_ci * @author Barry Kasindorf <barry.kasindorf@amd.com> 138c2ecf20Sopenharmony_ci * @author Jason Yeh <jason.yeh@amd.com> 148c2ecf20Sopenharmony_ci * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/percpu.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 238c2ecf20Sopenharmony_ci#include <asm/msr.h> 248c2ecf20Sopenharmony_ci#include <asm/nmi.h> 258c2ecf20Sopenharmony_ci#include <asm/apic.h> 268c2ecf20Sopenharmony_ci#include <asm/processor.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "op_x86_model.h" 298c2ecf20Sopenharmony_ci#include "op_counter.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 328c2ecf20Sopenharmony_ci#define NUM_VIRT_COUNTERS 32 338c2ecf20Sopenharmony_ci#else 348c2ecf20Sopenharmony_ci#define NUM_VIRT_COUNTERS 0 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define OP_EVENT_MASK 0x0FFF 388c2ecf20Sopenharmony_ci#define OP_CTR_OVERFLOW (1ULL<<31) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MSR_AMD_EVENTSEL_RESERVED ((0xFFFFFCF0ULL<<32)|(1ULL<<21)) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int num_counters; 438c2ecf20Sopenharmony_cistatic unsigned long reset_value[OP_MAX_COUNTER]; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define IBS_FETCH_SIZE 6 468c2ecf20Sopenharmony_ci#define IBS_OP_SIZE 12 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic u32 ibs_caps; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct ibs_config { 518c2ecf20Sopenharmony_ci unsigned long op_enabled; 528c2ecf20Sopenharmony_ci unsigned long fetch_enabled; 538c2ecf20Sopenharmony_ci unsigned long max_cnt_fetch; 548c2ecf20Sopenharmony_ci unsigned long max_cnt_op; 558c2ecf20Sopenharmony_ci unsigned long rand_en; 568c2ecf20Sopenharmony_ci unsigned long dispatched_ops; 578c2ecf20Sopenharmony_ci unsigned long branch_target; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct ibs_state { 618c2ecf20Sopenharmony_ci u64 ibs_op_ctl; 628c2ecf20Sopenharmony_ci int branch_target; 638c2ecf20Sopenharmony_ci unsigned long sample_size; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct ibs_config ibs_config; 678c2ecf20Sopenharmony_cistatic struct ibs_state ibs_state; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * IBS randomization macros 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci#define IBS_RANDOM_BITS 12 738c2ecf20Sopenharmony_ci#define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) 748c2ecf20Sopenharmony_ci#define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * 16-bit Linear Feedback Shift Register (LFSR) 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * 16 14 13 11 808c2ecf20Sopenharmony_ci * Feedback polynomial = X + X + X + X + 1 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic unsigned int lfsr_random(void) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci static unsigned int lfsr_value = 0xF00D; 858c2ecf20Sopenharmony_ci unsigned int bit; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Compute next bit to shift in */ 888c2ecf20Sopenharmony_ci bit = ((lfsr_value >> 0) ^ 898c2ecf20Sopenharmony_ci (lfsr_value >> 2) ^ 908c2ecf20Sopenharmony_ci (lfsr_value >> 3) ^ 918c2ecf20Sopenharmony_ci (lfsr_value >> 5)) & 0x0001; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Advance to next register value */ 948c2ecf20Sopenharmony_ci lfsr_value = (lfsr_value >> 1) | (bit << 15); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return lfsr_value; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * IBS software randomization 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * The IBS periodic op counter is randomized in software. The lower 12 1038c2ecf20Sopenharmony_ci * bits of the 20 bit counter are randomized. IbsOpCurCnt is 1048c2ecf20Sopenharmony_ci * initialized with a 12 bit random value. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic inline u64 op_amd_randomize_ibs_op(u64 val) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci unsigned int random = lfsr_random(); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * Work around if the hw can not write to IbsOpCurCnt 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * Randomize the lower 8 bits of the 16 bit 1158c2ecf20Sopenharmony_ci * IbsOpMaxCnt [15:0] value in the range of -128 to 1168c2ecf20Sopenharmony_ci * +127 by adding/subtracting an offset to the 1178c2ecf20Sopenharmony_ci * maximum count (IbsOpMaxCnt). 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * To avoid over or underflows and protect upper bits 1208c2ecf20Sopenharmony_ci * starting at bit 16, the initial value for 1218c2ecf20Sopenharmony_ci * IbsOpMaxCnt must fit in the range from 0x0081 to 1228c2ecf20Sopenharmony_ci * 0xff80. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci val += (s8)(random >> 4); 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci val |= (u64)(random & IBS_RANDOM_MASK) << 32; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return val; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void 1328c2ecf20Sopenharmony_ciop_amd_handle_ibs(struct pt_regs * const regs, 1338c2ecf20Sopenharmony_ci struct op_msrs const * const msrs) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci u64 val, ctl; 1368c2ecf20Sopenharmony_ci struct op_entry entry; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!ibs_caps) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (ibs_config.fetch_enabled) { 1428c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl); 1438c2ecf20Sopenharmony_ci if (ctl & IBS_FETCH_VAL) { 1448c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSFETCHLINAD, val); 1458c2ecf20Sopenharmony_ci oprofile_write_reserve(&entry, regs, val, 1468c2ecf20Sopenharmony_ci IBS_FETCH_CODE, IBS_FETCH_SIZE); 1478c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1488c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, ctl); 1498c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, val); 1508c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1518c2ecf20Sopenharmony_ci oprofile_write_commit(&entry); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* reenable the IRQ */ 1548c2ecf20Sopenharmony_ci ctl &= ~(IBS_FETCH_VAL | IBS_FETCH_CNT); 1558c2ecf20Sopenharmony_ci ctl |= IBS_FETCH_ENABLE; 1568c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_IBSFETCHCTL, ctl); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (ibs_config.op_enabled) { 1618c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSOPCTL, ctl); 1628c2ecf20Sopenharmony_ci if (ctl & IBS_OP_VAL) { 1638c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSOPRIP, val); 1648c2ecf20Sopenharmony_ci oprofile_write_reserve(&entry, regs, val, IBS_OP_CODE, 1658c2ecf20Sopenharmony_ci ibs_state.sample_size); 1668c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1678c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSOPDATA, val); 1688c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1698c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSOPDATA2, val); 1708c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1718c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSOPDATA3, val); 1728c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1738c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSDCLINAD, val); 1748c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1758c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSDCPHYSAD, val); 1768c2ecf20Sopenharmony_ci oprofile_add_data64(&entry, val); 1778c2ecf20Sopenharmony_ci if (ibs_state.branch_target) { 1788c2ecf20Sopenharmony_ci rdmsrl(MSR_AMD64_IBSBRTARGET, val); 1798c2ecf20Sopenharmony_ci oprofile_add_data(&entry, (unsigned long)val); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci oprofile_write_commit(&entry); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* reenable the IRQ */ 1848c2ecf20Sopenharmony_ci ctl = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl); 1858c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_IBSOPCTL, ctl); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic inline void op_amd_start_ibs(void) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u64 val; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!ibs_caps) 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci memset(&ibs_state, 0, sizeof(ibs_state)); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * Note: Since the max count settings may out of range we 2018c2ecf20Sopenharmony_ci * write back the actual used values so that userland can read 2028c2ecf20Sopenharmony_ci * it. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (ibs_config.fetch_enabled) { 2068c2ecf20Sopenharmony_ci val = ibs_config.max_cnt_fetch >> 4; 2078c2ecf20Sopenharmony_ci val = min(val, IBS_FETCH_MAX_CNT); 2088c2ecf20Sopenharmony_ci ibs_config.max_cnt_fetch = val << 4; 2098c2ecf20Sopenharmony_ci val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0; 2108c2ecf20Sopenharmony_ci val |= IBS_FETCH_ENABLE; 2118c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_IBSFETCHCTL, val); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (ibs_config.op_enabled) { 2158c2ecf20Sopenharmony_ci val = ibs_config.max_cnt_op >> 4; 2168c2ecf20Sopenharmony_ci if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) { 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * IbsOpCurCnt not supported. See 2198c2ecf20Sopenharmony_ci * op_amd_randomize_ibs_op() for details. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci val = clamp(val, 0x0081ULL, 0xFF80ULL); 2228c2ecf20Sopenharmony_ci ibs_config.max_cnt_op = val << 4; 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * The start value is randomized with a 2268c2ecf20Sopenharmony_ci * positive offset, we need to compensate it 2278c2ecf20Sopenharmony_ci * with the half of the randomized range. Also 2288c2ecf20Sopenharmony_ci * avoid underflows. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci val += IBS_RANDOM_MAXCNT_OFFSET; 2318c2ecf20Sopenharmony_ci if (ibs_caps & IBS_CAPS_OPCNTEXT) 2328c2ecf20Sopenharmony_ci val = min(val, IBS_OP_MAX_CNT_EXT); 2338c2ecf20Sopenharmony_ci else 2348c2ecf20Sopenharmony_ci val = min(val, IBS_OP_MAX_CNT); 2358c2ecf20Sopenharmony_ci ibs_config.max_cnt_op = 2368c2ecf20Sopenharmony_ci (val - IBS_RANDOM_MAXCNT_OFFSET) << 4; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci val = ((val & ~IBS_OP_MAX_CNT) << 4) | (val & IBS_OP_MAX_CNT); 2398c2ecf20Sopenharmony_ci val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0; 2408c2ecf20Sopenharmony_ci val |= IBS_OP_ENABLE; 2418c2ecf20Sopenharmony_ci ibs_state.ibs_op_ctl = val; 2428c2ecf20Sopenharmony_ci ibs_state.sample_size = IBS_OP_SIZE; 2438c2ecf20Sopenharmony_ci if (ibs_config.branch_target) { 2448c2ecf20Sopenharmony_ci ibs_state.branch_target = 1; 2458c2ecf20Sopenharmony_ci ibs_state.sample_size++; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci val = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl); 2488c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_IBSOPCTL, val); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void op_amd_stop_ibs(void) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (!ibs_caps) 2558c2ecf20Sopenharmony_ci return; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (ibs_config.fetch_enabled) 2588c2ecf20Sopenharmony_ci /* clear max count and enable */ 2598c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_IBSFETCHCTL, 0); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (ibs_config.op_enabled) 2628c2ecf20Sopenharmony_ci /* clear max count and enable */ 2638c2ecf20Sopenharmony_ci wrmsrl(MSR_AMD64_IBSOPCTL, 0); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void op_mux_switch_ctrl(struct op_x86_model_spec const *model, 2698c2ecf20Sopenharmony_ci struct op_msrs const * const msrs) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci u64 val; 2728c2ecf20Sopenharmony_ci int i; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* enable active counters */ 2758c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 2768c2ecf20Sopenharmony_ci int virt = op_x86_phys_to_virt(i); 2778c2ecf20Sopenharmony_ci if (!reset_value[virt]) 2788c2ecf20Sopenharmony_ci continue; 2798c2ecf20Sopenharmony_ci rdmsrl(msrs->controls[i].addr, val); 2808c2ecf20Sopenharmony_ci val &= model->reserved; 2818c2ecf20Sopenharmony_ci val |= op_x86_get_ctrl(model, &counter_config[virt]); 2828c2ecf20Sopenharmony_ci wrmsrl(msrs->controls[i].addr, val); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci#endif 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* functions for op_amd_spec */ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void op_amd_shutdown(struct op_msrs const * const msrs) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int i; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 2958c2ecf20Sopenharmony_ci if (!msrs->counters[i].addr) 2968c2ecf20Sopenharmony_ci continue; 2978c2ecf20Sopenharmony_ci release_perfctr_nmi(MSR_K7_PERFCTR0 + i); 2988c2ecf20Sopenharmony_ci release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int op_amd_fill_in_addresses(struct op_msrs * const msrs) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int i; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; i++) { 3078c2ecf20Sopenharmony_ci if (!reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) 3088c2ecf20Sopenharmony_ci goto fail; 3098c2ecf20Sopenharmony_ci if (!reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) { 3108c2ecf20Sopenharmony_ci release_perfctr_nmi(MSR_K7_PERFCTR0 + i); 3118c2ecf20Sopenharmony_ci goto fail; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci /* both registers must be reserved */ 3148c2ecf20Sopenharmony_ci if (num_counters == AMD64_NUM_COUNTERS_CORE) { 3158c2ecf20Sopenharmony_ci msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1); 3168c2ecf20Sopenharmony_ci msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1); 3178c2ecf20Sopenharmony_ci } else { 3188c2ecf20Sopenharmony_ci msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; 3198c2ecf20Sopenharmony_ci msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci continue; 3228c2ecf20Sopenharmony_ci fail: 3238c2ecf20Sopenharmony_ci if (!counter_config[i].enabled) 3248c2ecf20Sopenharmony_ci continue; 3258c2ecf20Sopenharmony_ci op_x86_warn_reserved(i); 3268c2ecf20Sopenharmony_ci op_amd_shutdown(msrs); 3278c2ecf20Sopenharmony_ci return -EBUSY; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic void op_amd_setup_ctrs(struct op_x86_model_spec const *model, 3348c2ecf20Sopenharmony_ci struct op_msrs const * const msrs) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci u64 val; 3378c2ecf20Sopenharmony_ci int i; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* setup reset_value */ 3408c2ecf20Sopenharmony_ci for (i = 0; i < OP_MAX_COUNTER; ++i) { 3418c2ecf20Sopenharmony_ci if (counter_config[i].enabled 3428c2ecf20Sopenharmony_ci && msrs->counters[op_x86_virt_to_phys(i)].addr) 3438c2ecf20Sopenharmony_ci reset_value[i] = counter_config[i].count; 3448c2ecf20Sopenharmony_ci else 3458c2ecf20Sopenharmony_ci reset_value[i] = 0; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* clear all counters */ 3498c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 3508c2ecf20Sopenharmony_ci if (!msrs->controls[i].addr) 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci rdmsrl(msrs->controls[i].addr, val); 3538c2ecf20Sopenharmony_ci if (val & ARCH_PERFMON_EVENTSEL_ENABLE) 3548c2ecf20Sopenharmony_ci op_x86_warn_in_use(i); 3558c2ecf20Sopenharmony_ci val &= model->reserved; 3568c2ecf20Sopenharmony_ci wrmsrl(msrs->controls[i].addr, val); 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * avoid a false detection of ctr overflows in NMI 3598c2ecf20Sopenharmony_ci * handler 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci wrmsrl(msrs->counters[i].addr, -1LL); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* enable active counters */ 3658c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 3668c2ecf20Sopenharmony_ci int virt = op_x86_phys_to_virt(i); 3678c2ecf20Sopenharmony_ci if (!reset_value[virt]) 3688c2ecf20Sopenharmony_ci continue; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* setup counter registers */ 3718c2ecf20Sopenharmony_ci wrmsrl(msrs->counters[i].addr, -(u64)reset_value[virt]); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* setup control registers */ 3748c2ecf20Sopenharmony_ci rdmsrl(msrs->controls[i].addr, val); 3758c2ecf20Sopenharmony_ci val &= model->reserved; 3768c2ecf20Sopenharmony_ci val |= op_x86_get_ctrl(model, &counter_config[virt]); 3778c2ecf20Sopenharmony_ci wrmsrl(msrs->controls[i].addr, val); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int op_amd_check_ctrs(struct pt_regs * const regs, 3828c2ecf20Sopenharmony_ci struct op_msrs const * const msrs) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci u64 val; 3858c2ecf20Sopenharmony_ci int i; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 3888c2ecf20Sopenharmony_ci int virt = op_x86_phys_to_virt(i); 3898c2ecf20Sopenharmony_ci if (!reset_value[virt]) 3908c2ecf20Sopenharmony_ci continue; 3918c2ecf20Sopenharmony_ci rdmsrl(msrs->counters[i].addr, val); 3928c2ecf20Sopenharmony_ci /* bit is clear if overflowed: */ 3938c2ecf20Sopenharmony_ci if (val & OP_CTR_OVERFLOW) 3948c2ecf20Sopenharmony_ci continue; 3958c2ecf20Sopenharmony_ci oprofile_add_sample(regs, virt); 3968c2ecf20Sopenharmony_ci wrmsrl(msrs->counters[i].addr, -(u64)reset_value[virt]); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci op_amd_handle_ibs(regs, msrs); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* See op_model_ppro.c */ 4028c2ecf20Sopenharmony_ci return 1; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void op_amd_start(struct op_msrs const * const msrs) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci u64 val; 4088c2ecf20Sopenharmony_ci int i; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 4118c2ecf20Sopenharmony_ci if (!reset_value[op_x86_phys_to_virt(i)]) 4128c2ecf20Sopenharmony_ci continue; 4138c2ecf20Sopenharmony_ci rdmsrl(msrs->controls[i].addr, val); 4148c2ecf20Sopenharmony_ci val |= ARCH_PERFMON_EVENTSEL_ENABLE; 4158c2ecf20Sopenharmony_ci wrmsrl(msrs->controls[i].addr, val); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci op_amd_start_ibs(); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic void op_amd_stop(struct op_msrs const * const msrs) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci u64 val; 4248c2ecf20Sopenharmony_ci int i; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * Subtle: stop on all counters to avoid race with setting our 4288c2ecf20Sopenharmony_ci * pm callback 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci for (i = 0; i < num_counters; ++i) { 4318c2ecf20Sopenharmony_ci if (!reset_value[op_x86_phys_to_virt(i)]) 4328c2ecf20Sopenharmony_ci continue; 4338c2ecf20Sopenharmony_ci rdmsrl(msrs->controls[i].addr, val); 4348c2ecf20Sopenharmony_ci val &= ~ARCH_PERFMON_EVENTSEL_ENABLE; 4358c2ecf20Sopenharmony_ci wrmsrl(msrs->controls[i].addr, val); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci op_amd_stop_ibs(); 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* 4428c2ecf20Sopenharmony_ci * check and reserve APIC extended interrupt LVT offset for IBS if 4438c2ecf20Sopenharmony_ci * available 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void init_ibs(void) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci ibs_caps = get_ibs_caps(); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (!ibs_caps) 4518c2ecf20Sopenharmony_ci return; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int (*create_arch_files)(struct dentry *root); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int setup_ibs_files(struct dentry *root) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct dentry *dir; 4618c2ecf20Sopenharmony_ci int ret = 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* architecture specific files */ 4648c2ecf20Sopenharmony_ci if (create_arch_files) 4658c2ecf20Sopenharmony_ci ret = create_arch_files(root); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (ret) 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!ibs_caps) 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* model specific files */ 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* setup some reasonable defaults */ 4768c2ecf20Sopenharmony_ci memset(&ibs_config, 0, sizeof(ibs_config)); 4778c2ecf20Sopenharmony_ci ibs_config.max_cnt_fetch = 250000; 4788c2ecf20Sopenharmony_ci ibs_config.max_cnt_op = 250000; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (ibs_caps & IBS_CAPS_FETCHSAM) { 4818c2ecf20Sopenharmony_ci dir = oprofilefs_mkdir(root, "ibs_fetch"); 4828c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "enable", 4838c2ecf20Sopenharmony_ci &ibs_config.fetch_enabled); 4848c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "max_count", 4858c2ecf20Sopenharmony_ci &ibs_config.max_cnt_fetch); 4868c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "rand_enable", 4878c2ecf20Sopenharmony_ci &ibs_config.rand_en); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (ibs_caps & IBS_CAPS_OPSAM) { 4918c2ecf20Sopenharmony_ci dir = oprofilefs_mkdir(root, "ibs_op"); 4928c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "enable", 4938c2ecf20Sopenharmony_ci &ibs_config.op_enabled); 4948c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "max_count", 4958c2ecf20Sopenharmony_ci &ibs_config.max_cnt_op); 4968c2ecf20Sopenharmony_ci if (ibs_caps & IBS_CAPS_OPCNT) 4978c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "dispatched_ops", 4988c2ecf20Sopenharmony_ci &ibs_config.dispatched_ops); 4998c2ecf20Sopenharmony_ci if (ibs_caps & IBS_CAPS_BRNTRGT) 5008c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "branch_target", 5018c2ecf20Sopenharmony_ci &ibs_config.branch_target); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistruct op_x86_model_spec op_amd_spec; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int op_amd_init(struct oprofile_operations *ops) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci init_ibs(); 5128c2ecf20Sopenharmony_ci create_arch_files = ops->create_files; 5138c2ecf20Sopenharmony_ci ops->create_files = setup_ibs_files; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 == 0x15) { 5168c2ecf20Sopenharmony_ci num_counters = AMD64_NUM_COUNTERS_CORE; 5178c2ecf20Sopenharmony_ci } else { 5188c2ecf20Sopenharmony_ci num_counters = AMD64_NUM_COUNTERS; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci op_amd_spec.num_counters = num_counters; 5228c2ecf20Sopenharmony_ci op_amd_spec.num_controls = num_counters; 5238c2ecf20Sopenharmony_ci op_amd_spec.num_virt_counters = max(num_counters, NUM_VIRT_COUNTERS); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistruct op_x86_model_spec op_amd_spec = { 5298c2ecf20Sopenharmony_ci /* num_counters/num_controls filled in at runtime */ 5308c2ecf20Sopenharmony_ci .reserved = MSR_AMD_EVENTSEL_RESERVED, 5318c2ecf20Sopenharmony_ci .event_mask = OP_EVENT_MASK, 5328c2ecf20Sopenharmony_ci .init = op_amd_init, 5338c2ecf20Sopenharmony_ci .fill_in_addresses = &op_amd_fill_in_addresses, 5348c2ecf20Sopenharmony_ci .setup_ctrs = &op_amd_setup_ctrs, 5358c2ecf20Sopenharmony_ci .check_ctrs = &op_amd_check_ctrs, 5368c2ecf20Sopenharmony_ci .start = &op_amd_start, 5378c2ecf20Sopenharmony_ci .stop = &op_amd_stop, 5388c2ecf20Sopenharmony_ci .shutdown = &op_amd_shutdown, 5398c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 5408c2ecf20Sopenharmony_ci .switch_ctrl = &op_mux_switch_ctrl, 5418c2ecf20Sopenharmony_ci#endif 5428c2ecf20Sopenharmony_ci}; 543