Commit | Line | Data |
---|---|---|
8b45b72b | 1 | /* |
1da177e4 LT |
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> | |
3e4ff115 | 17 | #include <asm/nmi.h> |
8b45b72b | 18 | |
1da177e4 LT |
19 | #include "op_x86_model.h" |
20 | #include "op_counter.h" | |
21 | ||
22 | #define NUM_COUNTERS 2 | |
23 | #define NUM_CONTROLS 2 | |
24 | ||
8b45b72b PC |
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) | |
1da177e4 LT |
29 | #define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) |
30 | ||
8b45b72b PC |
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) | |
1da177e4 LT |
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) | |
8b45b72b PC |
38 | #define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) |
39 | #define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) | |
1da177e4 LT |
40 | #define CTRL_SET_UM(val, m) (val |= (m << 8)) |
41 | #define CTRL_SET_EVENT(val, e) (val |= e) | |
42 | ||
43 | static unsigned long reset_value[NUM_COUNTERS]; | |
8b45b72b | 44 | |
1da177e4 LT |
45 | static void ppro_fill_in_addresses(struct op_msrs * const msrs) |
46 | { | |
cb9c448c DZ |
47 | int i; |
48 | ||
8b45b72b | 49 | for (i = 0; i < NUM_COUNTERS; i++) { |
cb9c448c DZ |
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 | } | |
8b45b72b PC |
55 | |
56 | for (i = 0; i < NUM_CONTROLS; i++) { | |
cb9c448c DZ |
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 | } | |
1da177e4 LT |
62 | } |
63 | ||
64 | ||
65 | static 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) { | |
8b45b72b | 72 | if (unlikely(!CTRL_IS_RESERVED(msrs, i))) |
cb9c448c | 73 | continue; |
1da177e4 LT |
74 | CTRL_READ(low, high, msrs, i); |
75 | CTRL_CLEAR(low); | |
76 | CTRL_WRITE(low, high, msrs, i); | |
77 | } | |
8b45b72b | 78 | |
1da177e4 LT |
79 | /* avoid a false detection of ctr overflows in NMI handler */ |
80 | for (i = 0; i < NUM_COUNTERS; ++i) { | |
8b45b72b | 81 | if (unlikely(!CTR_IS_RESERVED(msrs, i))) |
cb9c448c | 82 | continue; |
44264261 | 83 | CTR_32BIT_WRITE(1, msrs, i); |
1da177e4 LT |
84 | } |
85 | ||
86 | /* enable active counters */ | |
87 | for (i = 0; i < NUM_COUNTERS; ++i) { | |
8b45b72b | 88 | if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { |
1da177e4 LT |
89 | reset_value[i] = counter_config[i].count; |
90 | ||
44264261 | 91 | CTR_32BIT_WRITE(counter_config[i].count, msrs, i); |
1da177e4 LT |
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); | |
cb9c448c DZ |
101 | } else { |
102 | reset_value[i] = 0; | |
1da177e4 LT |
103 | } |
104 | } | |
105 | } | |
106 | ||
8b45b72b | 107 | |
1da177e4 LT |
108 | static 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; | |
8b45b72b | 113 | |
1da177e4 | 114 | for (i = 0 ; i < NUM_COUNTERS; ++i) { |
cb9c448c DZ |
115 | if (!reset_value[i]) |
116 | continue; | |
1da177e4 LT |
117 | CTR_READ(low, high, msrs, i); |
118 | if (CTR_OVERFLOWED(low)) { | |
119 | oprofile_add_sample(regs, i); | |
44264261 | 120 | CTR_32BIT_WRITE(reset_value[i], msrs, i); |
1da177e4 LT |
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 | ||
8b45b72b | 138 | |
1da177e4 LT |
139 | static void ppro_start(struct op_msrs const * const msrs) |
140 | { | |
8b45b72b | 141 | unsigned int low, high; |
6b77df08 | 142 | int i; |
cb9c448c | 143 | |
6b77df08 AS |
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 | } | |
cb9c448c | 150 | } |
1da177e4 LT |
151 | } |
152 | ||
153 | ||
154 | static void ppro_stop(struct op_msrs const * const msrs) | |
155 | { | |
8b45b72b | 156 | unsigned int low, high; |
6b77df08 | 157 | int i; |
cb9c448c | 158 | |
6b77df08 AS |
159 | for (i = 0; i < NUM_COUNTERS; ++i) { |
160 | if (!reset_value[i]) | |
161 | continue; | |
162 | CTRL_READ(low, high, msrs, i); | |
cb9c448c | 163 | CTRL_SET_INACTIVE(low); |
6b77df08 | 164 | CTRL_WRITE(low, high, msrs, i); |
cb9c448c DZ |
165 | } |
166 | } | |
167 | ||
168 | static void ppro_shutdown(struct op_msrs const * const msrs) | |
169 | { | |
170 | int i; | |
171 | ||
172 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { | |
8b45b72b | 173 | if (CTR_IS_RESERVED(msrs, i)) |
cb9c448c DZ |
174 | release_perfctr_nmi(MSR_P6_PERFCTR0 + i); |
175 | } | |
176 | for (i = 0 ; i < NUM_CONTROLS ; ++i) { | |
8b45b72b | 177 | if (CTRL_IS_RESERVED(msrs, i)) |
cb9c448c DZ |
178 | release_evntsel_nmi(MSR_P6_EVNTSEL0 + i); |
179 | } | |
1da177e4 LT |
180 | } |
181 | ||
182 | ||
183 | struct 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, | |
cb9c448c DZ |
190 | .stop = &ppro_stop, |
191 | .shutdown = &ppro_shutdown | |
1da177e4 | 192 | }; |