1 | /* |
---|
2 | * vectors_init.c Exception hanlding initialisation (and generic handler). |
---|
3 | * |
---|
4 | * This include file describe the data structure and the functions implemented |
---|
5 | * by rtems to handle exceptions. |
---|
6 | * |
---|
7 | * CopyRight (C) 1999 valette@crf.canon.fr |
---|
8 | * |
---|
9 | * The license and distribution terms for this file may be |
---|
10 | * found in found in the file LICENSE in this distribution or at |
---|
11 | * http://www.rtems.com/license/LICENSE. |
---|
12 | * |
---|
13 | * $Id$ |
---|
14 | */ |
---|
15 | #include <rtems.h> |
---|
16 | #include <rtems/bspIo.h> |
---|
17 | #include <rtems/error.h> |
---|
18 | |
---|
19 | #include <libcpu/raw_exception.h> |
---|
20 | #include <libcpu/spr.h> |
---|
21 | #include <libcpu/cpuIdent.h> |
---|
22 | |
---|
23 | #include "vectors.h" |
---|
24 | #include "ppc_exc_bspsupp.h" |
---|
25 | |
---|
26 | static rtems_raw_except_global_settings exception_config; |
---|
27 | static rtems_raw_except_connect_data exception_table[LAST_VALID_EXC + 1]; |
---|
28 | |
---|
29 | uint32_t ppc_exc_cache_wb_check = 1; |
---|
30 | |
---|
31 | #if 0 |
---|
32 | typedef struct ppc_exc_connect_data_ { |
---|
33 | rtems_raw_except_connect_data raw; |
---|
34 | ppc_exc_handler_t c_hdl; |
---|
35 | } ppc_exc_connect_data; |
---|
36 | #endif |
---|
37 | |
---|
38 | exception_handler_t globalExceptHdl; |
---|
39 | |
---|
40 | /* T. Straumann: provide a stack trace |
---|
41 | * <strauman@slac.stanford.edu>, 6/26/2001 |
---|
42 | */ |
---|
43 | typedef struct LRFrameRec_ { |
---|
44 | struct LRFrameRec_ *frameLink; |
---|
45 | unsigned long *lr; |
---|
46 | } LRFrameRec, *LRFrame; |
---|
47 | |
---|
48 | #define STACK_CLAMP 50 /* in case we have a corrupted bottom */ |
---|
49 | |
---|
50 | SPR_RO(LR) |
---|
51 | SPR_RO(DAR) |
---|
52 | #define DEAR_BOOKE 61 |
---|
53 | #define DEAR_405 0x3d5 |
---|
54 | SPR_RO(DEAR_BOOKE) |
---|
55 | SPR_RO(DEAR_405) |
---|
56 | |
---|
57 | uint32_t ppc_exc_get_DAR_dflt() |
---|
58 | { |
---|
59 | if ( ppc_cpu_is_60x() ) |
---|
60 | return _read_DAR(); |
---|
61 | else switch ( ppc_cpu_is_bookE() ) { |
---|
62 | default: break; |
---|
63 | case PPC_BOOKE_STD: |
---|
64 | case PPC_BOOKE_E500: |
---|
65 | return _read_DEAR_BOOKE(); |
---|
66 | case PPC_BOOKE_405: |
---|
67 | return _read_DEAR_405(); |
---|
68 | } |
---|
69 | return 0xdeadbeef; |
---|
70 | } |
---|
71 | |
---|
72 | uint32_t (*ppc_exc_get_DAR)() = ppc_exc_get_DAR_dflt; |
---|
73 | |
---|
74 | void |
---|
75 | BSP_printStackTrace(BSP_Exception_frame* excPtr) |
---|
76 | { |
---|
77 | LRFrame f; |
---|
78 | int i; |
---|
79 | LRFrame sp; |
---|
80 | void *lr; |
---|
81 | |
---|
82 | printk("Stack Trace: \n "); |
---|
83 | if (excPtr) { |
---|
84 | printk("IP: 0x%08x, ",excPtr->EXC_SRR0); |
---|
85 | sp=(LRFrame)excPtr->GPR1; |
---|
86 | lr=(void*)excPtr->EXC_LR; |
---|
87 | } else { |
---|
88 | /* there's no macro for this */ |
---|
89 | __asm__ __volatile__("mr %0, 1":"=r"(sp)); |
---|
90 | lr=(LRFrame)_read_LR(); |
---|
91 | } |
---|
92 | printk("LR: 0x%08x\n",lr); |
---|
93 | for (f=(LRFrame)sp, i=0; f->frameLink && i<STACK_CLAMP; f=f->frameLink) { |
---|
94 | printk("--^ 0x%08x", (long)(f->frameLink->lr)); |
---|
95 | if (!(++i%5)) |
---|
96 | printk("\n"); |
---|
97 | } |
---|
98 | if (i>=STACK_CLAMP) { |
---|
99 | printk("Too many stack frames (stack possibly corrupted), giving up...\n"); |
---|
100 | } else { |
---|
101 | if (i%5) |
---|
102 | printk("\n"); |
---|
103 | } |
---|
104 | } |
---|
105 | |
---|
106 | void C_exception_handler(BSP_Exception_frame* excPtr) |
---|
107 | { |
---|
108 | int recoverable = 0; |
---|
109 | int synch = (int)excPtr->_EXC_number >= 0 ; |
---|
110 | unsigned n = excPtr->_EXC_number & 0x7fff; |
---|
111 | |
---|
112 | printk("Exception handler called for exception %d (0x%x)\n", n, n); |
---|
113 | printk("\t Next PC or Address of fault = %08x\n", excPtr->EXC_SRR0); |
---|
114 | printk("\t Saved MSR = %08x\n", excPtr->EXC_SRR1); |
---|
115 | printk("\t R0 = %08x", excPtr->GPR0); |
---|
116 | if ( synch ) { |
---|
117 | printk(" R1 = %08x", excPtr->GPR1); |
---|
118 | printk(" R2 = %08x", excPtr->GPR2); |
---|
119 | } else { |
---|
120 | printk(" "); |
---|
121 | printk(" "); |
---|
122 | } |
---|
123 | printk(" R3 = %08x\n", excPtr->GPR3); |
---|
124 | printk("\t R4 = %08x", excPtr->GPR4); |
---|
125 | printk(" R5 = %08x", excPtr->GPR5); |
---|
126 | printk(" R6 = %08x", excPtr->GPR6); |
---|
127 | printk(" R7 = %08x\n", excPtr->GPR7); |
---|
128 | printk("\t R8 = %08x", excPtr->GPR8); |
---|
129 | printk(" R9 = %08x", excPtr->GPR9); |
---|
130 | printk(" R10 = %08x", excPtr->GPR10); |
---|
131 | printk(" R11 = %08x\n", excPtr->GPR11); |
---|
132 | printk("\t R12 = %08x", excPtr->GPR12); |
---|
133 | if ( synch ) { |
---|
134 | printk(" R13 = %08x", excPtr->GPR13); |
---|
135 | printk(" R14 = %08x", excPtr->GPR14); |
---|
136 | printk(" R15 = %08x\n", excPtr->GPR15); |
---|
137 | printk("\t R16 = %08x", excPtr->GPR16); |
---|
138 | printk(" R17 = %08x", excPtr->GPR17); |
---|
139 | printk(" R18 = %08x", excPtr->GPR18); |
---|
140 | printk(" R19 = %08x\n", excPtr->GPR19); |
---|
141 | printk("\t R20 = %08x", excPtr->GPR20); |
---|
142 | printk(" R21 = %08x", excPtr->GPR21); |
---|
143 | printk(" R22 = %08x", excPtr->GPR22); |
---|
144 | printk(" R23 = %08x\n", excPtr->GPR23); |
---|
145 | printk("\t R24 = %08x", excPtr->GPR24); |
---|
146 | printk(" R25 = %08x", excPtr->GPR25); |
---|
147 | printk(" R26 = %08x", excPtr->GPR26); |
---|
148 | printk(" R27 = %08x\n", excPtr->GPR27); |
---|
149 | printk("\t R28 = %08x", excPtr->GPR28); |
---|
150 | printk(" R29 = %08x", excPtr->GPR29); |
---|
151 | printk(" R30 = %08x", excPtr->GPR30); |
---|
152 | printk(" R31 = %08x\n", excPtr->GPR31); |
---|
153 | } else { |
---|
154 | printk("\n"); |
---|
155 | } |
---|
156 | printk("\t CR = %08x\n", excPtr->EXC_CR); |
---|
157 | printk("\t CTR = %08x\n", excPtr->EXC_CTR); |
---|
158 | printk("\t XER = %08x\n", excPtr->EXC_XER); |
---|
159 | printk("\t LR = %08x\n", excPtr->EXC_LR); |
---|
160 | |
---|
161 | /* Would be great to print DAR but unfortunately, |
---|
162 | * that is not portable across different CPUs. |
---|
163 | * AFAIK on classic PPC DAR is SPR 19, on the |
---|
164 | * 405 we have DEAR = SPR 0x3d5 and booE says |
---|
165 | * DEAR = SPR 61 :-( |
---|
166 | */ |
---|
167 | if ( ppc_exc_get_DAR ) { |
---|
168 | printk("\t DAR = %08x\n", ppc_exc_get_DAR()); |
---|
169 | } |
---|
170 | |
---|
171 | BSP_printStackTrace(excPtr); |
---|
172 | |
---|
173 | if (excPtr->_EXC_number == ASM_DEC_VECTOR) |
---|
174 | recoverable = 1; |
---|
175 | if (excPtr->_EXC_number == ASM_SYS_VECTOR) |
---|
176 | #ifdef TEST_RAW_EXCEPTION_CODE |
---|
177 | recoverable = 1; |
---|
178 | #else |
---|
179 | recoverable = 0; |
---|
180 | #endif |
---|
181 | if (!recoverable) { |
---|
182 | printk("unrecoverable exception!!! Push reset button\n"); |
---|
183 | while(1); |
---|
184 | } |
---|
185 | } |
---|
186 | |
---|
187 | /*********************************************************** |
---|
188 | * dummy functions for on/off/isOn calls |
---|
189 | * these functions just do nothing fulfill the semantic |
---|
190 | * requirements to enable/disable a certain exception |
---|
191 | */ |
---|
192 | void exception_nop_enable(const rtems_raw_except_connect_data* ptr) |
---|
193 | { |
---|
194 | } |
---|
195 | |
---|
196 | int exception_always_enabled(const rtems_raw_except_connect_data* ptr) |
---|
197 | { |
---|
198 | return 1; |
---|
199 | } |
---|
200 | |
---|
201 | /* Raw exception framework wants to keep a pointer to |
---|
202 | * the prologue so we must keep the ones we generate |
---|
203 | * from templates around... |
---|
204 | */ |
---|
205 | #define NUM_PROLOG 8 /* just a reasonable limit */ |
---|
206 | static int n_prolog = 0; |
---|
207 | static ppc_exc_min_prolog_t prologues[NUM_PROLOG]; |
---|
208 | |
---|
209 | static ppc_exc_min_prolog_template_t prolog_templates[][2] = { |
---|
210 | [ PPC_EXC_CLASSIC ] = |
---|
211 | { |
---|
212 | ppc_exc_min_prolog_sync_tmpl_std, |
---|
213 | ppc_exc_min_prolog_async_tmpl_std, |
---|
214 | }, |
---|
215 | [ PPC_EXC_405_CRITICAL ] = |
---|
216 | { |
---|
217 | ppc_exc_min_prolog_sync_tmpl_p405_crit, |
---|
218 | ppc_exc_min_prolog_async_tmpl_p405_crit, |
---|
219 | }, |
---|
220 | [ PPC_EXC_BOOKE_CRITICAL ] = |
---|
221 | { |
---|
222 | ppc_exc_min_prolog_sync_tmpl_bookE_crit, |
---|
223 | ppc_exc_min_prolog_async_tmpl_bookE_crit, |
---|
224 | }, |
---|
225 | [ PPC_EXC_E500_MACHCHK ] = |
---|
226 | { |
---|
227 | ppc_exc_min_prolog_sync_tmpl_e500_mchk, |
---|
228 | ppc_exc_min_prolog_async_tmpl_e500_mchk, |
---|
229 | }, |
---|
230 | }; |
---|
231 | |
---|
232 | static rtems_raw_except_func |
---|
233 | make_prologue(int vector, ppc_raw_exception_category cat) |
---|
234 | { |
---|
235 | int async = (cat & PPC_EXC_ASYNC) ? 1 : 0 ; |
---|
236 | ppc_exc_min_prolog_template_t tmpl; |
---|
237 | |
---|
238 | cat &= ~PPC_EXC_ASYNC; |
---|
239 | |
---|
240 | if ( n_prolog >= NUM_PROLOG ) { |
---|
241 | rtems_panic("Not enough exception prologue slots; increase NUM_PROLOG (%s)\n",__FILE__); |
---|
242 | } |
---|
243 | |
---|
244 | if ( ! (tmpl = prolog_templates[cat][async]) ) { |
---|
245 | rtems_panic("No exception prologue template for category 0x%02x found\n", cat); |
---|
246 | } |
---|
247 | |
---|
248 | ppc_exc_min_prolog_expand(prologues[n_prolog], tmpl, vector); |
---|
249 | |
---|
250 | return (rtems_raw_except_func)prologues[n_prolog++]; |
---|
251 | } |
---|
252 | |
---|
253 | void ppc_exc_init( |
---|
254 | rtems_raw_except_connect_data *exception_table, |
---|
255 | int nEntries) |
---|
256 | { |
---|
257 | int i,v; |
---|
258 | ppc_raw_exception_category cat; |
---|
259 | uintptr_t vaddr; |
---|
260 | |
---|
261 | /* |
---|
262 | * Initialize pointer used by low level execption handling |
---|
263 | */ |
---|
264 | globalExceptHdl = C_exception_handler; |
---|
265 | /* |
---|
266 | * Put default_exception_vector_code_prolog at relevant exception |
---|
267 | * code entry addresses |
---|
268 | */ |
---|
269 | exception_config.exceptSize = nEntries; |
---|
270 | exception_config.rawExceptHdlTbl = exception_table; |
---|
271 | exception_config.defaultRawEntry.exceptIndex = 0; |
---|
272 | exception_config.defaultRawEntry.hdl.vector = 0; |
---|
273 | /* Note that the 'auto' handler cannot be used for everything; in particular, |
---|
274 | * it assumes classic exceptions with a vector offset aligned on a 256-byte |
---|
275 | * boundary. |
---|
276 | */ |
---|
277 | exception_config.defaultRawEntry.hdl.raw_hdl = ppc_exc_min_prolog_auto; |
---|
278 | |
---|
279 | /* |
---|
280 | * Note that the cast of an array address to an unsigned |
---|
281 | * is not a bug as it is defined by a .set directly in asm... |
---|
282 | */ |
---|
283 | exception_config.defaultRawEntry.hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size; |
---|
284 | |
---|
285 | for (i=0; i < exception_config.exceptSize; i++) { |
---|
286 | |
---|
287 | if ( PPC_EXC_INVALID == (cat = ppc_vector_is_valid ((v=exception_table[i].hdl.vector))) ) { |
---|
288 | continue; |
---|
289 | } |
---|
290 | |
---|
291 | exception_table[i].exceptIndex = i; |
---|
292 | exception_table[v].hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size; |
---|
293 | |
---|
294 | /* special cases */ |
---|
295 | if ( ppc_cpu_has_shadowed_gprs() |
---|
296 | && ( ASM_60X_IMISS_VECTOR == v |
---|
297 | || ASM_60X_DLMISS_VECTOR == v |
---|
298 | || ASM_60X_DSMISS_VECTOR == v ) ) { |
---|
299 | exception_table[i].hdl.raw_hdl = ppc_exc_tgpr_clr_prolog; |
---|
300 | exception_table[i].hdl.raw_hdl_size = (unsigned)ppc_exc_tgpr_clr_prolog_size; |
---|
301 | } else { |
---|
302 | |
---|
303 | vaddr = (uintptr_t)ppc_get_vector_addr( v ); |
---|
304 | |
---|
305 | /* |
---|
306 | * default prolog can handle classic, synchronous exceptions |
---|
307 | * with a vector offset aligned on a 256-byte boundary. |
---|
308 | */ |
---|
309 | if ( PPC_EXC_CLASSIC == cat && 0 == ( vaddr & 0xff ) ) { |
---|
310 | exception_table[i].hdl.raw_hdl_size = exception_config.defaultRawEntry.hdl.raw_hdl_size; |
---|
311 | exception_table[i].hdl.raw_hdl = exception_config.defaultRawEntry.hdl.raw_hdl; |
---|
312 | } else { |
---|
313 | exception_table[i].hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size; |
---|
314 | exception_table[i].hdl.raw_hdl = make_prologue( v, cat ); |
---|
315 | } |
---|
316 | |
---|
317 | } |
---|
318 | exception_table[i].on = exception_nop_enable; |
---|
319 | exception_table[i].off = exception_nop_enable; |
---|
320 | exception_table[i].isOn = exception_always_enabled; |
---|
321 | } |
---|
322 | if (!ppc_init_exceptions(&exception_config)) { |
---|
323 | BSP_panic("Exception handling initialization failed\n"); |
---|
324 | } |
---|
325 | #ifdef RTEMS_DEBUG |
---|
326 | else { |
---|
327 | printk("Exception handling initialization done\n"); |
---|
328 | } |
---|
329 | #endif |
---|
330 | } |
---|
331 | |
---|
332 | void initialize_exceptions() |
---|
333 | { |
---|
334 | int i; |
---|
335 | int n = sizeof(exception_table)/sizeof(exception_table[0]); |
---|
336 | |
---|
337 | /* Use current MMU / RI settings when running C exception handlers */ |
---|
338 | ppc_exc_msr_bits = _read_MSR() & ( MSR_DR | MSR_IR | MSR_RI ); |
---|
339 | |
---|
340 | /* Cache size of the interrupt stack in a SDA variable */ |
---|
341 | ppc_exc_intr_stack_size = rtems_configuration_get_interrupt_stack_size(); |
---|
342 | |
---|
343 | /* Copy into a SDA variable that is easy to access from |
---|
344 | * assembly code |
---|
345 | */ |
---|
346 | if ( ppc_cpu_is_bookE() ) { |
---|
347 | ppc_exc_msr_irq_mask = MSR_EE | MSR_CE | MSR_DE ; |
---|
348 | switch (ppc_exc_crit_always_enabled) { |
---|
349 | case PPC_EXC_CRIT_NO_OS_SUPPORT: |
---|
350 | _write_MSR(_read_MSR() | (MSR_CE | MSR_DE)); |
---|
351 | break; |
---|
352 | |
---|
353 | case PPC_EXC_CRIT_OS_SUPPORT: |
---|
354 | printk("ppc_exc: PPC_EXC_CRIT_OS_SUPPORT not yet implemented\n"); |
---|
355 | /* fall thru */ |
---|
356 | |
---|
357 | case PPC_EXC_CRIT_DISABLED: |
---|
358 | default: |
---|
359 | ppc_exc_crit_always_enabled = PPC_EXC_CRIT_DISABLED; |
---|
360 | _write_MSR(_read_MSR() & ~(MSR_CE | MSR_DE)); |
---|
361 | break; |
---|
362 | } |
---|
363 | } else { |
---|
364 | ppc_exc_msr_irq_mask = MSR_EE ; |
---|
365 | } |
---|
366 | |
---|
367 | for ( i=0; i<n; i++ ) |
---|
368 | exception_table[i].hdl.vector = i; |
---|
369 | ppc_exc_init(exception_table, n); |
---|
370 | |
---|
371 | /* If we are on a classic PPC with MSR_DR enabled then |
---|
372 | * assert that the mapping for at least this task's |
---|
373 | * stack is write-back-caching enabled (see README/CAVEATS) |
---|
374 | * Do this only if the cache is physically enabled. |
---|
375 | * Since it is not easy to figure that out in a |
---|
376 | * generic way we need help from the BSP: BSPs |
---|
377 | * which run entirely w/o the cache may set |
---|
378 | * ppc_exc_cache_wb_check to zero prior to calling |
---|
379 | * this routine. |
---|
380 | * |
---|
381 | * We run this check only after exception handling is |
---|
382 | * initialized so that we have some chance to get |
---|
383 | * information printed if it fails. |
---|
384 | * |
---|
385 | * Note that it is unsafe to ignore this issue; if |
---|
386 | * the check fails, do NOT disable it unless caches |
---|
387 | * are always physically disabled. |
---|
388 | */ |
---|
389 | if ( ppc_exc_cache_wb_check && (MSR_DR & ppc_exc_msr_bits) ) { |
---|
390 | /* The size of 63 assumes cache lines are at most 32 bytes */ |
---|
391 | uint8_t dummy[63]; |
---|
392 | uintptr_t p = (uintptr_t)dummy; |
---|
393 | /* If the dcbz instruction raises an alignment exception |
---|
394 | * then the stack is mapped as write-thru or caching-disabled. |
---|
395 | * The low-level code is not capable of dealing with this |
---|
396 | * ATM. |
---|
397 | */ |
---|
398 | p = (p + 31) & ~31; |
---|
399 | asm volatile("dcbz 0, %0"::"b"(p)); |
---|
400 | /* If we make it thru here then things seem to be OK */ |
---|
401 | } |
---|
402 | |
---|
403 | } |
---|