1/** 2 * @file op_model_ppro.h 3 * pentium pro / P6 model-specific MSR operations 4 * 5 * @remark Copyright 2002 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author John Levon 9 * @author Philippe Elie 10 * @author Graydon Hoare 11 */ 12 13#include <linux/oprofile.h> 14#include <asm/ptrace.h> 15#include <asm/msr.h> 16#include <asm/apic.h> 17#include <asm/nmi.h> 18 19#include "op_x86_model.h" 20#include "op_counter.h" 21 22#define NUM_COUNTERS 2 23#define NUM_CONTROLS 2 24 25#define CTR_IS_RESERVED(msrs,c) (msrs->counters[(c)].addr ? 1 : 0) 26#define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters[(c)].addr, (l), (h));} while (0) 27#define CTR_32BIT_WRITE(l,msrs,c) \ 28 do {wrmsr(msrs->counters[(c)].addr, -(u32)(l), 0);} while (0) 29#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) 30 31#define CTRL_IS_RESERVED(msrs,c) (msrs->controls[(c)].addr ? 1 : 0) 32#define CTRL_READ(l,h,msrs,c) do {rdmsr((msrs->controls[(c)].addr), (l), (h));} while (0) 33#define CTRL_WRITE(l,h,msrs,c) do {wrmsr((msrs->controls[(c)].addr), (l), (h));} while (0) 34#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) 35#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) 36#define CTRL_CLEAR(x) (x &= (1<<21)) 37#define CTRL_SET_ENABLE(val) (val |= 1<<20) 38#define CTRL_SET_USR(val,u) (val |= ((u & 1) << 16)) 39#define CTRL_SET_KERN(val,k) (val |= ((k & 1) << 17)) 40#define CTRL_SET_UM(val, m) (val |= (m << 8)) 41#define CTRL_SET_EVENT(val, e) (val |= e) 42 43static unsigned long reset_value[NUM_COUNTERS]; 44 45static void ppro_fill_in_addresses(struct op_msrs * const msrs) 46{ 47 int i; 48 49 for (i=0; i < NUM_COUNTERS; i++) { 50 if (reserve_perfctr_nmi(MSR_P6_PERFCTR0 + i)) 51 msrs->counters[i].addr = MSR_P6_PERFCTR0 + i; 52 else 53 msrs->counters[i].addr = 0; 54 } 55 56 for (i=0; i < NUM_CONTROLS; i++) { 57 if (reserve_evntsel_nmi(MSR_P6_EVNTSEL0 + i)) 58 msrs->controls[i].addr = MSR_P6_EVNTSEL0 + i; 59 else 60 msrs->controls[i].addr = 0; 61 } 62} 63 64 65static void ppro_setup_ctrs(struct op_msrs const * const msrs) 66{ 67 unsigned int low, high; 68 int i; 69 70 /* clear all counters */ 71 for (i = 0 ; i < NUM_CONTROLS; ++i) { 72 if (unlikely(!CTRL_IS_RESERVED(msrs,i))) 73 continue; 74 CTRL_READ(low, high, msrs, i); 75 CTRL_CLEAR(low); 76 CTRL_WRITE(low, high, msrs, i); 77 } 78 79 /* avoid a false detection of ctr overflows in NMI handler */ 80 for (i = 0; i < NUM_COUNTERS; ++i) { 81 if (unlikely(!CTR_IS_RESERVED(msrs,i))) 82 continue; 83 CTR_32BIT_WRITE(1, msrs, i); 84 } 85 86 /* enable active counters */ 87 for (i = 0; i < NUM_COUNTERS; ++i) { 88 if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs,i))) { 89 reset_value[i] = counter_config[i].count; 90 91 CTR_32BIT_WRITE(counter_config[i].count, msrs, i); 92 93 CTRL_READ(low, high, msrs, i); 94 CTRL_CLEAR(low); 95 CTRL_SET_ENABLE(low); 96 CTRL_SET_USR(low, counter_config[i].user); 97 CTRL_SET_KERN(low, counter_config[i].kernel); 98 CTRL_SET_UM(low, counter_config[i].unit_mask); 99 CTRL_SET_EVENT(low, counter_config[i].event); 100 CTRL_WRITE(low, high, msrs, i); 101 } else { 102 reset_value[i] = 0; 103 } 104 } 105} 106 107 108static int ppro_check_ctrs(struct pt_regs * const regs, 109 struct op_msrs const * const msrs) 110{ 111 unsigned int low, high; 112 int i; 113 114 for (i = 0 ; i < NUM_COUNTERS; ++i) { 115 if (!reset_value[i]) 116 continue; 117 CTR_READ(low, high, msrs, i); 118 if (CTR_OVERFLOWED(low)) { 119 oprofile_add_sample(regs, i); 120 CTR_32BIT_WRITE(reset_value[i], msrs, i); 121 } 122 } 123 124 /* Only P6 based Pentium M need to re-unmask the apic vector but it 125 * doesn't hurt other P6 variant */ 126 apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); 127 128 /* We can't work out if we really handled an interrupt. We 129 * might have caught a *second* counter just after overflowing 130 * the interrupt for this counter then arrives 131 * and we don't find a counter that's overflowed, so we 132 * would return 0 and get dazed + confused. Instead we always 133 * assume we found an overflow. This sucks. 134 */ 135 return 1; 136} 137 138 139static void ppro_start(struct op_msrs const * const msrs) 140{ 141 unsigned int low,high; 142 int i; 143 144 for (i = 0; i < NUM_COUNTERS; ++i) { 145 if (reset_value[i]) { 146 CTRL_READ(low, high, msrs, i); 147 CTRL_SET_ACTIVE(low); 148 CTRL_WRITE(low, high, msrs, i); 149 } 150 } 151} 152 153 154static void ppro_stop(struct op_msrs const * const msrs) 155{ 156 unsigned int low,high; 157 int i; 158 159 for (i = 0; i < NUM_COUNTERS; ++i) { 160 if (!reset_value[i]) 161 continue; 162 CTRL_READ(low, high, msrs, i); 163 CTRL_SET_INACTIVE(low); 164 CTRL_WRITE(low, high, msrs, i); 165 } 166} 167 168static void ppro_shutdown(struct op_msrs const * const msrs) 169{ 170 int i; 171 172 for (i = 0 ; i < NUM_COUNTERS ; ++i) { 173 if (CTR_IS_RESERVED(msrs,i)) 174 release_perfctr_nmi(MSR_P6_PERFCTR0 + i); 175 } 176 for (i = 0 ; i < NUM_CONTROLS ; ++i) { 177 if (CTRL_IS_RESERVED(msrs,i)) 178 release_evntsel_nmi(MSR_P6_EVNTSEL0 + i); 179 } 180} 181 182 183struct op_x86_model_spec const op_ppro_spec = { 184 .num_counters = NUM_COUNTERS, 185 .num_controls = NUM_CONTROLS, 186 .fill_in_addresses = &ppro_fill_in_addresses, 187 .setup_ctrs = &ppro_setup_ctrs, 188 .check_ctrs = &ppro_check_ctrs, 189 .start = &ppro_start, 190 .stop = &ppro_stop, 191 .shutdown = &ppro_shutdown 192}; 193