x86/amd-iommu: Workaround for erratum 63
[linux-2.6/verdex.git] / arch / x86 / kernel / ds_selftest.c
blob6bc7c199ab99e177a52a1bbbb85047a8083f9600
1 /*
2 * Debug Store support - selftest
5 * Copyright (C) 2009 Intel Corporation.
6 * Markus Metzger <markus.t.metzger@intel.com>, 2009
7 */
9 #include "ds_selftest.h"
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13 #include <linux/smp.h>
14 #include <linux/cpu.h>
16 #include <asm/ds.h>
19 #define BUFFER_SIZE 521 /* Intentionally chose an odd size. */
20 #define SMALL_BUFFER_SIZE 24 /* A single bts entry. */
22 struct ds_selftest_bts_conf {
23 struct bts_tracer *tracer;
24 int error;
25 int (*suspend)(struct bts_tracer *);
26 int (*resume)(struct bts_tracer *);
29 static int ds_selftest_bts_consistency(const struct bts_trace *trace)
31 int error = 0;
33 if (!trace) {
34 printk(KERN_CONT "failed to access trace...");
35 /* Bail out. Other tests are pointless. */
36 return -1;
39 if (!trace->read) {
40 printk(KERN_CONT "bts read not available...");
41 error = -1;
44 /* Do some sanity checks on the trace configuration. */
45 if (!trace->ds.n) {
46 printk(KERN_CONT "empty bts buffer...");
47 error = -1;
49 if (!trace->ds.size) {
50 printk(KERN_CONT "bad bts trace setup...");
51 error = -1;
53 if (trace->ds.end !=
54 (char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) {
55 printk(KERN_CONT "bad bts buffer setup...");
56 error = -1;
59 * We allow top in [begin; end], since its not clear when the
60 * overflow adjustment happens: after the increment or before the
61 * write.
63 if ((trace->ds.top < trace->ds.begin) ||
64 (trace->ds.end < trace->ds.top)) {
65 printk(KERN_CONT "bts top out of bounds...");
66 error = -1;
69 return error;
72 static int ds_selftest_bts_read(struct bts_tracer *tracer,
73 const struct bts_trace *trace,
74 const void *from, const void *to)
76 const unsigned char *at;
79 * Check a few things which do not belong to this test.
80 * They should be covered by other tests.
82 if (!trace)
83 return -1;
85 if (!trace->read)
86 return -1;
88 if (to < from)
89 return -1;
91 if (from < trace->ds.begin)
92 return -1;
94 if (trace->ds.end < to)
95 return -1;
97 if (!trace->ds.size)
98 return -1;
100 /* Now to the test itself. */
101 for (at = from; (void *)at < to; at += trace->ds.size) {
102 struct bts_struct bts;
103 unsigned long index;
104 int error;
106 if (((void *)at - trace->ds.begin) % trace->ds.size) {
107 printk(KERN_CONT
108 "read from non-integer index...");
109 return -1;
111 index = ((void *)at - trace->ds.begin) / trace->ds.size;
113 memset(&bts, 0, sizeof(bts));
114 error = trace->read(tracer, at, &bts);
115 if (error < 0) {
116 printk(KERN_CONT
117 "error reading bts trace at [%lu] (0x%p)...",
118 index, at);
119 return error;
122 switch (bts.qualifier) {
123 case BTS_BRANCH:
124 break;
125 default:
126 printk(KERN_CONT
127 "unexpected bts entry %llu at [%lu] (0x%p)...",
128 bts.qualifier, index, at);
129 return -1;
133 return 0;
136 static void ds_selftest_bts_cpu(void *arg)
138 struct ds_selftest_bts_conf *conf = arg;
139 const struct bts_trace *trace;
140 void *top;
142 if (IS_ERR(conf->tracer)) {
143 conf->error = PTR_ERR(conf->tracer);
144 conf->tracer = NULL;
146 printk(KERN_CONT
147 "initialization failed (err: %d)...", conf->error);
148 return;
151 /* We should meanwhile have enough trace. */
152 conf->error = conf->suspend(conf->tracer);
153 if (conf->error < 0)
154 return;
156 /* Let's see if we can access the trace. */
157 trace = ds_read_bts(conf->tracer);
159 conf->error = ds_selftest_bts_consistency(trace);
160 if (conf->error < 0)
161 return;
163 /* If everything went well, we should have a few trace entries. */
164 if (trace->ds.top == trace->ds.begin) {
166 * It is possible but highly unlikely that we got a
167 * buffer overflow and end up at exactly the same
168 * position we started from.
169 * Let's issue a warning, but continue.
171 printk(KERN_CONT "no trace/overflow...");
174 /* Let's try to read the trace we collected. */
175 conf->error =
176 ds_selftest_bts_read(conf->tracer, trace,
177 trace->ds.begin, trace->ds.top);
178 if (conf->error < 0)
179 return;
182 * Let's read the trace again.
183 * Since we suspended tracing, we should get the same result.
185 top = trace->ds.top;
187 trace = ds_read_bts(conf->tracer);
188 conf->error = ds_selftest_bts_consistency(trace);
189 if (conf->error < 0)
190 return;
192 if (top != trace->ds.top) {
193 printk(KERN_CONT "suspend not working...");
194 conf->error = -1;
195 return;
198 /* Let's collect some more trace - see if resume is working. */
199 conf->error = conf->resume(conf->tracer);
200 if (conf->error < 0)
201 return;
203 conf->error = conf->suspend(conf->tracer);
204 if (conf->error < 0)
205 return;
207 trace = ds_read_bts(conf->tracer);
209 conf->error = ds_selftest_bts_consistency(trace);
210 if (conf->error < 0)
211 return;
213 if (trace->ds.top == top) {
215 * It is possible but highly unlikely that we got a
216 * buffer overflow and end up at exactly the same
217 * position we started from.
218 * Let's issue a warning and check the full trace.
220 printk(KERN_CONT
221 "no resume progress/overflow...");
223 conf->error =
224 ds_selftest_bts_read(conf->tracer, trace,
225 trace->ds.begin, trace->ds.end);
226 } else if (trace->ds.top < top) {
228 * We had a buffer overflow - the entire buffer should
229 * contain trace records.
231 conf->error =
232 ds_selftest_bts_read(conf->tracer, trace,
233 trace->ds.begin, trace->ds.end);
234 } else {
236 * It is quite likely that the buffer did not overflow.
237 * Let's just check the delta trace.
239 conf->error =
240 ds_selftest_bts_read(conf->tracer, trace, top,
241 trace->ds.top);
243 if (conf->error < 0)
244 return;
246 conf->error = 0;
249 static int ds_suspend_bts_wrap(struct bts_tracer *tracer)
251 ds_suspend_bts(tracer);
252 return 0;
255 static int ds_resume_bts_wrap(struct bts_tracer *tracer)
257 ds_resume_bts(tracer);
258 return 0;
261 static void ds_release_bts_noirq_wrap(void *tracer)
263 (void)ds_release_bts_noirq(tracer);
266 static int ds_selftest_bts_bad_release_noirq(int cpu,
267 struct bts_tracer *tracer)
269 int error = -EPERM;
271 /* Try to release the tracer on the wrong cpu. */
272 get_cpu();
273 if (cpu != smp_processor_id()) {
274 error = ds_release_bts_noirq(tracer);
275 if (error != -EPERM)
276 printk(KERN_CONT "release on wrong cpu...");
278 put_cpu();
280 return error ? 0 : -1;
283 static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer)
285 struct bts_tracer *tracer;
286 int error;
288 /* Try to request cpu tracing while task tracing is active. */
289 tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL,
290 (size_t)-1, BTS_KERNEL);
291 error = PTR_ERR(tracer);
292 if (!IS_ERR(tracer)) {
293 ds_release_bts(tracer);
294 error = 0;
297 if (error != -EPERM)
298 printk(KERN_CONT "cpu/task tracing overlap...");
300 return error ? 0 : -1;
303 static int ds_selftest_bts_bad_request_task(void *buffer)
305 struct bts_tracer *tracer;
306 int error;
308 /* Try to request cpu tracing while task tracing is active. */
309 tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL,
310 (size_t)-1, BTS_KERNEL);
311 error = PTR_ERR(tracer);
312 if (!IS_ERR(tracer)) {
313 error = 0;
314 ds_release_bts(tracer);
317 if (error != -EPERM)
318 printk(KERN_CONT "task/cpu tracing overlap...");
320 return error ? 0 : -1;
323 int ds_selftest_bts(void)
325 struct ds_selftest_bts_conf conf;
326 unsigned char buffer[BUFFER_SIZE], *small_buffer;
327 unsigned long irq;
328 int cpu;
330 printk(KERN_INFO "[ds] bts selftest...");
331 conf.error = 0;
333 small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8;
335 get_online_cpus();
336 for_each_online_cpu(cpu) {
337 conf.suspend = ds_suspend_bts_wrap;
338 conf.resume = ds_resume_bts_wrap;
339 conf.tracer =
340 ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
341 NULL, (size_t)-1, BTS_KERNEL);
342 ds_selftest_bts_cpu(&conf);
343 if (conf.error >= 0)
344 conf.error = ds_selftest_bts_bad_request_task(buffer);
345 ds_release_bts(conf.tracer);
346 if (conf.error < 0)
347 goto out;
349 conf.suspend = ds_suspend_bts_noirq;
350 conf.resume = ds_resume_bts_noirq;
351 conf.tracer =
352 ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
353 NULL, (size_t)-1, BTS_KERNEL);
354 smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1);
355 if (conf.error >= 0) {
356 conf.error =
357 ds_selftest_bts_bad_release_noirq(cpu,
358 conf.tracer);
359 /* We must not release the tracer twice. */
360 if (conf.error < 0)
361 conf.tracer = NULL;
363 if (conf.error >= 0)
364 conf.error = ds_selftest_bts_bad_request_task(buffer);
365 smp_call_function_single(cpu, ds_release_bts_noirq_wrap,
366 conf.tracer, 1);
367 if (conf.error < 0)
368 goto out;
371 conf.suspend = ds_suspend_bts_wrap;
372 conf.resume = ds_resume_bts_wrap;
373 conf.tracer =
374 ds_request_bts_task(current, buffer, BUFFER_SIZE,
375 NULL, (size_t)-1, BTS_KERNEL);
376 ds_selftest_bts_cpu(&conf);
377 if (conf.error >= 0)
378 conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
379 ds_release_bts(conf.tracer);
380 if (conf.error < 0)
381 goto out;
383 conf.suspend = ds_suspend_bts_noirq;
384 conf.resume = ds_resume_bts_noirq;
385 conf.tracer =
386 ds_request_bts_task(current, small_buffer, SMALL_BUFFER_SIZE,
387 NULL, (size_t)-1, BTS_KERNEL);
388 local_irq_save(irq);
389 ds_selftest_bts_cpu(&conf);
390 if (conf.error >= 0)
391 conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
392 ds_release_bts_noirq(conf.tracer);
393 local_irq_restore(irq);
394 if (conf.error < 0)
395 goto out;
397 conf.error = 0;
398 out:
399 put_online_cpus();
400 printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed"));
402 return conf.error;
405 int ds_selftest_pebs(void)
407 return 0;