aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2009-03-13 05:45:07 -0400
committerIngo Molnar <mingo@elte.hu>2009-03-13 06:57:20 -0400
commit8a327f6d1b05f5ce16572b4413a5df1d0e872283 (patch)
tree7903a0b4725bea86dba942003d5f6048077271d0 /arch
parentbc44fb5f7d3e764ed7698c835a1a0f35aba2eb3d (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')
-rw-r--r--arch/x86/Kconfig.debug9
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/ds.c21
-rw-r--r--arch/x86/kernel/ds_selftest.c241
-rw-r--r--arch/x86/kernel/ds_selftest.h15
5 files changed, 287 insertions, 0 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index fdb45df608b6..dfd74abc03f8 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -175,6 +175,15 @@ config IOMMU_LEAK
175 Add a simple leak tracer to the IOMMU code. This is useful when you 175 Add a simple leak tracer to the IOMMU code. This is useful when you
176 are debugging a buggy device driver that leaks IOMMU mappings. 176 are debugging a buggy device driver that leaks IOMMU mappings.
177 177
178config X86_DS_SELFTEST
179 bool "DS selftest"
180 default y
181 depends on DEBUG_KERNEL
182 depends on X86_DS
183 ---help---
184 Perform Debug Store selftests at boot time.
185 If in doubt, say "N".
186
178config HAVE_MMIOTRACE_SUPPORT 187config HAVE_MMIOTRACE_SUPPORT
179 def_bool y 188 def_bool y
180 189
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 339ce35648e6..a0c9e138b008 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -44,6 +44,7 @@ obj-y += process.o
44obj-y += i387.o xsave.o 44obj-y += i387.o xsave.o
45obj-y += ptrace.o 45obj-y += ptrace.o
46obj-$(CONFIG_X86_DS) += ds.o 46obj-$(CONFIG_X86_DS) += ds.o
47obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o
47obj-$(CONFIG_X86_32) += tls.o 48obj-$(CONFIG_X86_32) += tls.o
48obj-$(CONFIG_IA32_EMULATION) += tls.o 49obj-$(CONFIG_IA32_EMULATION) += tls.o
49obj-y += step.o 50obj-y += step.o
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c
index 6e5ec679a0cd..51c936c1a390 100644
--- a/arch/x86/kernel/ds.c
+++ b/arch/x86/kernel/ds.c
@@ -29,6 +29,7 @@
29#include <linux/mm.h> 29#include <linux/mm.h>
30#include <linux/kernel.h> 30#include <linux/kernel.h>
31 31
32#include "ds_selftest.h"
32 33
33/* 34/*
34 * The configuration for a particular DS hardware implementation. 35 * The configuration for a particular DS hardware implementation.
@@ -940,6 +941,26 @@ ds_configure(const struct ds_configuration *cfg,
940 printk(KERN_INFO "[ds] pebs not available\n"); 941 printk(KERN_INFO "[ds] pebs not available\n");
941 } 942 }
942 943
944 if (ds_cfg.sizeof_rec[ds_bts]) {
945 int error;
946
947 error = ds_selftest_bts();
948 if (error) {
949 WARN(1, "[ds] selftest failed. disabling bts.\n");
950 ds_cfg.sizeof_rec[ds_bts] = 0;
951 }
952 }
953
954 if (ds_cfg.sizeof_rec[ds_pebs]) {
955 int error;
956
957 error = ds_selftest_pebs();
958 if (error) {
959 WARN(1, "[ds] selftest failed. disabling pebs.\n");
960 ds_cfg.sizeof_rec[ds_pebs] = 0;
961 }
962 }
963
943 printk(KERN_INFO "[ds] sizes: address: %u bit, ", 964 printk(KERN_INFO "[ds] sizes: address: %u bit, ",
944 8 * ds_cfg.sizeof_ptr_field); 965 8 * ds_cfg.sizeof_ptr_field);
945 printk("bts/pebs record: %u/%u bytes\n", 966 printk("bts/pebs record: %u/%u bytes\n",
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
20static 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
58static 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
122int 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
238int ds_selftest_pebs(void)
239{
240 return 0;
241}
diff --git a/arch/x86/kernel/ds_selftest.h b/arch/x86/kernel/ds_selftest.h
new file mode 100644
index 000000000000..0e6e19d4c7d2
--- /dev/null
+++ b/arch/x86/kernel/ds_selftest.h
@@ -0,0 +1,15 @@
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#ifdef CONFIG_X86_DS_SELFTEST
10extern int ds_selftest_bts(void);
11extern int ds_selftest_pebs(void);
12#else
13static inline int ds_selftest_bts(void) { return 0; }
14static inline int ds_selftest_pebs(void) { return 0; }
15#endif /* CONFIG_X86_DS_SELFTEST */