diff options
author | Robin Getz <robin.getz@analog.com> | 2009-07-07 16:17:09 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2009-09-16 21:31:44 -0400 |
commit | 837ec2d56c41640d1f1238e52c350b2a516d29ba (patch) | |
tree | 1732468388385c411853c67cb2b288c2f8d17cc7 /arch | |
parent | 3f871feaf3390c6d6e578818f867917c2e4738a2 (diff) |
Blackfin: catch hardware errors earlier during booting
Allow hardware errors to be caught during early portions of booting, and
leave something in the shadow console that people can use to debug their
system with (to be printed out by the bootloader on next reset).
This enables the hardare error interrupts in head.S, allowing us to find
hardware errors when they happen (well, as much as you can with a hardware
error) and prints out the trace if it is enabled. This will catch errors
(like booting the wrong image on a 533) which previously resulted in a
infinite loop/hang, as well as random hardware errors before before
setup_arch().
To disable this debug only feature - turn off EARLY_PRINTK.
Signed-off-by: Robin Getz <robin.getz@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/blackfin/include/asm/early_printk.h | 22 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_dma_5xx.c | 6 | ||||
-rw-r--r-- | arch/blackfin/kernel/early_printk.c | 74 | ||||
-rw-r--r-- | arch/blackfin/kernel/setup.c | 3 | ||||
-rw-r--r-- | arch/blackfin/kernel/shadow_console.c | 51 | ||||
-rw-r--r-- | arch/blackfin/mach-common/head.S | 11 | ||||
-rw-r--r-- | arch/blackfin/mm/init.c | 3 |
7 files changed, 158 insertions, 12 deletions
diff --git a/arch/blackfin/include/asm/early_printk.h b/arch/blackfin/include/asm/early_printk.h index f5b6b7d972e8..53a762b6fcd2 100644 --- a/arch/blackfin/include/asm/early_printk.h +++ b/arch/blackfin/include/asm/early_printk.h | |||
@@ -21,10 +21,32 @@ | |||
21 | * GNU General Public License for more details. | 21 | * GNU General Public License for more details. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | |||
25 | #ifndef __ASM_EARLY_PRINTK_H__ | ||
26 | #define __ASM_EARLY_PRINTK_H__ | ||
27 | |||
24 | #ifdef CONFIG_EARLY_PRINTK | 28 | #ifdef CONFIG_EARLY_PRINTK |
29 | /* For those that don't include it already */ | ||
30 | #include <linux/console.h> | ||
31 | |||
25 | extern int setup_early_printk(char *); | 32 | extern int setup_early_printk(char *); |
26 | extern void enable_shadow_console(void); | 33 | extern void enable_shadow_console(void); |
34 | extern int shadow_console_enabled(void); | ||
35 | extern void mark_shadow_error(void); | ||
36 | extern void early_shadow_reg(unsigned long reg, unsigned int n); | ||
37 | extern void early_shadow_write(struct console *con, const char *s, | ||
38 | unsigned int n) __attribute__((nonnull(2))); | ||
39 | #define early_shadow_puts(str) early_shadow_write(NULL, str, strlen(str)) | ||
40 | #define early_shadow_stamp() \ | ||
41 | do { \ | ||
42 | early_shadow_puts(__FILE__ " : " __stringify(__LINE__) " ["); \ | ||
43 | early_shadow_puts(__func__); \ | ||
44 | early_shadow_puts("]\n"); \ | ||
45 | } while (0) | ||
27 | #else | 46 | #else |
28 | #define setup_early_printk(fmt) do { } while (0) | 47 | #define setup_early_printk(fmt) do { } while (0) |
29 | #define enable_shadow_console(fmt) do { } while (0) | 48 | #define enable_shadow_console(fmt) do { } while (0) |
49 | #define early_shadow_stamp() do { } while (0) | ||
30 | #endif /* CONFIG_EARLY_PRINTK */ | 50 | #endif /* CONFIG_EARLY_PRINTK */ |
51 | |||
52 | #endif /* __ASM_EARLY_PRINTK_H__ */ | ||
diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index 9f9b82816652..14dea1a87b7c 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <asm/cacheflush.h> | 19 | #include <asm/cacheflush.h> |
20 | #include <asm/dma.h> | 20 | #include <asm/dma.h> |
21 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
22 | #include <asm/early_printk.h> | ||
22 | 23 | ||
23 | /* | 24 | /* |
24 | * To make sure we work around 05000119 - we always check DMA_DONE bit, | 25 | * To make sure we work around 05000119 - we always check DMA_DONE bit, |
@@ -236,6 +237,7 @@ void blackfin_dma_resume(void) | |||
236 | */ | 237 | */ |
237 | void __init blackfin_dma_early_init(void) | 238 | void __init blackfin_dma_early_init(void) |
238 | { | 239 | { |
240 | early_shadow_stamp(); | ||
239 | bfin_write_MDMA_S0_CONFIG(0); | 241 | bfin_write_MDMA_S0_CONFIG(0); |
240 | bfin_write_MDMA_S1_CONFIG(0); | 242 | bfin_write_MDMA_S1_CONFIG(0); |
241 | } | 243 | } |
@@ -246,6 +248,8 @@ void __init early_dma_memcpy(void *pdst, const void *psrc, size_t size) | |||
246 | unsigned long src = (unsigned long)psrc; | 248 | unsigned long src = (unsigned long)psrc; |
247 | struct dma_register *dst_ch, *src_ch; | 249 | struct dma_register *dst_ch, *src_ch; |
248 | 250 | ||
251 | early_shadow_stamp(); | ||
252 | |||
249 | /* We assume that everything is 4 byte aligned, so include | 253 | /* We assume that everything is 4 byte aligned, so include |
250 | * a basic sanity check | 254 | * a basic sanity check |
251 | */ | 255 | */ |
@@ -300,6 +304,8 @@ void __init early_dma_memcpy(void *pdst, const void *psrc, size_t size) | |||
300 | 304 | ||
301 | void __init early_dma_memcpy_done(void) | 305 | void __init early_dma_memcpy_done(void) |
302 | { | 306 | { |
307 | early_shadow_stamp(); | ||
308 | |||
303 | while ((bfin_read_MDMA_S0_CONFIG() && !(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) || | 309 | while ((bfin_read_MDMA_S0_CONFIG() && !(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) || |
304 | (bfin_read_MDMA_S1_CONFIG() && !(bfin_read_MDMA_D1_IRQ_STATUS() & DMA_DONE))) | 310 | (bfin_read_MDMA_S1_CONFIG() && !(bfin_read_MDMA_D1_IRQ_STATUS() & DMA_DONE))) |
305 | continue; | 311 | continue; |
diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c index 2ab56811841c..931c78b5ea1f 100644 --- a/arch/blackfin/kernel/early_printk.c +++ b/arch/blackfin/kernel/early_printk.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/serial_core.h> | 27 | #include <linux/serial_core.h> |
28 | #include <linux/console.h> | 28 | #include <linux/console.h> |
29 | #include <linux/string.h> | 29 | #include <linux/string.h> |
30 | #include <linux/reboot.h> | ||
30 | #include <asm/blackfin.h> | 31 | #include <asm/blackfin.h> |
31 | #include <asm/irq_handler.h> | 32 | #include <asm/irq_handler.h> |
32 | #include <asm/early_printk.h> | 33 | #include <asm/early_printk.h> |
@@ -181,6 +182,22 @@ asmlinkage void __init init_early_exception_vectors(void) | |||
181 | u32 evt; | 182 | u32 evt; |
182 | SSYNC(); | 183 | SSYNC(); |
183 | 184 | ||
185 | /* | ||
186 | * This starts up the shadow buffer, incase anything crashes before | ||
187 | * setup arch | ||
188 | */ | ||
189 | mark_shadow_error(); | ||
190 | early_shadow_puts(linux_banner); | ||
191 | early_shadow_stamp(); | ||
192 | |||
193 | if (CPUID != bfin_cpuid()) { | ||
194 | early_shadow_puts("Running on wrong machine type, expected"); | ||
195 | early_shadow_reg(CPUID, 16); | ||
196 | early_shadow_puts(", but running on"); | ||
197 | early_shadow_reg(bfin_cpuid(), 16); | ||
198 | early_shadow_puts("\n"); | ||
199 | } | ||
200 | |||
184 | /* cannot program in software: | 201 | /* cannot program in software: |
185 | * evt0 - emulation (jtag) | 202 | * evt0 - emulation (jtag) |
186 | * evt1 - reset | 203 | * evt1 - reset |
@@ -199,6 +216,7 @@ asmlinkage void __init init_early_exception_vectors(void) | |||
199 | 216 | ||
200 | } | 217 | } |
201 | 218 | ||
219 | __attribute__((__noreturn__)) | ||
202 | asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr) | 220 | asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr) |
203 | { | 221 | { |
204 | /* This can happen before the uart is initialized, so initialize | 222 | /* This can happen before the uart is initialized, so initialize |
@@ -210,10 +228,58 @@ asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr) | |||
210 | if (likely(early_console == NULL) && CPUID == bfin_cpuid()) | 228 | if (likely(early_console == NULL) && CPUID == bfin_cpuid()) |
211 | setup_early_printk(DEFAULT_EARLY_PORT); | 229 | setup_early_printk(DEFAULT_EARLY_PORT); |
212 | 230 | ||
213 | printk(KERN_EMERG "Early panic\n"); | 231 | if (!shadow_console_enabled()) { |
214 | dump_bfin_mem(fp); | 232 | /* crap - we crashed before setup_arch() */ |
215 | show_regs(fp); | 233 | early_shadow_puts("panic before setup_arch\n"); |
216 | dump_bfin_trace_buffer(); | 234 | early_shadow_puts("IPEND:"); |
235 | early_shadow_reg(fp->ipend, 16); | ||
236 | if (fp->seqstat & SEQSTAT_EXCAUSE) { | ||
237 | early_shadow_puts("\nEXCAUSE:"); | ||
238 | early_shadow_reg(fp->seqstat & SEQSTAT_EXCAUSE, 8); | ||
239 | } | ||
240 | if (fp->seqstat & SEQSTAT_HWERRCAUSE) { | ||
241 | early_shadow_puts("\nHWERRCAUSE:"); | ||
242 | early_shadow_reg( | ||
243 | (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14, 8); | ||
244 | } | ||
245 | early_shadow_puts("\nErr @"); | ||
246 | if (fp->ipend & EVT_EVX) | ||
247 | early_shadow_reg(fp->retx, 32); | ||
248 | else | ||
249 | early_shadow_reg(fp->pc, 32); | ||
250 | #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON | ||
251 | early_shadow_puts("\nTrace:"); | ||
252 | if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) { | ||
253 | while (bfin_read_TBUFSTAT() & TBUFCNT) { | ||
254 | early_shadow_puts("\nT :"); | ||
255 | early_shadow_reg(bfin_read_TBUF(), 32); | ||
256 | early_shadow_puts("\n S :"); | ||
257 | early_shadow_reg(bfin_read_TBUF(), 32); | ||
258 | } | ||
259 | } | ||
260 | #endif | ||
261 | early_shadow_puts("\nUse bfin-elf-addr2line to determine " | ||
262 | "function names\n"); | ||
263 | /* | ||
264 | * We should panic(), but we can't - since panic calls printk, | ||
265 | * and printk uses memcpy. | ||
266 | * we want to reboot, but if the machine type is different, | ||
267 | * can't due to machine specific reboot sequences | ||
268 | */ | ||
269 | if (CPUID == bfin_cpuid()) { | ||
270 | early_shadow_puts("Trying to restart\n"); | ||
271 | machine_restart(""); | ||
272 | } | ||
273 | |||
274 | early_shadow_puts("Halting, since it is not safe to restart\n"); | ||
275 | while (1) | ||
276 | asm volatile ("EMUEXCPT; IDLE;\n"); | ||
277 | |||
278 | } else { | ||
279 | printk(KERN_EMERG "Early panic\n"); | ||
280 | show_regs(fp); | ||
281 | dump_bfin_trace_buffer(); | ||
282 | } | ||
217 | 283 | ||
218 | panic("Died early"); | 284 | panic("Died early"); |
219 | } | 285 | } |
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index 3974764acd2c..f92fffc31689 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c | |||
@@ -187,6 +187,8 @@ void __init bfin_relocate_l1_mem(void) | |||
187 | unsigned long l1_data_b_length; | 187 | unsigned long l1_data_b_length; |
188 | unsigned long l2_length; | 188 | unsigned long l2_length; |
189 | 189 | ||
190 | early_shadow_stamp(); | ||
191 | |||
190 | /* | 192 | /* |
191 | * due to the ALIGN(4) in the arch/blackfin/kernel/vmlinux.lds.S | 193 | * due to the ALIGN(4) in the arch/blackfin/kernel/vmlinux.lds.S |
192 | * we know that everything about l1 text/data is nice and aligned, | 194 | * we know that everything about l1 text/data is nice and aligned, |
@@ -1352,6 +1354,7 @@ const struct seq_operations cpuinfo_op = { | |||
1352 | 1354 | ||
1353 | void __init cmdline_init(const char *r0) | 1355 | void __init cmdline_init(const char *r0) |
1354 | { | 1356 | { |
1357 | early_shadow_stamp(); | ||
1355 | if (r0) | 1358 | if (r0) |
1356 | strncpy(command_line, r0, COMMAND_LINE_SIZE); | 1359 | strncpy(command_line, r0, COMMAND_LINE_SIZE); |
1357 | } | 1360 | } |
diff --git a/arch/blackfin/kernel/shadow_console.c b/arch/blackfin/kernel/shadow_console.c index 15819ff10573..8b8c7107a162 100644 --- a/arch/blackfin/kernel/shadow_console.c +++ b/arch/blackfin/kernel/shadow_console.c | |||
@@ -24,15 +24,18 @@ | |||
24 | 24 | ||
25 | static __initdata char *shadow_console_buffer = (char *)SHADOW_CONSOLE_START; | 25 | static __initdata char *shadow_console_buffer = (char *)SHADOW_CONSOLE_START; |
26 | 26 | ||
27 | static __init void early_shadow_write(struct console *con, const char *s, | 27 | __init void early_shadow_write(struct console *con, const char *s, |
28 | unsigned int n) | 28 | unsigned int n) |
29 | { | 29 | { |
30 | unsigned int i; | ||
30 | /* | 31 | /* |
31 | * save 2 bytes for the double null at the end | 32 | * save 2 bytes for the double null at the end |
32 | * once we fail on a long line, make sure we don't write a short line afterwards | 33 | * once we fail on a long line, make sure we don't write a short line afterwards |
33 | */ | 34 | */ |
34 | if ((shadow_console_buffer + n) <= (char *)(SHADOW_CONSOLE_END - 2)) { | 35 | if ((shadow_console_buffer + n) <= (char *)(SHADOW_CONSOLE_END - 2)) { |
35 | memcpy(shadow_console_buffer, s, n); | 36 | /* can't use memcpy - it may not be relocated yet */ |
37 | for (i = 0; i <= n; i++) | ||
38 | shadow_console_buffer[i] = s[i]; | ||
36 | shadow_console_buffer += n; | 39 | shadow_console_buffer += n; |
37 | shadow_console_buffer[0] = 0; | 40 | shadow_console_buffer[0] = 0; |
38 | shadow_console_buffer[1] = 0; | 41 | shadow_console_buffer[1] = 0; |
@@ -48,16 +51,24 @@ static __initdata struct console early_shadow_console = { | |||
48 | .device = 0, | 51 | .device = 0, |
49 | }; | 52 | }; |
50 | 53 | ||
51 | __init void enable_shadow_console(void) | 54 | __init int shadow_console_enabled(void) |
55 | { | ||
56 | return early_shadow_console.flags & CON_ENABLED; | ||
57 | } | ||
58 | |||
59 | __init void mark_shadow_error(void) | ||
52 | { | 60 | { |
53 | int *loc = (int *)SHADOW_CONSOLE_MAGIC_LOC; | 61 | int *loc = (int *)SHADOW_CONSOLE_MAGIC_LOC; |
62 | loc[0] = SHADOW_CONSOLE_MAGIC; | ||
63 | loc[1] = SHADOW_CONSOLE_START; | ||
64 | } | ||
54 | 65 | ||
55 | if (!(early_shadow_console.flags & CON_ENABLED)) { | 66 | __init void enable_shadow_console(void) |
67 | { | ||
68 | if (!shadow_console_enabled()) { | ||
56 | register_console(&early_shadow_console); | 69 | register_console(&early_shadow_console); |
57 | /* for now, assume things are going to fail */ | 70 | /* for now, assume things are going to fail */ |
58 | *loc = SHADOW_CONSOLE_MAGIC; | 71 | mark_shadow_error(); |
59 | loc++; | ||
60 | *loc = SHADOW_CONSOLE_START; | ||
61 | } | 72 | } |
62 | } | 73 | } |
63 | 74 | ||
@@ -71,8 +82,32 @@ static __init int disable_shadow_console(void) | |||
71 | */ | 82 | */ |
72 | int *loc = (int *)SHADOW_CONSOLE_MAGIC_LOC; | 83 | int *loc = (int *)SHADOW_CONSOLE_MAGIC_LOC; |
73 | 84 | ||
74 | *loc = 0; | 85 | loc[0] = 0; |
75 | 86 | ||
76 | return 0; | 87 | return 0; |
77 | } | 88 | } |
78 | pure_initcall(disable_shadow_console); | 89 | pure_initcall(disable_shadow_console); |
90 | |||
91 | /* | ||
92 | * since we can't use printk, dump numbers (as hex), n = # bits | ||
93 | */ | ||
94 | __init void early_shadow_reg(unsigned long reg, unsigned int n) | ||
95 | { | ||
96 | /* | ||
97 | * can't use any "normal" kernel features, since thay | ||
98 | * may not be relocated to their execute address yet | ||
99 | */ | ||
100 | int i; | ||
101 | char ascii[11] = " 0x"; | ||
102 | |||
103 | n = n / 4; | ||
104 | reg = reg << ((8 - n) * 4); | ||
105 | n += 3; | ||
106 | |||
107 | for (i = 3; i <= n ; i++) { | ||
108 | ascii[i] = hex_asc_lo(reg >> 28); | ||
109 | reg <<= 4; | ||
110 | } | ||
111 | early_shadow_write(NULL, ascii, n); | ||
112 | |||
113 | } | ||
diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S index f826f6b9f917..66910121fa6f 100644 --- a/arch/blackfin/mach-common/head.S +++ b/arch/blackfin/mach-common/head.S | |||
@@ -153,6 +153,8 @@ ENTRY(__start) | |||
153 | 153 | ||
154 | #ifdef CONFIG_EARLY_PRINTK | 154 | #ifdef CONFIG_EARLY_PRINTK |
155 | call _init_early_exception_vectors; | 155 | call _init_early_exception_vectors; |
156 | r0 = (EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); | ||
157 | sti r0; | ||
156 | #endif | 158 | #endif |
157 | 159 | ||
158 | r0 = 0 (x); | 160 | r0 = 0 (x); |
@@ -212,12 +214,21 @@ ENTRY(__start) | |||
212 | [p0] = p1; | 214 | [p0] = p1; |
213 | csync; | 215 | csync; |
214 | 216 | ||
217 | #ifdef CONFIG_EARLY_PRINTK | ||
218 | r0 = (EVT_IVG15 | EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU) (z); | ||
219 | #else | ||
215 | r0 = EVT_IVG15 (z); | 220 | r0 = EVT_IVG15 (z); |
221 | #endif | ||
216 | sti r0; | 222 | sti r0; |
217 | 223 | ||
218 | raise 15; | 224 | raise 15; |
225 | #ifdef CONFIG_EARLY_PRINTK | ||
226 | p0.l = _early_trap; | ||
227 | p0.h = _early_trap; | ||
228 | #else | ||
219 | p0.l = .LWAIT_HERE; | 229 | p0.l = .LWAIT_HERE; |
220 | p0.h = .LWAIT_HERE; | 230 | p0.h = .LWAIT_HERE; |
231 | #endif | ||
221 | reti = p0; | 232 | reti = p0; |
222 | #if ANOMALY_05000281 | 233 | #if ANOMALY_05000281 |
223 | nop; nop; nop; | 234 | nop; nop; nop; |
diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index 68bd0bd680cd..b88ce7fda548 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <asm/bfin-global.h> | 33 | #include <asm/bfin-global.h> |
34 | #include <asm/pda.h> | 34 | #include <asm/pda.h> |
35 | #include <asm/cplbinit.h> | 35 | #include <asm/cplbinit.h> |
36 | #include <asm/early_printk.h> | ||
36 | #include "blackfin_sram.h" | 37 | #include "blackfin_sram.h" |
37 | 38 | ||
38 | /* | 39 | /* |
@@ -113,6 +114,8 @@ asmlinkage void __init init_pda(void) | |||
113 | { | 114 | { |
114 | unsigned int cpu = raw_smp_processor_id(); | 115 | unsigned int cpu = raw_smp_processor_id(); |
115 | 116 | ||
117 | early_shadow_stamp(); | ||
118 | |||
116 | /* Initialize the PDA fields holding references to other parts | 119 | /* Initialize the PDA fields holding references to other parts |
117 | of the memory. The content of such memory is still | 120 | of the memory. The content of such memory is still |
118 | undefined at the time of the call, we are only setting up | 121 | undefined at the time of the call, we are only setting up |