123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- /**
- * @file arch/alpha/oprofile/op_model_ev67.c
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author Richard Henderson <rth@twiddle.net>
- * @author Falk Hueffner <falk@debian.org>
- */
- #include <linux/oprofile.h>
- #include <linux/smp.h>
- #include <asm/ptrace.h>
- #include "op_impl.h"
- /* Compute all of the registers in preparation for enabling profiling. */
- static void
- ev67_reg_setup(struct op_register_config *reg,
- struct op_counter_config *ctr,
- struct op_system_config *sys)
- {
- unsigned long ctl, reset, need_reset, i;
- /* Select desired events. */
- ctl = 1UL << 4; /* Enable ProfileMe mode. */
- /* The event numbers are chosen so we can use them directly if
- PCTR1 is enabled. */
- if (ctr[1].enabled) {
- ctl |= (ctr[1].event & 3) << 2;
- } else {
- if (ctr[0].event == 0) /* cycles */
- ctl |= 1UL << 2;
- }
- reg->mux_select = ctl;
- /* Select logging options. */
- /* ??? Need to come up with some mechanism to trace only
- selected processes. EV67 does not have a mechanism to
- select kernel or user mode only. For now, enable always. */
- reg->proc_mode = 0;
- /* EV67 cannot change the width of the counters as with the
- other implementations. But fortunately, we can write to
- the counters and set the value such that it will overflow
- at the right time. */
- reset = need_reset = 0;
- for (i = 0; i < 2; ++i) {
- unsigned long count = ctr[i].count;
- if (!ctr[i].enabled)
- continue;
- if (count > 0x100000)
- count = 0x100000;
- ctr[i].count = count;
- reset |= (0x100000 - count) << (i ? 6 : 28);
- if (count != 0x100000)
- need_reset |= 1 << i;
- }
- reg->reset_values = reset;
- reg->need_reset = need_reset;
- }
- /* Program all of the registers in preparation for enabling profiling. */
- static void
- ev67_cpu_setup (void *x)
- {
- struct op_register_config *reg = x;
- wrperfmon(2, reg->mux_select);
- wrperfmon(3, reg->proc_mode);
- wrperfmon(6, reg->reset_values | 3);
- }
- /* CTR is a counter for which the user has requested an interrupt count
- in between one of the widths selectable in hardware. Reset the count
- for CTR to the value stored in REG->RESET_VALUES. */
- static void
- ev67_reset_ctr(struct op_register_config *reg, unsigned long ctr)
- {
- wrperfmon(6, reg->reset_values | (1 << ctr));
- }
- /* ProfileMe conditions which will show up as counters. We can also
- detect the following, but it seems unlikely that anybody is
- interested in counting them:
- * Reset
- * MT_FPCR (write to floating point control register)
- * Arithmetic trap
- * Dstream Fault
- * Machine Check (ECC fault, etc.)
- * OPCDEC (illegal opcode)
- * Floating point disabled
- * Differentiate between DTB single/double misses and 3 or 4 level
- page tables
- * Istream access violation
- * Interrupt
- * Icache Parity Error.
- * Instruction killed (nop, trapb)
- Unfortunately, there seems to be no way to detect Dcache and Bcache
- misses; the latter could be approximated by making the counter
- count Bcache misses, but that is not precise.
- We model this as 20 counters:
- * PCTR0
- * PCTR1
- * 9 ProfileMe events, induced by PCTR0
- * 9 ProfileMe events, induced by PCTR1
- */
- enum profileme_counters {
- PM_STALLED, /* Stalled for at least one cycle
- between the fetch and map stages */
- PM_TAKEN, /* Conditional branch taken */
- PM_MISPREDICT, /* Branch caused mispredict trap */
- PM_ITB_MISS, /* ITB miss */
- PM_DTB_MISS, /* DTB miss */
- PM_REPLAY, /* Replay trap */
- PM_LOAD_STORE, /* Load-store order trap */
- PM_ICACHE_MISS, /* Icache miss */
- PM_UNALIGNED, /* Unaligned Load/Store */
- PM_NUM_COUNTERS
- };
- static inline void
- op_add_pm(unsigned long pc, int kern, unsigned long counter,
- struct op_counter_config *ctr, unsigned long event)
- {
- unsigned long fake_counter = 2 + event;
- if (counter == 1)
- fake_counter += PM_NUM_COUNTERS;
- if (ctr[fake_counter].enabled)
- oprofile_add_pc(pc, kern, fake_counter);
- }
- static void
- ev67_handle_interrupt(unsigned long which, struct pt_regs *regs,
- struct op_counter_config *ctr)
- {
- unsigned long pmpc, pctr_ctl;
- int kern = !user_mode(regs);
- int mispredict = 0;
- union {
- unsigned long v;
- struct {
- unsigned reserved: 30; /* 0-29 */
- unsigned overcount: 3; /* 30-32 */
- unsigned icache_miss: 1; /* 33 */
- unsigned trap_type: 4; /* 34-37 */
- unsigned load_store: 1; /* 38 */
- unsigned trap: 1; /* 39 */
- unsigned mispredict: 1; /* 40 */
- } fields;
- } i_stat;
- enum trap_types {
- TRAP_REPLAY,
- TRAP_INVALID0,
- TRAP_DTB_DOUBLE_MISS_3,
- TRAP_DTB_DOUBLE_MISS_4,
- TRAP_FP_DISABLED,
- TRAP_UNALIGNED,
- TRAP_DTB_SINGLE_MISS,
- TRAP_DSTREAM_FAULT,
- TRAP_OPCDEC,
- TRAP_INVALID1,
- TRAP_MACHINE_CHECK,
- TRAP_INVALID2,
- TRAP_ARITHMETIC,
- TRAP_INVALID3,
- TRAP_MT_FPCR,
- TRAP_RESET
- };
- pmpc = wrperfmon(9, 0);
- /* ??? Don't know how to handle physical-mode PALcode address. */
- if (pmpc & 1)
- return;
- pmpc &= ~2; /* clear reserved bit */
- i_stat.v = wrperfmon(8, 0);
- if (i_stat.fields.trap) {
- switch (i_stat.fields.trap_type) {
- case TRAP_INVALID1:
- case TRAP_INVALID2:
- case TRAP_INVALID3:
- /* Pipeline redirection occurred. PMPC points
- to PALcode. Recognize ITB miss by PALcode
- offset address, and get actual PC from
- EXC_ADDR. */
- oprofile_add_pc(regs->pc, kern, which);
- if ((pmpc & ((1 << 15) - 1)) == 581)
- op_add_pm(regs->pc, kern, which,
- ctr, PM_ITB_MISS);
- /* Most other bit and counter values will be
- those for the first instruction in the
- fault handler, so we're done. */
- return;
- case TRAP_REPLAY:
- op_add_pm(pmpc, kern, which, ctr,
- (i_stat.fields.load_store
- ? PM_LOAD_STORE : PM_REPLAY));
- break;
- case TRAP_DTB_DOUBLE_MISS_3:
- case TRAP_DTB_DOUBLE_MISS_4:
- case TRAP_DTB_SINGLE_MISS:
- op_add_pm(pmpc, kern, which, ctr, PM_DTB_MISS);
- break;
- case TRAP_UNALIGNED:
- op_add_pm(pmpc, kern, which, ctr, PM_UNALIGNED);
- break;
- case TRAP_INVALID0:
- case TRAP_FP_DISABLED:
- case TRAP_DSTREAM_FAULT:
- case TRAP_OPCDEC:
- case TRAP_MACHINE_CHECK:
- case TRAP_ARITHMETIC:
- case TRAP_MT_FPCR:
- case TRAP_RESET:
- break;
- }
- /* ??? JSR/JMP/RET/COR or HW_JSR/HW_JMP/HW_RET/HW_COR
- mispredicts do not set this bit but can be
- recognized by the presence of one of these
- instructions at the PMPC location with bit 39
- set. */
- if (i_stat.fields.mispredict) {
- mispredict = 1;
- op_add_pm(pmpc, kern, which, ctr, PM_MISPREDICT);
- }
- }
- oprofile_add_pc(pmpc, kern, which);
- pctr_ctl = wrperfmon(5, 0);
- if (pctr_ctl & (1UL << 27))
- op_add_pm(pmpc, kern, which, ctr, PM_STALLED);
- /* Unfortunately, TAK is undefined on mispredicted branches.
- ??? It is also undefined for non-cbranch insns, should
- check that. */
- if (!mispredict && pctr_ctl & (1UL << 0))
- op_add_pm(pmpc, kern, which, ctr, PM_TAKEN);
- }
- struct op_axp_model op_model_ev67 = {
- .reg_setup = ev67_reg_setup,
- .cpu_setup = ev67_cpu_setup,
- .reset_ctr = ev67_reset_ctr,
- .handle_interrupt = ev67_handle_interrupt,
- .cpu_type = "alpha/ev67",
- .num_counters = 20,
- .can_set_proc_mode = 0,
- };
|