Merge branch 'linus' into tracing/hw-branch-tracing
[pandora-kernel.git] / arch / x86 / kernel / ds_selftest.c
1 /*
2  * Debug Store support - selftest
3  *
4  *
5  * Copyright (C) 2009 Intel Corporation.
6  * Markus Metzger <markus.t.metzger@intel.com>, 2009
7  */
8
9 #include "ds_selftest.h"
10
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13
14 #include <asm/ds.h>
15
16
17 #define DS_SELFTEST_BUFFER_SIZE 1021 /* Intentionally chose an odd size. */
18
19
20 static int ds_selftest_bts_consistency(const struct bts_trace *trace)
21 {
22         int error = 0;
23
24         if (!trace) {
25                 printk(KERN_CONT "failed to access trace...");
26                 /* Bail out. Other tests are pointless. */
27                 return -1;
28         }
29
30         if (!trace->read) {
31                 printk(KERN_CONT "bts read not available...");
32                 error = -1;
33         }
34
35         /* Do some sanity checks on the trace configuration. */
36         if (!trace->ds.n) {
37                 printk(KERN_CONT "empty bts buffer...");
38                 error = -1;
39         }
40         if (!trace->ds.size) {
41                 printk(KERN_CONT "bad bts trace setup...");
42                 error = -1;
43         }
44         if (trace->ds.end !=
45             (char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) {
46                 printk(KERN_CONT "bad bts buffer setup...");
47                 error = -1;
48         }
49         if ((trace->ds.top < trace->ds.begin) ||
50             (trace->ds.end <= trace->ds.top)) {
51                 printk(KERN_CONT "bts top out of bounds...");
52                 error = -1;
53         }
54
55         return error;
56 }
57
58 static int ds_selftest_bts_read(struct bts_tracer *tracer,
59                                 const struct bts_trace *trace,
60                                 const void *from, const void *to)
61 {
62         const unsigned char *at;
63
64         /*
65          * Check a few things which do not belong to this test.
66          * They should be covered by other tests.
67          */
68         if (!trace)
69                 return -1;
70
71         if (!trace->read)
72                 return -1;
73
74         if (to < from)
75                 return -1;
76
77         if (from < trace->ds.begin)
78                 return -1;
79
80         if (trace->ds.end < to)
81                 return -1;
82
83         if (!trace->ds.size)
84                 return -1;
85
86         /* Now to the test itself. */
87         for (at = from; (void *)at < to; at += trace->ds.size) {
88                 struct bts_struct bts;
89                 size_t index;
90                 int error;
91
92                 if (((void *)at - trace->ds.begin) % trace->ds.size) {
93                         printk(KERN_CONT
94                                "read from non-integer index...");
95                         return -1;
96                 }
97                 index = ((void *)at - trace->ds.begin) / trace->ds.size;
98
99                 memset(&bts, 0, sizeof(bts));
100                 error = trace->read(tracer, at, &bts);
101                 if (error < 0) {
102                         printk(KERN_CONT
103                                "error reading bts trace at [%lu] (0x%p)...",
104                                index, at);
105                         return error;
106                 }
107
108                 switch (bts.qualifier) {
109                 case BTS_BRANCH:
110                         break;
111                 default:
112                         printk(KERN_CONT
113                                "unexpected bts entry %llu at [%lu] (0x%p)...",
114                                bts.qualifier, index, at);
115                         return -1;
116                 }
117         }
118
119         return 0;
120 }
121
122 int ds_selftest_bts(void)
123 {
124         const struct bts_trace *trace;
125         struct bts_tracer *tracer;
126         int error = 0;
127         void *top;
128         unsigned char buffer[DS_SELFTEST_BUFFER_SIZE];
129
130         printk(KERN_INFO "[ds] bts selftest...");
131
132         tracer = ds_request_bts(NULL, buffer, DS_SELFTEST_BUFFER_SIZE,
133                                 NULL, (size_t)-1, BTS_KERNEL);
134         if (IS_ERR(tracer)) {
135                 error = PTR_ERR(tracer);
136                 tracer = NULL;
137
138                 printk(KERN_CONT
139                        "initialization failed (err: %d)...", error);
140                 goto out;
141         }
142
143         /* The return should already give us enough trace. */
144         ds_suspend_bts(tracer);
145
146         /* Let's see if we can access the trace. */
147         trace = ds_read_bts(tracer);
148
149         error = ds_selftest_bts_consistency(trace);
150         if (error < 0)
151                 goto out;
152
153         /* If everything went well, we should have a few trace entries. */
154         if (trace->ds.top == trace->ds.begin) {
155                 /*
156                  * It is possible but highly unlikely that we got a
157                  * buffer overflow and end up at exactly the same
158                  * position we started from.
159                  * Let's issue a warning, but continue.
160                  */
161                 printk(KERN_CONT "no trace/overflow...");
162         }
163
164         /* Let's try to read the trace we collected. */
165         error = ds_selftest_bts_read(tracer, trace,
166                                      trace->ds.begin, trace->ds.top);
167         if (error < 0)
168                 goto out;
169
170         /*
171          * Let's read the trace again.
172          * Since we suspended tracing, we should get the same result.
173          */
174         top = trace->ds.top;
175
176         trace = ds_read_bts(tracer);
177         error = ds_selftest_bts_consistency(trace);
178         if (error < 0)
179                 goto out;
180
181         if (top != trace->ds.top) {
182                 printk(KERN_CONT "suspend not working...");
183                 error = -1;
184                 goto out;
185         }
186
187         /* Let's collect some more trace - see if resume is working. */
188         ds_resume_bts(tracer);
189         ds_suspend_bts(tracer);
190
191         trace = ds_read_bts(tracer);
192
193         error = ds_selftest_bts_consistency(trace);
194         if (error < 0)
195                 goto out;
196
197         if (trace->ds.top == top) {
198                 /*
199                  * It is possible but highly unlikely that we got a
200                  * buffer overflow and end up at exactly the same
201                  * position we started from.
202                  * Let's issue a warning and check the full trace.
203                  */
204                 printk(KERN_CONT
205                        "no resume progress/overflow...");
206
207                 error = ds_selftest_bts_read(tracer, trace,
208                                              trace->ds.begin, trace->ds.end);
209         } else if (trace->ds.top < top) {
210                 /*
211                  * We had a buffer overflow - the entire buffer should
212                  * contain trace records.
213                  */
214                 error = ds_selftest_bts_read(tracer, trace,
215                                              trace->ds.begin, trace->ds.end);
216         } else {
217                 /*
218                  * It is quite likely that the buffer did not overflow.
219                  * Let's just check the delta trace.
220                  */
221                 error = ds_selftest_bts_read(tracer, trace,
222                                              top, trace->ds.top);
223         }
224         if (error < 0)
225                 goto out;
226
227         error = 0;
228
229         /* The final test: release the tracer while tracing is suspended. */
230  out:
231         ds_release_bts(tracer);
232
233         printk(KERN_CONT "%s.\n", (error ? "failed" : "passed"));
234
235         return error;
236 }
237
238 int ds_selftest_pebs(void)
239 {
240         return 0;
241 }