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 | |
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')
-rw-r--r-- | arch/x86/Kconfig.debug | 9 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/ds.c | 21 | ||||
-rw-r--r-- | arch/x86/kernel/ds_selftest.c | 241 | ||||
-rw-r--r-- | arch/x86/kernel/ds_selftest.h | 15 |
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 | ||
178 | config 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 | |||
178 | config HAVE_MMIOTRACE_SUPPORT | 187 | config 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 | |||
44 | obj-y += i387.o xsave.o | 44 | obj-y += i387.o xsave.o |
45 | obj-y += ptrace.o | 45 | obj-y += ptrace.o |
46 | obj-$(CONFIG_X86_DS) += ds.o | 46 | obj-$(CONFIG_X86_DS) += ds.o |
47 | obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o | ||
47 | obj-$(CONFIG_X86_32) += tls.o | 48 | obj-$(CONFIG_X86_32) += tls.o |
48 | obj-$(CONFIG_IA32_EMULATION) += tls.o | 49 | obj-$(CONFIG_IA32_EMULATION) += tls.o |
49 | obj-y += step.o | 50 | obj-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 | |||
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 | } | ||
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 | ||
10 | extern int ds_selftest_bts(void); | ||
11 | extern int ds_selftest_pebs(void); | ||
12 | #else | ||
13 | static inline int ds_selftest_bts(void) { return 0; } | ||
14 | static inline int ds_selftest_pebs(void) { return 0; } | ||
15 | #endif /* CONFIG_X86_DS_SELFTEST */ | ||