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