18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * @file arch/alpha/oprofile/op_model_ev67.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * @remark Copyright 2002 OProfile authors 58c2ecf20Sopenharmony_ci * @remark Read the file COPYING 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * @author Richard Henderson <rth@twiddle.net> 88c2ecf20Sopenharmony_ci * @author Falk Hueffner <falk@debian.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 128c2ecf20Sopenharmony_ci#include <linux/smp.h> 138c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "op_impl.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Compute all of the registers in preparation for enabling profiling. */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void 218c2ecf20Sopenharmony_ciev67_reg_setup(struct op_register_config *reg, 228c2ecf20Sopenharmony_ci struct op_counter_config *ctr, 238c2ecf20Sopenharmony_ci struct op_system_config *sys) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci unsigned long ctl, reset, need_reset, i; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci /* Select desired events. */ 288c2ecf20Sopenharmony_ci ctl = 1UL << 4; /* Enable ProfileMe mode. */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* The event numbers are chosen so we can use them directly if 318c2ecf20Sopenharmony_ci PCTR1 is enabled. */ 328c2ecf20Sopenharmony_ci if (ctr[1].enabled) { 338c2ecf20Sopenharmony_ci ctl |= (ctr[1].event & 3) << 2; 348c2ecf20Sopenharmony_ci } else { 358c2ecf20Sopenharmony_ci if (ctr[0].event == 0) /* cycles */ 368c2ecf20Sopenharmony_ci ctl |= 1UL << 2; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci reg->mux_select = ctl; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* Select logging options. */ 418c2ecf20Sopenharmony_ci /* ??? Need to come up with some mechanism to trace only 428c2ecf20Sopenharmony_ci selected processes. EV67 does not have a mechanism to 438c2ecf20Sopenharmony_ci select kernel or user mode only. For now, enable always. */ 448c2ecf20Sopenharmony_ci reg->proc_mode = 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* EV67 cannot change the width of the counters as with the 478c2ecf20Sopenharmony_ci other implementations. But fortunately, we can write to 488c2ecf20Sopenharmony_ci the counters and set the value such that it will overflow 498c2ecf20Sopenharmony_ci at the right time. */ 508c2ecf20Sopenharmony_ci reset = need_reset = 0; 518c2ecf20Sopenharmony_ci for (i = 0; i < 2; ++i) { 528c2ecf20Sopenharmony_ci unsigned long count = ctr[i].count; 538c2ecf20Sopenharmony_ci if (!ctr[i].enabled) 548c2ecf20Sopenharmony_ci continue; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (count > 0x100000) 578c2ecf20Sopenharmony_ci count = 0x100000; 588c2ecf20Sopenharmony_ci ctr[i].count = count; 598c2ecf20Sopenharmony_ci reset |= (0x100000 - count) << (i ? 6 : 28); 608c2ecf20Sopenharmony_ci if (count != 0x100000) 618c2ecf20Sopenharmony_ci need_reset |= 1 << i; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci reg->reset_values = reset; 648c2ecf20Sopenharmony_ci reg->need_reset = need_reset; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Program all of the registers in preparation for enabling profiling. */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void 708c2ecf20Sopenharmony_ciev67_cpu_setup (void *x) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct op_register_config *reg = x; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci wrperfmon(2, reg->mux_select); 758c2ecf20Sopenharmony_ci wrperfmon(3, reg->proc_mode); 768c2ecf20Sopenharmony_ci wrperfmon(6, reg->reset_values | 3); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* CTR is a counter for which the user has requested an interrupt count 808c2ecf20Sopenharmony_ci in between one of the widths selectable in hardware. Reset the count 818c2ecf20Sopenharmony_ci for CTR to the value stored in REG->RESET_VALUES. */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void 848c2ecf20Sopenharmony_ciev67_reset_ctr(struct op_register_config *reg, unsigned long ctr) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci wrperfmon(6, reg->reset_values | (1 << ctr)); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* ProfileMe conditions which will show up as counters. We can also 908c2ecf20Sopenharmony_ci detect the following, but it seems unlikely that anybody is 918c2ecf20Sopenharmony_ci interested in counting them: 928c2ecf20Sopenharmony_ci * Reset 938c2ecf20Sopenharmony_ci * MT_FPCR (write to floating point control register) 948c2ecf20Sopenharmony_ci * Arithmetic trap 958c2ecf20Sopenharmony_ci * Dstream Fault 968c2ecf20Sopenharmony_ci * Machine Check (ECC fault, etc.) 978c2ecf20Sopenharmony_ci * OPCDEC (illegal opcode) 988c2ecf20Sopenharmony_ci * Floating point disabled 998c2ecf20Sopenharmony_ci * Differentiate between DTB single/double misses and 3 or 4 level 1008c2ecf20Sopenharmony_ci page tables 1018c2ecf20Sopenharmony_ci * Istream access violation 1028c2ecf20Sopenharmony_ci * Interrupt 1038c2ecf20Sopenharmony_ci * Icache Parity Error. 1048c2ecf20Sopenharmony_ci * Instruction killed (nop, trapb) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci Unfortunately, there seems to be no way to detect Dcache and Bcache 1078c2ecf20Sopenharmony_ci misses; the latter could be approximated by making the counter 1088c2ecf20Sopenharmony_ci count Bcache misses, but that is not precise. 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci We model this as 20 counters: 1118c2ecf20Sopenharmony_ci * PCTR0 1128c2ecf20Sopenharmony_ci * PCTR1 1138c2ecf20Sopenharmony_ci * 9 ProfileMe events, induced by PCTR0 1148c2ecf20Sopenharmony_ci * 9 ProfileMe events, induced by PCTR1 1158c2ecf20Sopenharmony_ci*/ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cienum profileme_counters { 1188c2ecf20Sopenharmony_ci PM_STALLED, /* Stalled for at least one cycle 1198c2ecf20Sopenharmony_ci between the fetch and map stages */ 1208c2ecf20Sopenharmony_ci PM_TAKEN, /* Conditional branch taken */ 1218c2ecf20Sopenharmony_ci PM_MISPREDICT, /* Branch caused mispredict trap */ 1228c2ecf20Sopenharmony_ci PM_ITB_MISS, /* ITB miss */ 1238c2ecf20Sopenharmony_ci PM_DTB_MISS, /* DTB miss */ 1248c2ecf20Sopenharmony_ci PM_REPLAY, /* Replay trap */ 1258c2ecf20Sopenharmony_ci PM_LOAD_STORE, /* Load-store order trap */ 1268c2ecf20Sopenharmony_ci PM_ICACHE_MISS, /* Icache miss */ 1278c2ecf20Sopenharmony_ci PM_UNALIGNED, /* Unaligned Load/Store */ 1288c2ecf20Sopenharmony_ci PM_NUM_COUNTERS 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void 1328c2ecf20Sopenharmony_ciop_add_pm(unsigned long pc, int kern, unsigned long counter, 1338c2ecf20Sopenharmony_ci struct op_counter_config *ctr, unsigned long event) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long fake_counter = 2 + event; 1368c2ecf20Sopenharmony_ci if (counter == 1) 1378c2ecf20Sopenharmony_ci fake_counter += PM_NUM_COUNTERS; 1388c2ecf20Sopenharmony_ci if (ctr[fake_counter].enabled) 1398c2ecf20Sopenharmony_ci oprofile_add_pc(pc, kern, fake_counter); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void 1438c2ecf20Sopenharmony_ciev67_handle_interrupt(unsigned long which, struct pt_regs *regs, 1448c2ecf20Sopenharmony_ci struct op_counter_config *ctr) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci unsigned long pmpc, pctr_ctl; 1478c2ecf20Sopenharmony_ci int kern = !user_mode(regs); 1488c2ecf20Sopenharmony_ci int mispredict = 0; 1498c2ecf20Sopenharmony_ci union { 1508c2ecf20Sopenharmony_ci unsigned long v; 1518c2ecf20Sopenharmony_ci struct { 1528c2ecf20Sopenharmony_ci unsigned reserved: 30; /* 0-29 */ 1538c2ecf20Sopenharmony_ci unsigned overcount: 3; /* 30-32 */ 1548c2ecf20Sopenharmony_ci unsigned icache_miss: 1; /* 33 */ 1558c2ecf20Sopenharmony_ci unsigned trap_type: 4; /* 34-37 */ 1568c2ecf20Sopenharmony_ci unsigned load_store: 1; /* 38 */ 1578c2ecf20Sopenharmony_ci unsigned trap: 1; /* 39 */ 1588c2ecf20Sopenharmony_ci unsigned mispredict: 1; /* 40 */ 1598c2ecf20Sopenharmony_ci } fields; 1608c2ecf20Sopenharmony_ci } i_stat; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci enum trap_types { 1638c2ecf20Sopenharmony_ci TRAP_REPLAY, 1648c2ecf20Sopenharmony_ci TRAP_INVALID0, 1658c2ecf20Sopenharmony_ci TRAP_DTB_DOUBLE_MISS_3, 1668c2ecf20Sopenharmony_ci TRAP_DTB_DOUBLE_MISS_4, 1678c2ecf20Sopenharmony_ci TRAP_FP_DISABLED, 1688c2ecf20Sopenharmony_ci TRAP_UNALIGNED, 1698c2ecf20Sopenharmony_ci TRAP_DTB_SINGLE_MISS, 1708c2ecf20Sopenharmony_ci TRAP_DSTREAM_FAULT, 1718c2ecf20Sopenharmony_ci TRAP_OPCDEC, 1728c2ecf20Sopenharmony_ci TRAP_INVALID1, 1738c2ecf20Sopenharmony_ci TRAP_MACHINE_CHECK, 1748c2ecf20Sopenharmony_ci TRAP_INVALID2, 1758c2ecf20Sopenharmony_ci TRAP_ARITHMETIC, 1768c2ecf20Sopenharmony_ci TRAP_INVALID3, 1778c2ecf20Sopenharmony_ci TRAP_MT_FPCR, 1788c2ecf20Sopenharmony_ci TRAP_RESET 1798c2ecf20Sopenharmony_ci }; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci pmpc = wrperfmon(9, 0); 1828c2ecf20Sopenharmony_ci /* ??? Don't know how to handle physical-mode PALcode address. */ 1838c2ecf20Sopenharmony_ci if (pmpc & 1) 1848c2ecf20Sopenharmony_ci return; 1858c2ecf20Sopenharmony_ci pmpc &= ~2; /* clear reserved bit */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci i_stat.v = wrperfmon(8, 0); 1888c2ecf20Sopenharmony_ci if (i_stat.fields.trap) { 1898c2ecf20Sopenharmony_ci switch (i_stat.fields.trap_type) { 1908c2ecf20Sopenharmony_ci case TRAP_INVALID1: 1918c2ecf20Sopenharmony_ci case TRAP_INVALID2: 1928c2ecf20Sopenharmony_ci case TRAP_INVALID3: 1938c2ecf20Sopenharmony_ci /* Pipeline redirection occurred. PMPC points 1948c2ecf20Sopenharmony_ci to PALcode. Recognize ITB miss by PALcode 1958c2ecf20Sopenharmony_ci offset address, and get actual PC from 1968c2ecf20Sopenharmony_ci EXC_ADDR. */ 1978c2ecf20Sopenharmony_ci oprofile_add_pc(regs->pc, kern, which); 1988c2ecf20Sopenharmony_ci if ((pmpc & ((1 << 15) - 1)) == 581) 1998c2ecf20Sopenharmony_ci op_add_pm(regs->pc, kern, which, 2008c2ecf20Sopenharmony_ci ctr, PM_ITB_MISS); 2018c2ecf20Sopenharmony_ci /* Most other bit and counter values will be 2028c2ecf20Sopenharmony_ci those for the first instruction in the 2038c2ecf20Sopenharmony_ci fault handler, so we're done. */ 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci case TRAP_REPLAY: 2068c2ecf20Sopenharmony_ci op_add_pm(pmpc, kern, which, ctr, 2078c2ecf20Sopenharmony_ci (i_stat.fields.load_store 2088c2ecf20Sopenharmony_ci ? PM_LOAD_STORE : PM_REPLAY)); 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci case TRAP_DTB_DOUBLE_MISS_3: 2118c2ecf20Sopenharmony_ci case TRAP_DTB_DOUBLE_MISS_4: 2128c2ecf20Sopenharmony_ci case TRAP_DTB_SINGLE_MISS: 2138c2ecf20Sopenharmony_ci op_add_pm(pmpc, kern, which, ctr, PM_DTB_MISS); 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case TRAP_UNALIGNED: 2168c2ecf20Sopenharmony_ci op_add_pm(pmpc, kern, which, ctr, PM_UNALIGNED); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case TRAP_INVALID0: 2198c2ecf20Sopenharmony_ci case TRAP_FP_DISABLED: 2208c2ecf20Sopenharmony_ci case TRAP_DSTREAM_FAULT: 2218c2ecf20Sopenharmony_ci case TRAP_OPCDEC: 2228c2ecf20Sopenharmony_ci case TRAP_MACHINE_CHECK: 2238c2ecf20Sopenharmony_ci case TRAP_ARITHMETIC: 2248c2ecf20Sopenharmony_ci case TRAP_MT_FPCR: 2258c2ecf20Sopenharmony_ci case TRAP_RESET: 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* ??? JSR/JMP/RET/COR or HW_JSR/HW_JMP/HW_RET/HW_COR 2308c2ecf20Sopenharmony_ci mispredicts do not set this bit but can be 2318c2ecf20Sopenharmony_ci recognized by the presence of one of these 2328c2ecf20Sopenharmony_ci instructions at the PMPC location with bit 39 2338c2ecf20Sopenharmony_ci set. */ 2348c2ecf20Sopenharmony_ci if (i_stat.fields.mispredict) { 2358c2ecf20Sopenharmony_ci mispredict = 1; 2368c2ecf20Sopenharmony_ci op_add_pm(pmpc, kern, which, ctr, PM_MISPREDICT); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci oprofile_add_pc(pmpc, kern, which); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci pctr_ctl = wrperfmon(5, 0); 2438c2ecf20Sopenharmony_ci if (pctr_ctl & (1UL << 27)) 2448c2ecf20Sopenharmony_ci op_add_pm(pmpc, kern, which, ctr, PM_STALLED); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Unfortunately, TAK is undefined on mispredicted branches. 2478c2ecf20Sopenharmony_ci ??? It is also undefined for non-cbranch insns, should 2488c2ecf20Sopenharmony_ci check that. */ 2498c2ecf20Sopenharmony_ci if (!mispredict && pctr_ctl & (1UL << 0)) 2508c2ecf20Sopenharmony_ci op_add_pm(pmpc, kern, which, ctr, PM_TAKEN); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistruct op_axp_model op_model_ev67 = { 2548c2ecf20Sopenharmony_ci .reg_setup = ev67_reg_setup, 2558c2ecf20Sopenharmony_ci .cpu_setup = ev67_cpu_setup, 2568c2ecf20Sopenharmony_ci .reset_ctr = ev67_reset_ctr, 2578c2ecf20Sopenharmony_ci .handle_interrupt = ev67_handle_interrupt, 2588c2ecf20Sopenharmony_ci .cpu_type = "alpha/ev67", 2598c2ecf20Sopenharmony_ci .num_counters = 20, 2608c2ecf20Sopenharmony_ci .can_set_proc_mode = 0, 2618c2ecf20Sopenharmony_ci}; 262