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