]>
Commit | Line | Data |
---|---|---|
affae2bf | 1 | /* |
a47a12be | 2 | * linux/arch/powerpc/kernel/traps.c |
affae2bf WD |
3 | * |
4 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) | |
5 | * | |
6 | * Modified by Cort Dougan (cort@cs.nmt.edu) | |
7 | * and Paul Mackerras (paulus@cs.anu.edu.au) | |
8 | * | |
9 | * (C) Copyright 2000 | |
10 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
11 | * | |
1a459660 | 12 | * SPDX-License-Identifier: GPL-2.0+ |
affae2bf WD |
13 | */ |
14 | ||
15 | /* | |
16 | * This file handles the architecture-dependent parts of hardware exceptions | |
17 | */ | |
18 | ||
19 | #include <common.h> | |
20 | #include <command.h> | |
6f6c26e4 | 21 | #include <kgdb.h> |
affae2bf WD |
22 | #include <asm/processor.h> |
23 | ||
efa35cf1 GB |
24 | DECLARE_GLOBAL_DATA_PTR; |
25 | ||
affae2bf WD |
26 | /* Returns 0 if exception not found and fixup otherwise. */ |
27 | extern unsigned long search_exception_table(unsigned long); | |
28 | ||
29 | /* THIS NEEDS CHANGING to use the board info structure. | |
30 | */ | |
efa35cf1 | 31 | #define END_OF_MEM (gd->bd->bi_memstart + gd->bd->bi_memsize) |
affae2bf | 32 | |
affae2bf WD |
33 | static __inline__ unsigned long get_esr(void) |
34 | { | |
35 | unsigned long val; | |
36 | ||
37 | #if defined(CONFIG_440) | |
38 | asm volatile("mfspr %0, 0x03e" : "=r" (val) :); | |
39 | #else | |
40 | asm volatile("mfesr %0" : "=r" (val) :); | |
41 | #endif | |
42 | return val; | |
43 | } | |
44 | ||
45 | #define ESR_MCI 0x80000000 | |
46 | #define ESR_PIL 0x08000000 | |
47 | #define ESR_PPR 0x04000000 | |
48 | #define ESR_PTR 0x02000000 | |
49 | #define ESR_DST 0x00800000 | |
50 | #define ESR_DIZ 0x00400000 | |
51 | #define ESR_U0F 0x00008000 | |
52 | ||
3a1ed1e1 | 53 | #if defined(CONFIG_CMD_BEDBUG) |
affae2bf WD |
54 | extern void do_bedbug_breakpoint(struct pt_regs *); |
55 | #endif | |
56 | ||
57 | /* | |
58 | * Trap & Exception support | |
59 | */ | |
60 | ||
20051f2a | 61 | static void print_backtrace(unsigned long *sp) |
affae2bf | 62 | { |
83b4cfa3 WD |
63 | int cnt = 0; |
64 | unsigned long i; | |
65 | ||
66 | printf("Call backtrace: "); | |
67 | while (sp) { | |
68 | if ((uint)sp > END_OF_MEM) | |
69 | break; | |
70 | ||
71 | i = sp[1]; | |
72 | if (cnt++ % 7 == 0) | |
73 | printf("\n"); | |
74 | printf("%08lX ", i); | |
75 | if (cnt > 32) break; | |
76 | sp = (unsigned long *)*sp; | |
77 | } | |
78 | printf("\n"); | |
affae2bf WD |
79 | } |
80 | ||
20051f2a | 81 | void show_regs(struct pt_regs *regs) |
affae2bf WD |
82 | { |
83 | int i; | |
84 | ||
efa35cf1 | 85 | printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DEAR: %08lX\n", |
affae2bf WD |
86 | regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); |
87 | printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", | |
88 | regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, | |
89 | regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, | |
90 | regs->msr&MSR_IR ? 1 : 0, | |
91 | regs->msr&MSR_DR ? 1 : 0); | |
92 | ||
93 | printf("\n"); | |
94 | for (i = 0; i < 32; i++) { | |
83b4cfa3 | 95 | if ((i % 8) == 0) { |
affae2bf WD |
96 | printf("GPR%02d: ", i); |
97 | } | |
98 | ||
99 | printf("%08lX ", regs->gpr[i]); | |
83b4cfa3 | 100 | if ((i % 8) == 7) { |
affae2bf WD |
101 | printf("\n"); |
102 | } | |
103 | } | |
104 | } | |
105 | ||
106 | ||
20051f2a | 107 | static void _exception(int signr, struct pt_regs *regs) |
affae2bf WD |
108 | { |
109 | show_regs(regs); | |
110 | print_backtrace((unsigned long *)regs->gpr[1]); | |
efa35cf1 | 111 | panic("Exception"); |
affae2bf WD |
112 | } |
113 | ||
20051f2a | 114 | void MachineCheckException(struct pt_regs *regs) |
affae2bf | 115 | { |
efa35cf1 | 116 | unsigned long fixup, val; |
a1bd6200 NG |
117 | #if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) |
118 | u32 value2; | |
27a528fb SR |
119 | int corr_ecc = 0; |
120 | int uncorr_ecc = 0; | |
a1bd6200 | 121 | #endif |
83b4cfa3 | 122 | |
affae2bf WD |
123 | if ((fixup = search_exception_table(regs->nip)) != 0) { |
124 | regs->nip = fixup; | |
c9240981 GB |
125 | val = mfspr(MCSR); |
126 | /* Clear MCSR */ | |
9ca8d79d | 127 | mtspr(SPRN_MCSR, val); |
affae2bf WD |
128 | return; |
129 | } | |
130 | ||
3a1ed1e1 | 131 | #if defined(CONFIG_CMD_KGDB) |
affae2bf WD |
132 | if (debugger_exception_handler && (*debugger_exception_handler)(regs)) |
133 | return; | |
134 | #endif | |
135 | ||
efa35cf1 | 136 | printf("Machine Check Exception.\n"); |
affae2bf | 137 | printf("Caused by (from msr): "); |
efa35cf1 GB |
138 | printf("regs %p ", regs); |
139 | ||
140 | val = get_esr(); | |
141 | ||
c821b5f1 | 142 | #if !defined(CONFIG_440) && !defined(CONFIG_405EX) |
efa35cf1 GB |
143 | if (val& ESR_IMCP) { |
144 | printf("Instruction"); | |
145 | mtspr(ESR, val & ~ESR_IMCP); | |
83b4cfa3 | 146 | } else { |
efa35cf1 | 147 | printf("Data"); |
83b4cfa3 | 148 | } |
efa35cf1 GB |
149 | printf(" machine check.\n"); |
150 | ||
c821b5f1 | 151 | #elif defined(CONFIG_440) || defined(CONFIG_405EX) |
efa35cf1 GB |
152 | if (val& ESR_IMCP){ |
153 | printf("Instruction Synchronous Machine Check exception\n"); | |
154 | mtspr(SPRN_ESR, val & ~ESR_IMCP); | |
83b4cfa3 | 155 | } else { |
efa35cf1 GB |
156 | val = mfspr(MCSR); |
157 | if (val & MCSR_IB) | |
158 | printf("Instruction Read PLB Error\n"); | |
c821b5f1 | 159 | #if defined(CONFIG_440) |
efa35cf1 GB |
160 | if (val & MCSR_DRB) |
161 | printf("Data Read PLB Error\n"); | |
162 | if (val & MCSR_DWB) | |
163 | printf("Data Write PLB Error\n"); | |
c821b5f1 GE |
164 | #else |
165 | if (val & MCSR_DB) | |
166 | printf("Data PLB Error\n"); | |
167 | #endif | |
efa35cf1 GB |
168 | if (val & MCSR_TLBP) |
169 | printf("TLB Parity Error\n"); | |
170 | if (val & MCSR_ICP){ | |
171 | /*flush_instruction_cache(); */ | |
172 | printf("I-Cache Parity Error\n"); | |
173 | } | |
174 | if (val & MCSR_DCSP) | |
175 | printf("D-Cache Search Parity Error\n"); | |
176 | if (val & MCSR_DCFP) | |
177 | printf("D-Cache Flush Parity Error\n"); | |
178 | if (val & MCSR_IMPE) | |
179 | printf("Machine Check exception is imprecise\n"); | |
180 | ||
181 | /* Clear MCSR */ | |
182 | mtspr(SPRN_MCSR, val); | |
183 | } | |
be24ef6e SR |
184 | |
185 | #if defined(CONFIG_DDR_ECC) && defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) | |
186 | /* | |
187 | * Read and print ECC status register/info: | |
188 | * The faulting address is only known upon uncorrectable ECC | |
189 | * errors. | |
190 | */ | |
191 | mfsdram(SDRAM_ECCES, val); | |
192 | if (val & SDRAM_ECCES_CE) | |
193 | printf("ECC: Correctable error\n"); | |
194 | if (val & SDRAM_ECCES_UE) { | |
195 | printf("ECC: Uncorrectable error at 0x%02x%08x\n", | |
196 | mfdcr(SDRAM_ERRADDULL), mfdcr(SDRAM_ERRADDLLL)); | |
197 | } | |
198 | #endif /* CONFIG_DDR_ECC ... */ | |
199 | ||
a1bd6200 NG |
200 | #if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) |
201 | mfsdram(DDR0_00, val) ; | |
b002144e | 202 | printf("DDR0: DDR0_00 %lx\n", val); |
a1bd6200 NG |
203 | val = (val >> 16) & 0xff; |
204 | if (val & 0x80) | |
205 | printf("DDR0: At least one interrupt active\n"); | |
206 | if (val & 0x40) | |
207 | printf("DDR0: DRAM initialization complete.\n"); | |
27a528fb | 208 | if (val & 0x20) { |
a1bd6200 | 209 | printf("DDR0: Multiple uncorrectable ECC events.\n"); |
27a528fb SR |
210 | uncorr_ecc = 1; |
211 | } | |
212 | if (val & 0x10) { | |
a1bd6200 | 213 | printf("DDR0: Single uncorrectable ECC event.\n"); |
27a528fb SR |
214 | uncorr_ecc = 1; |
215 | } | |
216 | if (val & 0x08) { | |
a1bd6200 | 217 | printf("DDR0: Multiple correctable ECC events.\n"); |
27a528fb SR |
218 | corr_ecc = 1; |
219 | } | |
220 | if (val & 0x04) { | |
a1bd6200 | 221 | printf("DDR0: Single correctable ECC event.\n"); |
27a528fb SR |
222 | corr_ecc = 1; |
223 | } | |
a1bd6200 NG |
224 | if (val & 0x02) |
225 | printf("Multiple accesses outside the defined" | |
226 | " physical memory space detected\n"); | |
227 | if (val & 0x01) | |
228 | printf("DDR0: Single access outside the defined" | |
229 | " physical memory space detected.\n"); | |
230 | ||
231 | mfsdram(DDR0_01, val); | |
232 | val = (val >> 8) & 0x7; | |
233 | switch (val ) { | |
234 | case 0: | |
235 | printf("DDR0: Write Out-of-Range command\n"); | |
236 | break; | |
237 | case 1: | |
238 | printf("DDR0: Read Out-of-Range command\n"); | |
239 | break; | |
240 | case 2: | |
241 | printf("DDR0: Masked write Out-of-Range command\n"); | |
242 | break; | |
243 | case 4: | |
244 | printf("DDR0: Wrap write Out-of-Range command\n"); | |
245 | break; | |
246 | case 5: | |
247 | printf("DDR0: Wrap read Out-of-Range command\n"); | |
248 | break; | |
249 | default: | |
250 | mfsdram(DDR0_01, value2); | |
b002144e | 251 | printf("DDR0: No DDR0 error know 0x%lx %x\n", val, value2); |
a1bd6200 NG |
252 | } |
253 | mfsdram(DDR0_23, val); | |
27a528fb | 254 | if (((val >> 16) & 0xff) && corr_ecc) |
b002144e | 255 | printf("DDR0: Syndrome for correctable ECC event 0x%lx\n", |
a1bd6200 NG |
256 | (val >> 16) & 0xff); |
257 | mfsdram(DDR0_23, val); | |
27a528fb | 258 | if (((val >> 8) & 0xff) && uncorr_ecc) |
b002144e | 259 | printf("DDR0: Syndrome for uncorrectable ECC event 0x%lx\n", |
a1bd6200 NG |
260 | (val >> 8) & 0xff); |
261 | mfsdram(DDR0_33, val); | |
262 | if (val) | |
263 | printf("DDR0: Address of command that caused an " | |
b002144e | 264 | "Out-of-Range interrupt %lx\n", val); |
a1bd6200 | 265 | mfsdram(DDR0_34, val); |
27a528fb | 266 | if (val && uncorr_ecc) |
b002144e | 267 | printf("DDR0: Address of uncorrectable ECC event %lx\n", val); |
a1bd6200 | 268 | mfsdram(DDR0_35, val); |
27a528fb | 269 | if (val && uncorr_ecc) |
b002144e | 270 | printf("DDR0: Address of uncorrectable ECC event %lx\n", val); |
a1bd6200 | 271 | mfsdram(DDR0_36, val); |
27a528fb | 272 | if (val && uncorr_ecc) |
b002144e | 273 | printf("DDR0: Data of uncorrectable ECC event 0x%08lx\n", val); |
a1bd6200 | 274 | mfsdram(DDR0_37, val); |
27a528fb | 275 | if (val && uncorr_ecc) |
b002144e | 276 | printf("DDR0: Data of uncorrectable ECC event 0x%08lx\n", val); |
a1bd6200 | 277 | mfsdram(DDR0_38, val); |
27a528fb | 278 | if (val && corr_ecc) |
b002144e | 279 | printf("DDR0: Address of correctable ECC event %lx\n", val); |
a1bd6200 | 280 | mfsdram(DDR0_39, val); |
27a528fb | 281 | if (val && corr_ecc) |
b002144e | 282 | printf("DDR0: Address of correctable ECC event %lx\n", val); |
a1bd6200 | 283 | mfsdram(DDR0_40, val); |
27a528fb | 284 | if (val && corr_ecc) |
b002144e | 285 | printf("DDR0: Data of correctable ECC event 0x%08lx\n", val); |
a1bd6200 | 286 | mfsdram(DDR0_41, val); |
27a528fb | 287 | if (val && corr_ecc) |
b002144e | 288 | printf("DDR0: Data of correctable ECC event 0x%08lx\n", val); |
a1bd6200 NG |
289 | #endif /* CONFIG_440EPX */ |
290 | #endif /* CONFIG_440 */ | |
affae2bf WD |
291 | show_regs(regs); |
292 | print_backtrace((unsigned long *)regs->gpr[1]); | |
293 | panic("machine check"); | |
294 | } | |
295 | ||
20051f2a | 296 | void AlignmentException(struct pt_regs *regs) |
affae2bf | 297 | { |
3a1ed1e1 | 298 | #if defined(CONFIG_CMD_KGDB) |
affae2bf WD |
299 | if (debugger_exception_handler && (*debugger_exception_handler)(regs)) |
300 | return; | |
301 | #endif | |
302 | ||
303 | show_regs(regs); | |
304 | print_backtrace((unsigned long *)regs->gpr[1]); | |
305 | panic("Alignment Exception"); | |
306 | } | |
307 | ||
20051f2a | 308 | void ProgramCheckException(struct pt_regs *regs) |
affae2bf | 309 | { |
8bde7f77 | 310 | long esr_val; |
affae2bf | 311 | |
3a1ed1e1 | 312 | #if defined(CONFIG_CMD_KGDB) |
affae2bf WD |
313 | if (debugger_exception_handler && (*debugger_exception_handler)(regs)) |
314 | return; | |
315 | #endif | |
316 | ||
317 | show_regs(regs); | |
318 | ||
8bde7f77 WD |
319 | esr_val = get_esr(); |
320 | if( esr_val & ESR_PIL ) | |
affae2bf | 321 | printf( "** Illegal Instruction **\n" ); |
8bde7f77 | 322 | else if( esr_val & ESR_PPR ) |
affae2bf | 323 | printf( "** Privileged Instruction **\n" ); |
8bde7f77 | 324 | else if( esr_val & ESR_PTR ) |
affae2bf WD |
325 | printf( "** Trap Instruction **\n" ); |
326 | ||
327 | print_backtrace((unsigned long *)regs->gpr[1]); | |
328 | panic("Program Check Exception"); | |
329 | } | |
330 | ||
20051f2a | 331 | void DecrementerPITException(struct pt_regs *regs) |
affae2bf | 332 | { |
8bde7f77 WD |
333 | /* |
334 | * Reset PIT interrupt | |
335 | */ | |
f472069f | 336 | mtspr(SPRN_TSR, 0x08000000); |
8bde7f77 WD |
337 | |
338 | /* | |
339 | * Call timer_interrupt routine in interrupts.c | |
340 | */ | |
341 | timer_interrupt(NULL); | |
affae2bf WD |
342 | } |
343 | ||
344 | ||
20051f2a | 345 | void UnknownException(struct pt_regs *regs) |
affae2bf | 346 | { |
3a1ed1e1 | 347 | #if defined(CONFIG_CMD_KGDB) |
affae2bf WD |
348 | if (debugger_exception_handler && (*debugger_exception_handler)(regs)) |
349 | return; | |
350 | #endif | |
351 | ||
352 | printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", | |
353 | regs->nip, regs->msr, regs->trap); | |
354 | _exception(0, regs); | |
355 | } | |
356 | ||
20051f2a | 357 | void DebugException(struct pt_regs *regs) |
affae2bf WD |
358 | { |
359 | printf("Debugger trap at @ %lx\n", regs->nip ); | |
360 | show_regs(regs); | |
3a1ed1e1 | 361 | #if defined(CONFIG_CMD_BEDBUG) |
affae2bf WD |
362 | do_bedbug_breakpoint( regs ); |
363 | #endif | |
364 | } | |
365 | ||
366 | /* Probe an address by reading. If not present, return -1, otherwise | |
367 | * return 0. | |
368 | */ | |
369 | int | |
370 | addr_probe(uint *addr) | |
371 | { | |
372 | #if 0 | |
373 | int retval; | |
374 | ||
375 | __asm__ __volatile__( \ | |
376 | "1: lwz %0,0(%1)\n" \ | |
83b4cfa3 WD |
377 | " eieio\n" \ |
378 | " li %0,0\n" \ | |
379 | "2:\n" \ | |
380 | ".section .fixup,\"ax\"\n" \ | |
381 | "3: li %0,-1\n" \ | |
382 | " b 2b\n" \ | |
383 | ".section __ex_table,\"a\"\n" \ | |
384 | " .align 2\n" \ | |
385 | " .long 1b,3b\n" \ | |
386 | ".text" \ | |
387 | : "=r" (retval) : "r"(addr)); | |
affae2bf WD |
388 | |
389 | return (retval); | |
390 | #endif | |
391 | return 0; | |
392 | } |