aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel
diff options
context:
space:
mode:
authorRobin Getz <robin.getz@analog.com>2009-07-07 16:17:09 -0400
committerMike Frysinger <vapier@gentoo.org>2009-09-16 21:31:44 -0400
commit837ec2d56c41640d1f1238e52c350b2a516d29ba (patch)
tree1732468388385c411853c67cb2b288c2f8d17cc7 /arch/blackfin/kernel
parent3f871feaf3390c6d6e578818f867917c2e4738a2 (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/blackfin/kernel')
-rw-r--r--arch/blackfin/kernel/bfin_dma_5xx.c6
-rw-r--r--arch/blackfin/kernel/early_printk.c74
-rw-r--r--arch/blackfin/kernel/setup.c3
-rw-r--r--arch/blackfin/kernel/shadow_console.c51
4 files changed, 122 insertions, 12 deletions
diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c
index 9f9b8281665..14dea1a87b7 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 */
237void __init blackfin_dma_early_init(void) 238void __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
301void __init early_dma_memcpy_done(void) 305void __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 2ab56811841..931c78b5ea1 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__))
202asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr) 220asmlinkage 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 3974764acd2..f92fffc3168 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
1353void __init cmdline_init(const char *r0) 1355void __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 15819ff1057..8b8c7107a16 100644
--- a/arch/blackfin/kernel/shadow_console.c
+++ b/arch/blackfin/kernel/shadow_console.c
@@ -24,15 +24,18 @@
24 24
25static __initdata char *shadow_console_buffer = (char *)SHADOW_CONSOLE_START; 25static __initdata char *shadow_console_buffer = (char *)SHADOW_CONSOLE_START;
26 26
27static __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}
78pure_initcall(disable_shadow_console); 89pure_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}