diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2012-11-28 02:33:02 -0500 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2012-12-19 00:10:22 -0500 |
commit | 00273125c39be9cbf619aef90147354a9ed8c385 (patch) | |
tree | 331f33a0a31f9306f4612ecc25c4c9a476840966 /arch/xtensa | |
parent | 28570e8dac5c86ab10ce2a7e9c02d3aaece63760 (diff) |
xtensa: add s32c1i sanity check
Add a brief sanity test of S32C1I functionality. This instruction
is needed by the kernel and userland as part of the base ABI
(including GCC atomic builtins, certain threading packages, future
atomic support in the C++ standard, etc). However, correct operation
of this instruction requires some cooperation by hardware external to
the processor (such as bus bridge, bus fabric, or memory controller).
Minimally exercising this mechanism and reporting explicit status
early in the boot process is helpful to chip vendors using the Linux
kernel as a benchmark of correctness of hardware.
As it turns out, S32C1I is not exercised by the kernel and by uClibc
based userland as of early June 2008. This is expected to change
soon as both incorporate more recent open source developments.
Signed-off-by: Marc Gauthier <marc@tensilica.com>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa')
-rw-r--r-- | arch/xtensa/Kconfig.debug | 11 | ||||
-rw-r--r-- | arch/xtensa/include/asm/regs.h | 4 | ||||
-rw-r--r-- | arch/xtensa/kernel/setup.c | 120 |
3 files changed, 135 insertions, 0 deletions
diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug index be5fb4c6243f..a34010e0e51c 100644 --- a/arch/xtensa/Kconfig.debug +++ b/arch/xtensa/Kconfig.debug | |||
@@ -13,4 +13,15 @@ config LD_NO_RELAX | |||
13 | Enabling this option improves the link time but increases the | 13 | Enabling this option improves the link time but increases the |
14 | code size, and possibly execution time. | 14 | code size, and possibly execution time. |
15 | 15 | ||
16 | config S32C1I_SELFTEST | ||
17 | bool "Perform S32C1I instruction self-test at boot" | ||
18 | default y | ||
19 | help | ||
20 | Enable this option to test S32C1I instruction behavior at boot. | ||
21 | Correct operation of this instruction requires some cooperation from hardware | ||
22 | external to the processor (such as bus bridge, bus fabric, or memory controller). | ||
23 | It is easy to make wrong hardware configuration, this test should catch it early. | ||
24 | |||
25 | Say 'N' on stable hardware. | ||
26 | |||
16 | endmenu | 27 | endmenu |
diff --git a/arch/xtensa/include/asm/regs.h b/arch/xtensa/include/asm/regs.h index 8a8aa61ccc8d..6aaf6d6a5fc4 100644 --- a/arch/xtensa/include/asm/regs.h +++ b/arch/xtensa/include/asm/regs.h | |||
@@ -52,6 +52,10 @@ | |||
52 | #define EXCCAUSE_SPECULATION 7 | 52 | #define EXCCAUSE_SPECULATION 7 |
53 | #define EXCCAUSE_PRIVILEGED 8 | 53 | #define EXCCAUSE_PRIVILEGED 8 |
54 | #define EXCCAUSE_UNALIGNED 9 | 54 | #define EXCCAUSE_UNALIGNED 9 |
55 | #define EXCCAUSE_INSTR_DATA_ERROR 12 | ||
56 | #define EXCCAUSE_LOAD_STORE_DATA_ERROR 13 | ||
57 | #define EXCCAUSE_INSTR_ADDR_ERROR 14 | ||
58 | #define EXCCAUSE_LOAD_STORE_ADDR_ERROR 15 | ||
55 | #define EXCCAUSE_ITLB_MISS 16 | 59 | #define EXCCAUSE_ITLB_MISS 16 |
56 | #define EXCCAUSE_ITLB_MULTIHIT 17 | 60 | #define EXCCAUSE_ITLB_MULTIHIT 17 |
57 | #define EXCCAUSE_ITLB_PRIVILEGE 18 | 61 | #define EXCCAUSE_ITLB_PRIVILEGE 18 |
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index e53a94b3edbc..45217617c60d 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <asm/page.h> | 42 | #include <asm/page.h> |
43 | #include <asm/setup.h> | 43 | #include <asm/setup.h> |
44 | #include <asm/param.h> | 44 | #include <asm/param.h> |
45 | #include <asm/traps.h> | ||
45 | 46 | ||
46 | #include <platform/hardware.h> | 47 | #include <platform/hardware.h> |
47 | 48 | ||
@@ -235,6 +236,123 @@ extern char _UserExceptionVector_text_end; | |||
235 | extern char _DoubleExceptionVector_literal_start; | 236 | extern char _DoubleExceptionVector_literal_start; |
236 | extern char _DoubleExceptionVector_text_end; | 237 | extern char _DoubleExceptionVector_text_end; |
237 | 238 | ||
239 | |||
240 | #ifdef CONFIG_S32C1I_SELFTEST | ||
241 | #if XCHAL_HAVE_S32C1I | ||
242 | |||
243 | static int __initdata rcw_word, rcw_probe_pc, rcw_exc; | ||
244 | |||
245 | /* | ||
246 | * Basic atomic compare-and-swap, that records PC of S32C1I for probing. | ||
247 | * | ||
248 | * If *v == cmp, set *v = set. Return previous *v. | ||
249 | */ | ||
250 | static inline int probed_compare_swap(int *v, int cmp, int set) | ||
251 | { | ||
252 | int tmp; | ||
253 | |||
254 | __asm__ __volatile__( | ||
255 | " movi %1, 1f\n" | ||
256 | " s32i %1, %4, 0\n" | ||
257 | " wsr %2, scompare1\n" | ||
258 | "1: s32c1i %0, %3, 0\n" | ||
259 | : "=a" (set), "=&a" (tmp) | ||
260 | : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set) | ||
261 | : "memory" | ||
262 | ); | ||
263 | return set; | ||
264 | } | ||
265 | |||
266 | /* Handle probed exception */ | ||
267 | |||
268 | void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause) | ||
269 | { | ||
270 | if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */ | ||
271 | regs->pc += 3; /* skip the s32c1i instruction */ | ||
272 | rcw_exc = exccause; | ||
273 | } else { | ||
274 | do_unhandled(regs, exccause); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | /* Simple test of S32C1I (soc bringup assist) */ | ||
279 | |||
280 | void __init check_s32c1i(void) | ||
281 | { | ||
282 | int n, cause1, cause2; | ||
283 | void *handbus, *handdata, *handaddr; /* temporarily saved handlers */ | ||
284 | |||
285 | rcw_probe_pc = 0; | ||
286 | handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, | ||
287 | do_probed_exception); | ||
288 | handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, | ||
289 | do_probed_exception); | ||
290 | handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, | ||
291 | do_probed_exception); | ||
292 | |||
293 | /* First try an S32C1I that does not store: */ | ||
294 | rcw_exc = 0; | ||
295 | rcw_word = 1; | ||
296 | n = probed_compare_swap(&rcw_word, 0, 2); | ||
297 | cause1 = rcw_exc; | ||
298 | |||
299 | /* took exception? */ | ||
300 | if (cause1 != 0) { | ||
301 | /* unclean exception? */ | ||
302 | if (n != 2 || rcw_word != 1) | ||
303 | panic("S32C1I exception error"); | ||
304 | } else if (rcw_word != 1 || n != 1) { | ||
305 | panic("S32C1I compare error"); | ||
306 | } | ||
307 | |||
308 | /* Then an S32C1I that stores: */ | ||
309 | rcw_exc = 0; | ||
310 | rcw_word = 0x1234567; | ||
311 | n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde); | ||
312 | cause2 = rcw_exc; | ||
313 | |||
314 | if (cause2 != 0) { | ||
315 | /* unclean exception? */ | ||
316 | if (n != 0xabcde || rcw_word != 0x1234567) | ||
317 | panic("S32C1I exception error (b)"); | ||
318 | } else if (rcw_word != 0xabcde || n != 0x1234567) { | ||
319 | panic("S32C1I store error"); | ||
320 | } | ||
321 | |||
322 | /* Verify consistency of exceptions: */ | ||
323 | if (cause1 || cause2) { | ||
324 | pr_warn("S32C1I took exception %d, %d\n", cause1, cause2); | ||
325 | /* If emulation of S32C1I upon bus error gets implemented, | ||
326 | we can get rid of this panic for single core (not SMP) */ | ||
327 | panic("S32C1I exceptions not currently supported"); | ||
328 | } | ||
329 | if (cause1 != cause2) | ||
330 | panic("inconsistent S32C1I exceptions"); | ||
331 | |||
332 | trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus); | ||
333 | trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata); | ||
334 | trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr); | ||
335 | } | ||
336 | |||
337 | #else /* XCHAL_HAVE_S32C1I */ | ||
338 | |||
339 | /* This condition should not occur with a commercially deployed processor. | ||
340 | Display reminder for early engr test or demo chips / FPGA bitstreams */ | ||
341 | void __init check_s32c1i(void) | ||
342 | { | ||
343 | pr_warn("Processor configuration lacks atomic compare-and-swap support!\n"); | ||
344 | } | ||
345 | |||
346 | #endif /* XCHAL_HAVE_S32C1I */ | ||
347 | #else /* CONFIG_S32C1I_SELFTEST */ | ||
348 | |||
349 | void __init check_s32c1i(void) | ||
350 | { | ||
351 | } | ||
352 | |||
353 | #endif /* CONFIG_S32C1I_SELFTEST */ | ||
354 | |||
355 | |||
238 | void __init setup_arch(char **cmdline_p) | 356 | void __init setup_arch(char **cmdline_p) |
239 | { | 357 | { |
240 | extern int mem_reserve(unsigned long, unsigned long, int); | 358 | extern int mem_reserve(unsigned long, unsigned long, int); |
@@ -244,6 +362,8 @@ void __init setup_arch(char **cmdline_p) | |||
244 | boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; | 362 | boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; |
245 | *cmdline_p = command_line; | 363 | *cmdline_p = command_line; |
246 | 364 | ||
365 | check_s32c1i(); | ||
366 | |||
247 | /* Reserve some memory regions */ | 367 | /* Reserve some memory regions */ |
248 | 368 | ||
249 | #ifdef CONFIG_BLK_DEV_INITRD | 369 | #ifdef CONFIG_BLK_DEV_INITRD |