/root/src/xen/xen/arch/x86/oprofile/op_model_athlon.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file op_model_athlon.h |
3 | | * athlon / K7 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 <xen/types.h> |
14 | | #include <asm/msr.h> |
15 | | #include <asm/io.h> |
16 | | #include <asm/apic.h> |
17 | | #include <asm/processor.h> |
18 | | #include <xen/xenoprof.h> |
19 | | #include <asm/regs.h> |
20 | | #include <asm/current.h> |
21 | | #include <asm/hvm/support.h> |
22 | | #include <xen/pci_regs.h> |
23 | | #include <xen/pci_ids.h> |
24 | | |
25 | | #include "op_x86_model.h" |
26 | | #include "op_counter.h" |
27 | | |
28 | | #define K7_NUM_COUNTERS 4 |
29 | | #define K7_NUM_CONTROLS 4 |
30 | | |
31 | | #define FAM15H_NUM_COUNTERS 6 |
32 | | #define FAM15H_NUM_CONTROLS 6 |
33 | | |
34 | | #define MAX_COUNTERS FAM15H_NUM_COUNTERS |
35 | | |
36 | 0 | #define CTR_READ(msr_content,msrs,c) do {rdmsrl(msrs->counters[(c)].addr, (msr_content));} while (0) |
37 | 0 | #define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1);} while (0) |
38 | 0 | #define CTR_OVERFLOWED(n) (!((n) & (1ULL<<31))) |
39 | | |
40 | 0 | #define CTRL_READ(msr_content,msrs,c) do {rdmsrl(msrs->controls[(c)].addr, (msr_content));} while (0) |
41 | 0 | #define CTRL_WRITE(msr_content,msrs,c) do {wrmsrl(msrs->controls[(c)].addr, (msr_content));} while (0) |
42 | 0 | #define CTRL_SET_ACTIVE(n) (n |= (1ULL<<22)) |
43 | 0 | #define CTRL_SET_INACTIVE(n) (n &= ~(1ULL<<22)) |
44 | 0 | #define CTRL_CLEAR(val) (val &= (1ULL<<21)) |
45 | 0 | #define CTRL_SET_ENABLE(val) (val |= 1ULL<<20) |
46 | 0 | #define CTRL_SET_USR(val,u) (val |= ((u & 1) << 16)) |
47 | 0 | #define CTRL_SET_KERN(val,k) (val |= ((k & 1) << 17)) |
48 | 0 | #define CTRL_SET_UM(val, m) (val |= ((m & 0xff) << 8)) |
49 | 0 | #define CTRL_SET_EVENT(val, e) (val |= (((e >> 8) & 0xf) | (e & 0xff))) |
50 | 0 | #define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 0x1ULL) << 41)) |
51 | 0 | #define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 0x1ULL) << 40)) |
52 | | |
53 | | static unsigned long reset_value[MAX_COUNTERS]; |
54 | | |
55 | | extern char svm_stgi_label[]; |
56 | | |
57 | | u32 ibs_caps = 0; |
58 | | static u64 ibs_op_ctl; |
59 | | |
60 | | /* IBS cpuid feature detection */ |
61 | 0 | #define IBS_CPUID_FEATURES 0x8000001b |
62 | | |
63 | | /* IBS MSRs */ |
64 | 0 | #define MSR_AMD64_IBSFETCHCTL 0xc0011030 |
65 | | #define MSR_AMD64_IBSFETCHLINAD 0xc0011031 |
66 | | #define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032 |
67 | 0 | #define MSR_AMD64_IBSOPCTL 0xc0011033 |
68 | | #define MSR_AMD64_IBSOPRIP 0xc0011034 |
69 | | #define MSR_AMD64_IBSOPDATA 0xc0011035 |
70 | | #define MSR_AMD64_IBSOPDATA2 0xc0011036 |
71 | | #define MSR_AMD64_IBSOPDATA3 0xc0011037 |
72 | | #define MSR_AMD64_IBSDCLINAD 0xc0011038 |
73 | | #define MSR_AMD64_IBSDCPHYSAD 0xc0011039 |
74 | | #define MSR_AMD64_IBSCTL 0xc001103a |
75 | | |
76 | | /* |
77 | | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but |
78 | | * bit 0 is used to indicate the existence of IBS. |
79 | | */ |
80 | 0 | #define IBS_CAPS_AVAIL (1LL<<0) |
81 | 0 | #define IBS_CAPS_RDWROPCNT (1LL<<3) |
82 | 0 | #define IBS_CAPS_OPCNT (1LL<<4) |
83 | | |
84 | | /* IBS randomization macros */ |
85 | 0 | #define IBS_RANDOM_BITS 12 |
86 | 0 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) |
87 | | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) |
88 | | |
89 | | /* IbsFetchCtl bits/masks */ |
90 | 0 | #define IBS_FETCH_RAND_EN (1ULL<<57) |
91 | 0 | #define IBS_FETCH_VAL (1ULL<<49) |
92 | 0 | #define IBS_FETCH_ENABLE (1ULL<<48) |
93 | 0 | #define IBS_FETCH_CNT 0xFFFF0000ULL |
94 | 0 | #define IBS_FETCH_MAX_CNT 0x0000FFFFULL |
95 | | |
96 | | /* IbsOpCtl bits */ |
97 | 0 | #define IBS_OP_CNT_CTL (1ULL<<19) |
98 | 0 | #define IBS_OP_VAL (1ULL<<18) |
99 | 0 | #define IBS_OP_ENABLE (1ULL<<17) |
100 | | #define IBS_OP_MAX_CNT 0x0000FFFFULL |
101 | | |
102 | | /* IBS sample identifier */ |
103 | 0 | #define IBS_FETCH_CODE 13 |
104 | 0 | #define IBS_OP_CODE 14 |
105 | | |
106 | 0 | #define clamp(val, min, max) ({ \ |
107 | 0 | typeof(val) __val = (val); \ |
108 | 0 | typeof(min) __min = (min); \ |
109 | 0 | typeof(max) __max = (max); \ |
110 | 0 | (void) (&__val == &__min); \ |
111 | 0 | (void) (&__val == &__max); \ |
112 | 0 | __val = __val < __min ? __min: __val; \ |
113 | 0 | __val > __max ? __max: __val; }) |
114 | | |
115 | | /* |
116 | | * 16-bit Linear Feedback Shift Register (LFSR) |
117 | | */ |
118 | | static unsigned int lfsr_random(void) |
119 | 0 | { |
120 | 0 | static unsigned int lfsr_value = 0xF00D; |
121 | 0 | unsigned int bit; |
122 | 0 |
|
123 | 0 | /* Compute next bit to shift in */ |
124 | 0 | bit = ((lfsr_value >> 0) ^ |
125 | 0 | (lfsr_value >> 2) ^ |
126 | 0 | (lfsr_value >> 3) ^ |
127 | 0 | (lfsr_value >> 5)) & 0x0001; |
128 | 0 |
|
129 | 0 | /* Advance to next register value */ |
130 | 0 | lfsr_value = (lfsr_value >> 1) | (bit << 15); |
131 | 0 |
|
132 | 0 | return lfsr_value; |
133 | 0 | } |
134 | | |
135 | | /* |
136 | | * IBS software randomization |
137 | | * |
138 | | * The IBS periodic op counter is randomized in software. The lower 12 |
139 | | * bits of the 20 bit counter are randomized. IbsOpCurCnt is |
140 | | * initialized with a 12 bit random value. |
141 | | */ |
142 | | static inline u64 op_amd_randomize_ibs_op(u64 val) |
143 | 0 | { |
144 | 0 | unsigned int random = lfsr_random(); |
145 | 0 |
|
146 | 0 | if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) |
147 | 0 | /* |
148 | 0 | * Work around if the hw can not write to IbsOpCurCnt |
149 | 0 | * |
150 | 0 | * Randomize the lower 8 bits of the 16 bit |
151 | 0 | * IbsOpMaxCnt [15:0] value in the range of -128 to |
152 | 0 | * +127 by adding/subtracting an offset to the |
153 | 0 | * maximum count (IbsOpMaxCnt). |
154 | 0 | * |
155 | 0 | * To avoid over or underflows and protect upper bits |
156 | 0 | * starting at bit 16, the initial value for |
157 | 0 | * IbsOpMaxCnt must fit in the range from 0x0081 to |
158 | 0 | * 0xff80. |
159 | 0 | */ |
160 | 0 | val += (s8)(random >> 4); |
161 | 0 | else |
162 | 0 | val |= (u64)(random & IBS_RANDOM_MASK) << 32; |
163 | 0 |
|
164 | 0 | return val; |
165 | 0 | } |
166 | | |
167 | | static void athlon_fill_in_addresses(struct op_msrs * const msrs) |
168 | 0 | { |
169 | 0 | msrs->counters[0].addr = MSR_K7_PERFCTR0; |
170 | 0 | msrs->counters[1].addr = MSR_K7_PERFCTR1; |
171 | 0 | msrs->counters[2].addr = MSR_K7_PERFCTR2; |
172 | 0 | msrs->counters[3].addr = MSR_K7_PERFCTR3; |
173 | 0 |
|
174 | 0 | msrs->controls[0].addr = MSR_K7_EVNTSEL0; |
175 | 0 | msrs->controls[1].addr = MSR_K7_EVNTSEL1; |
176 | 0 | msrs->controls[2].addr = MSR_K7_EVNTSEL2; |
177 | 0 | msrs->controls[3].addr = MSR_K7_EVNTSEL3; |
178 | 0 | } |
179 | | |
180 | | static void fam15h_fill_in_addresses(struct op_msrs * const msrs) |
181 | 0 | { |
182 | 0 | msrs->counters[0].addr = MSR_AMD_FAM15H_PERFCTR0; |
183 | 0 | msrs->counters[1].addr = MSR_AMD_FAM15H_PERFCTR1; |
184 | 0 | msrs->counters[2].addr = MSR_AMD_FAM15H_PERFCTR2; |
185 | 0 | msrs->counters[3].addr = MSR_AMD_FAM15H_PERFCTR3; |
186 | 0 | msrs->counters[4].addr = MSR_AMD_FAM15H_PERFCTR4; |
187 | 0 | msrs->counters[5].addr = MSR_AMD_FAM15H_PERFCTR5; |
188 | 0 |
|
189 | 0 | msrs->controls[0].addr = MSR_AMD_FAM15H_EVNTSEL0; |
190 | 0 | msrs->controls[1].addr = MSR_AMD_FAM15H_EVNTSEL1; |
191 | 0 | msrs->controls[2].addr = MSR_AMD_FAM15H_EVNTSEL2; |
192 | 0 | msrs->controls[3].addr = MSR_AMD_FAM15H_EVNTSEL3; |
193 | 0 | msrs->controls[4].addr = MSR_AMD_FAM15H_EVNTSEL4; |
194 | 0 | msrs->controls[5].addr = MSR_AMD_FAM15H_EVNTSEL5; |
195 | 0 | } |
196 | | |
197 | | static void athlon_setup_ctrs(struct op_msrs const * const msrs) |
198 | 0 | { |
199 | 0 | uint64_t msr_content; |
200 | 0 | int i; |
201 | 0 | unsigned int const nr_ctrs = model->num_counters; |
202 | 0 | unsigned int const nr_ctrls = model->num_controls; |
203 | 0 | |
204 | 0 | /* clear all counters */ |
205 | 0 | for (i = 0 ; i < nr_ctrls; ++i) { |
206 | 0 | CTRL_READ(msr_content, msrs, i); |
207 | 0 | CTRL_CLEAR(msr_content); |
208 | 0 | CTRL_WRITE(msr_content, msrs, i); |
209 | 0 | } |
210 | 0 | |
211 | 0 | /* avoid a false detection of ctr overflows in NMI handler */ |
212 | 0 | for (i = 0; i < nr_ctrs; ++i) { |
213 | 0 | CTR_WRITE(1, msrs, i); |
214 | 0 | } |
215 | 0 |
|
216 | 0 | /* enable active counters */ |
217 | 0 | for (i = 0; i < nr_ctrs; ++i) { |
218 | 0 | if (counter_config[i].enabled) { |
219 | 0 | reset_value[i] = counter_config[i].count; |
220 | 0 |
|
221 | 0 | CTR_WRITE(counter_config[i].count, msrs, i); |
222 | 0 |
|
223 | 0 | CTRL_READ(msr_content, msrs, i); |
224 | 0 | CTRL_CLEAR(msr_content); |
225 | 0 | CTRL_SET_ENABLE(msr_content); |
226 | 0 | CTRL_SET_USR(msr_content, counter_config[i].user); |
227 | 0 | CTRL_SET_KERN(msr_content, counter_config[i].kernel); |
228 | 0 | CTRL_SET_UM(msr_content, counter_config[i].unit_mask); |
229 | 0 | CTRL_SET_EVENT(msr_content, counter_config[i].event); |
230 | 0 | CTRL_SET_HOST_ONLY(msr_content, 0); |
231 | 0 | CTRL_SET_GUEST_ONLY(msr_content, 0); |
232 | 0 | CTRL_WRITE(msr_content, msrs, i); |
233 | 0 | } else { |
234 | 0 | reset_value[i] = 0; |
235 | 0 | } |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | static inline void |
240 | | ibs_log_event(u64 data, struct cpu_user_regs const * const regs, int mode) |
241 | 0 | { |
242 | 0 | struct vcpu *v = current; |
243 | 0 | u32 temp = 0; |
244 | 0 |
|
245 | 0 | temp = data & 0xFFFFFFFF; |
246 | 0 | xenoprof_log_event(v, regs, temp, mode, 0); |
247 | 0 | |
248 | 0 | temp = (data >> 32) & 0xFFFFFFFF; |
249 | 0 | xenoprof_log_event(v, regs, temp, mode, 0); |
250 | 0 | |
251 | 0 | } |
252 | | |
253 | | static inline int handle_ibs(int mode, struct cpu_user_regs const * const regs) |
254 | 0 | { |
255 | 0 | u64 val, ctl; |
256 | 0 | struct vcpu *v = current; |
257 | 0 |
|
258 | 0 | if (!ibs_caps) |
259 | 0 | return 1; |
260 | 0 |
|
261 | 0 | if (ibs_config.fetch_enabled) { |
262 | 0 | rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl); |
263 | 0 | if (ctl & IBS_FETCH_VAL) { |
264 | 0 | rdmsrl(MSR_AMD64_IBSFETCHLINAD, val); |
265 | 0 | xenoprof_log_event(v, regs, IBS_FETCH_CODE, mode, 0); |
266 | 0 | xenoprof_log_event(v, regs, val, mode, 0); |
267 | 0 |
|
268 | 0 | ibs_log_event(val, regs, mode); |
269 | 0 | ibs_log_event(ctl, regs, mode); |
270 | 0 |
|
271 | 0 | rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, val); |
272 | 0 | ibs_log_event(val, regs, mode); |
273 | 0 | |
274 | 0 | /* reenable the IRQ */ |
275 | 0 | ctl &= ~(IBS_FETCH_VAL | IBS_FETCH_CNT); |
276 | 0 | ctl |= IBS_FETCH_ENABLE; |
277 | 0 | wrmsrl(MSR_AMD64_IBSFETCHCTL, ctl); |
278 | 0 | } |
279 | 0 | } |
280 | 0 |
|
281 | 0 | if (ibs_config.op_enabled) { |
282 | 0 | rdmsrl(MSR_AMD64_IBSOPCTL, ctl); |
283 | 0 | if (ctl & IBS_OP_VAL) { |
284 | 0 |
|
285 | 0 | rdmsrl(MSR_AMD64_IBSOPRIP, val); |
286 | 0 | xenoprof_log_event(v, regs, IBS_OP_CODE, mode, 0); |
287 | 0 | xenoprof_log_event(v, regs, val, mode, 0); |
288 | 0 | |
289 | 0 | ibs_log_event(val, regs, mode); |
290 | 0 |
|
291 | 0 | rdmsrl(MSR_AMD64_IBSOPDATA, val); |
292 | 0 | ibs_log_event(val, regs, mode); |
293 | 0 | rdmsrl(MSR_AMD64_IBSOPDATA2, val); |
294 | 0 | ibs_log_event(val, regs, mode); |
295 | 0 | rdmsrl(MSR_AMD64_IBSOPDATA3, val); |
296 | 0 | ibs_log_event(val, regs, mode); |
297 | 0 | rdmsrl(MSR_AMD64_IBSDCLINAD, val); |
298 | 0 | ibs_log_event(val, regs, mode); |
299 | 0 | rdmsrl(MSR_AMD64_IBSDCPHYSAD, val); |
300 | 0 | ibs_log_event(val, regs, mode); |
301 | 0 |
|
302 | 0 | /* reenable the IRQ */ |
303 | 0 | ctl = op_amd_randomize_ibs_op(ibs_op_ctl); |
304 | 0 | wrmsrl(MSR_AMD64_IBSOPCTL, ctl); |
305 | 0 | } |
306 | 0 | } |
307 | 0 |
|
308 | 0 | return 1; |
309 | 0 | } |
310 | | |
311 | | static int athlon_check_ctrs(unsigned int const cpu, |
312 | | struct op_msrs const * const msrs, |
313 | | struct cpu_user_regs const * const regs) |
314 | | |
315 | 0 | { |
316 | 0 | uint64_t msr_content; |
317 | 0 | int i; |
318 | 0 | int ovf = 0; |
319 | 0 | unsigned long eip = regs->rip; |
320 | 0 | int mode = 0; |
321 | 0 | struct vcpu *v = current; |
322 | 0 | struct cpu_user_regs *guest_regs = guest_cpu_user_regs(); |
323 | 0 | unsigned int const nr_ctrs = model->num_counters; |
324 | 0 |
|
325 | 0 | if (!guest_mode(regs) && |
326 | 0 | (eip == (unsigned long)svm_stgi_label)) { |
327 | 0 | /* SVM guest was running when NMI occurred */ |
328 | 0 | ASSERT(is_hvm_vcpu(v)); |
329 | 0 | eip = guest_regs->rip; |
330 | 0 | mode = xenoprofile_get_mode(v, guest_regs); |
331 | 0 | } else |
332 | 0 | mode = xenoprofile_get_mode(v, regs); |
333 | 0 |
|
334 | 0 | for (i = 0 ; i < nr_ctrs; ++i) { |
335 | 0 | CTR_READ(msr_content, msrs, i); |
336 | 0 | if (CTR_OVERFLOWED(msr_content)) { |
337 | 0 | xenoprof_log_event(current, regs, eip, mode, i); |
338 | 0 | CTR_WRITE(reset_value[i], msrs, i); |
339 | 0 | ovf = 1; |
340 | 0 | } |
341 | 0 | } |
342 | 0 |
|
343 | 0 | ovf = handle_ibs(mode, regs); |
344 | 0 | /* See op_model_ppro.c */ |
345 | 0 | return ovf; |
346 | 0 | } |
347 | | |
348 | | static inline void start_ibs(void) |
349 | 0 | { |
350 | 0 | u64 val = 0; |
351 | 0 |
|
352 | 0 | if (!ibs_caps) |
353 | 0 | return; |
354 | 0 |
|
355 | 0 | if (ibs_config.fetch_enabled) { |
356 | 0 | val = (ibs_config.max_cnt_fetch >> 4) & IBS_FETCH_MAX_CNT; |
357 | 0 | val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0; |
358 | 0 | val |= IBS_FETCH_ENABLE; |
359 | 0 | wrmsrl(MSR_AMD64_IBSFETCHCTL, val); |
360 | 0 | } |
361 | 0 |
|
362 | 0 | if (ibs_config.op_enabled) { |
363 | 0 | ibs_op_ctl = ibs_config.max_cnt_op >> 4; |
364 | 0 | if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) { |
365 | 0 | /* |
366 | 0 | * IbsOpCurCnt not supported. See |
367 | 0 | * op_amd_randomize_ibs_op() for details. |
368 | 0 | */ |
369 | 0 | ibs_op_ctl = clamp((unsigned long long)ibs_op_ctl, |
370 | 0 | 0x0081ULL, 0xFF80ULL); |
371 | 0 | } else { |
372 | 0 | /* |
373 | 0 | * The start value is randomized with a |
374 | 0 | * positive offset, we need to compensate it |
375 | 0 | * with the half of the randomized range. Also |
376 | 0 | * avoid underflows. |
377 | 0 | */ |
378 | 0 | ibs_op_ctl = min(ibs_op_ctl + IBS_RANDOM_MAXCNT_OFFSET, |
379 | 0 | IBS_OP_MAX_CNT); |
380 | 0 | } |
381 | 0 | if (ibs_caps & IBS_CAPS_OPCNT && ibs_config.dispatched_ops) |
382 | 0 | ibs_op_ctl |= IBS_OP_CNT_CTL; |
383 | 0 | ibs_op_ctl |= IBS_OP_ENABLE; |
384 | 0 | val = op_amd_randomize_ibs_op(ibs_op_ctl); |
385 | 0 | wrmsrl(MSR_AMD64_IBSOPCTL, val); |
386 | 0 | } |
387 | 0 | } |
388 | | |
389 | | static void athlon_start(struct op_msrs const * const msrs) |
390 | 0 | { |
391 | 0 | uint64_t msr_content; |
392 | 0 | int i; |
393 | 0 | unsigned int const nr_ctrs = model->num_counters; |
394 | 0 | for (i = 0 ; i < nr_ctrs ; ++i) { |
395 | 0 | if (reset_value[i]) { |
396 | 0 | CTRL_READ(msr_content, msrs, i); |
397 | 0 | CTRL_SET_ACTIVE(msr_content); |
398 | 0 | CTRL_WRITE(msr_content, msrs, i); |
399 | 0 | } |
400 | 0 | } |
401 | 0 | start_ibs(); |
402 | 0 | } |
403 | | |
404 | | static void stop_ibs(void) |
405 | 0 | { |
406 | 0 | if (!ibs_caps) |
407 | 0 | return; |
408 | 0 |
|
409 | 0 | if (ibs_config.fetch_enabled) |
410 | 0 | /* clear max count and enable */ |
411 | 0 | wrmsrl(MSR_AMD64_IBSFETCHCTL, 0); |
412 | 0 |
|
413 | 0 | if (ibs_config.op_enabled) |
414 | 0 | /* clear max count and enable */ |
415 | 0 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
416 | 0 | } |
417 | | |
418 | | static void athlon_stop(struct op_msrs const * const msrs) |
419 | 0 | { |
420 | 0 | uint64_t msr_content; |
421 | 0 | int i; |
422 | 0 | unsigned int const nr_ctrs = model->num_counters; |
423 | 0 |
|
424 | 0 | /* Subtle: stop on all counters to avoid race with |
425 | 0 | * setting our pm callback */ |
426 | 0 | for (i = 0 ; i < nr_ctrs ; ++i) { |
427 | 0 | CTRL_READ(msr_content, msrs, i); |
428 | 0 | CTRL_SET_INACTIVE(msr_content); |
429 | 0 | CTRL_WRITE(msr_content, msrs, i); |
430 | 0 | } |
431 | 0 |
|
432 | 0 | stop_ibs(); |
433 | 0 | } |
434 | | |
435 | 0 | #define IBSCTL_LVTOFFSETVAL (1 << 8) |
436 | 0 | #define APIC_EILVT_MSG_NMI 0x4 |
437 | 0 | #define APIC_EILVT_LVTOFF_IBS 1 |
438 | 0 | #define APIC_EILVTn(n) (0x500 + 0x10 * n) |
439 | | static inline void __init init_ibs_nmi_per_cpu(void *arg) |
440 | 0 | { |
441 | 0 | unsigned long reg; |
442 | 0 |
|
443 | 0 | reg = (APIC_EILVT_LVTOFF_IBS << 4) + APIC_EILVTn(0); |
444 | 0 | apic_write(reg, APIC_EILVT_MSG_NMI << 8); |
445 | 0 | } |
446 | | |
447 | 0 | #define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 |
448 | 0 | #define IBSCTL 0x1cc |
449 | | static int __init init_ibs_nmi(void) |
450 | 0 | { |
451 | 0 | int bus, dev, func; |
452 | 0 | u32 id, value; |
453 | 0 | u16 vendor_id, dev_id; |
454 | 0 | int nodes; |
455 | 0 |
|
456 | 0 | /* per CPU setup */ |
457 | 0 | on_each_cpu(init_ibs_nmi_per_cpu, NULL, 1); |
458 | 0 |
|
459 | 0 | nodes = 0; |
460 | 0 | for (bus = 0; bus < 256; bus++) { |
461 | 0 | for (dev = 0; dev < 32; dev++) { |
462 | 0 | for (func = 0; func < 8; func++) { |
463 | 0 | id = pci_conf_read32(0, bus, dev, func, PCI_VENDOR_ID); |
464 | 0 |
|
465 | 0 | vendor_id = id & 0xffff; |
466 | 0 | dev_id = (id >> 16) & 0xffff; |
467 | 0 |
|
468 | 0 | if ((vendor_id == PCI_VENDOR_ID_AMD) && |
469 | 0 | (dev_id == PCI_DEVICE_ID_AMD_10H_NB_MISC)) { |
470 | 0 |
|
471 | 0 | pci_conf_write32(0, bus, dev, func, IBSCTL, |
472 | 0 | IBSCTL_LVTOFFSETVAL | APIC_EILVT_LVTOFF_IBS); |
473 | 0 |
|
474 | 0 | value = pci_conf_read32(0, bus, dev, func, IBSCTL); |
475 | 0 |
|
476 | 0 | if (value != (IBSCTL_LVTOFFSETVAL | |
477 | 0 | APIC_EILVT_LVTOFF_IBS)) { |
478 | 0 | printk("Xenoprofile: Failed to setup IBS LVT offset, " |
479 | 0 | "IBSCTL = %#x\n", value); |
480 | 0 | return 1; |
481 | 0 | } |
482 | 0 | nodes++; |
483 | 0 | } |
484 | 0 | } |
485 | 0 | } |
486 | 0 | } |
487 | 0 |
|
488 | 0 | if (!nodes) { |
489 | 0 | printk("Xenoprofile: No CPU node configured for IBS\n"); |
490 | 0 | return 1; |
491 | 0 | } |
492 | 0 |
|
493 | 0 | return 0; |
494 | 0 | } |
495 | | |
496 | | static void __init get_ibs_caps(void) |
497 | 0 | { |
498 | 0 | if (!boot_cpu_has(X86_FEATURE_IBS)) |
499 | 0 | return; |
500 | 0 |
|
501 | 0 | /* check IBS cpuid feature flags */ |
502 | 0 | if (current_cpu_data.extended_cpuid_level >= IBS_CPUID_FEATURES) |
503 | 0 | ibs_caps = cpuid_eax(IBS_CPUID_FEATURES); |
504 | 0 | if (!(ibs_caps & IBS_CAPS_AVAIL)) |
505 | 0 | /* cpuid flags not valid */ |
506 | 0 | ibs_caps = 0; |
507 | 0 | } |
508 | | |
509 | | void __init ibs_init(void) |
510 | 0 | { |
511 | 0 | get_ibs_caps(); |
512 | 0 |
|
513 | 0 | if ( !ibs_caps ) |
514 | 0 | return; |
515 | 0 |
|
516 | 0 | if (init_ibs_nmi()) { |
517 | 0 | ibs_caps = 0; |
518 | 0 | return; |
519 | 0 | } |
520 | 0 |
|
521 | 0 | printk("Xenoprofile: AMD IBS detected (%#x)\n", |
522 | 0 | (unsigned)ibs_caps); |
523 | 0 | } |
524 | | |
525 | | struct op_x86_model_spec const op_athlon_spec = { |
526 | | .num_counters = K7_NUM_COUNTERS, |
527 | | .num_controls = K7_NUM_CONTROLS, |
528 | | .fill_in_addresses = &athlon_fill_in_addresses, |
529 | | .setup_ctrs = &athlon_setup_ctrs, |
530 | | .check_ctrs = &athlon_check_ctrs, |
531 | | .start = &athlon_start, |
532 | | .stop = &athlon_stop |
533 | | }; |
534 | | |
535 | | struct op_x86_model_spec const op_amd_fam15h_spec = { |
536 | | .num_counters = FAM15H_NUM_COUNTERS, |
537 | | .num_controls = FAM15H_NUM_CONTROLS, |
538 | | .fill_in_addresses = &fam15h_fill_in_addresses, |
539 | | .setup_ctrs = &athlon_setup_ctrs, |
540 | | .check_ctrs = &athlon_check_ctrs, |
541 | | .start = &athlon_start, |
542 | | .stop = &athlon_stop |
543 | | }; |