diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2009-03-13 05:45:07 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-03-13 06:57:20 -0400 |
commit | 8a327f6d1b05f5ce16572b4413a5df1d0e872283 (patch) | |
tree | 7903a0b4725bea86dba942003d5f6048077271d0 /arch/x86/kernel/ds_selftest.c | |
parent | bc44fb5f7d3e764ed7698c835a1a0f35aba2eb3d (diff) |
x86, bts: add selftest for BTS
Perform a selftest of branch trace store when a cpu is initialized.
WARN and disable branch trace store support if the selftest fails.
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
LKML-Reference: <20090313104507.A30125@sedona.ch.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/ds_selftest.c')
-rw-r--r-- | arch/x86/kernel/ds_selftest.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/arch/x86/kernel/ds_selftest.c b/arch/x86/kernel/ds_selftest.c new file mode 100644 index 000000000000..8c46fbf38c46 --- /dev/null +++ b/arch/x86/kernel/ds_selftest.c | |||
@@ -0,0 +1,241 @@ | |||
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 | } | ||