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(void) |
---|
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)(void) = 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 | static int nest = 0; |
---|
111 | |
---|
112 | int recoverable = 0; |
---|
113 | rtems_id id = 0; |
---|
114 | int synch; |
---|
115 | unsigned n; |
---|
116 | rtems_status_code sc; |
---|
117 | |
---|
118 | /* Catch recursion */ |
---|
119 | nest++; |
---|
120 | |
---|
121 | if ( nest > 2 ) { |
---|
122 | /* maybe printk() or dereferencing excPtr caused an exception; |
---|
123 | * die silently... |
---|
124 | */ |
---|
125 | while (1) |
---|
126 | ; |
---|
127 | } |
---|
128 | |
---|
129 | synch = (int)excPtr->_EXC_number >= 0 ; |
---|
130 | n = excPtr->_EXC_number & 0x7fff; |
---|
131 | |
---|
132 | printk("Exception handler called for exception %d (0x%x)\n", n, n); |
---|
133 | printk("\t Next PC or Address of fault = %08x\n", excPtr->EXC_SRR0); |
---|
134 | printk("\t Saved MSR = %08x\n", excPtr->EXC_SRR1); |
---|
135 | |
---|
136 | if ( nest > 1 ) { |
---|
137 | printk("Recursion in the exception handler detected; I'll spin now...\n"); |
---|
138 | while ( 1 ) |
---|
139 | ; |
---|
140 | } |
---|
141 | |
---|
142 | /* Try to find out more about the context where this happened */ |
---|
143 | printk("\t Context: "); |
---|
144 | if ( rtems_interrupt_is_in_progress() ) { |
---|
145 | printk("ISR"); |
---|
146 | } else if ( !_Thread_Executing ) { |
---|
147 | printk("Initialization (_Thread_Executing not available yet)"); |
---|
148 | } else { |
---|
149 | if ( RTEMS_SUCCESSFUL != (sc=rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &id)) ) { |
---|
150 | printk("Unable to determine faulting task; rtems_task_ident() returned %u", sc); |
---|
151 | id = 0; |
---|
152 | } else { |
---|
153 | printk("Task ID 0x%08x", id); |
---|
154 | } |
---|
155 | } |
---|
156 | printk("\n"); |
---|
157 | |
---|
158 | /* Dump registers */ |
---|
159 | |
---|
160 | printk("\t R0 = %08x", excPtr->GPR0); |
---|
161 | if ( synch ) { |
---|
162 | printk(" R1 = %08x", excPtr->GPR1); |
---|
163 | printk(" R2 = %08x", excPtr->GPR2); |
---|
164 | } else { |
---|
165 | printk(" "); |
---|
166 | printk(" "); |
---|
167 | } |
---|
168 | printk(" R3 = %08x\n", excPtr->GPR3); |
---|
169 | printk("\t R4 = %08x", excPtr->GPR4); |
---|
170 | printk(" R5 = %08x", excPtr->GPR5); |
---|
171 | printk(" R6 = %08x", excPtr->GPR6); |
---|
172 | printk(" R7 = %08x\n", excPtr->GPR7); |
---|
173 | printk("\t R8 = %08x", excPtr->GPR8); |
---|
174 | printk(" R9 = %08x", excPtr->GPR9); |
---|
175 | printk(" R10 = %08x", excPtr->GPR10); |
---|
176 | printk(" R11 = %08x\n", excPtr->GPR11); |
---|
177 | printk("\t R12 = %08x", excPtr->GPR12); |
---|
178 | if ( synch ) { |
---|
179 | printk(" R13 = %08x", excPtr->GPR13); |
---|
180 | printk(" R14 = %08x", excPtr->GPR14); |
---|
181 | printk(" R15 = %08x\n", excPtr->GPR15); |
---|
182 | printk("\t R16 = %08x", excPtr->GPR16); |
---|
183 | printk(" R17 = %08x", excPtr->GPR17); |
---|
184 | printk(" R18 = %08x", excPtr->GPR18); |
---|
185 | printk(" R19 = %08x\n", excPtr->GPR19); |
---|
186 | printk("\t R20 = %08x", excPtr->GPR20); |
---|
187 | printk(" R21 = %08x", excPtr->GPR21); |
---|
188 | printk(" R22 = %08x", excPtr->GPR22); |
---|
189 | printk(" R23 = %08x\n", excPtr->GPR23); |
---|
190 | printk("\t R24 = %08x", excPtr->GPR24); |
---|
191 | printk(" R25 = %08x", excPtr->GPR25); |
---|
192 | printk(" R26 = %08x", excPtr->GPR26); |
---|
193 | printk(" R27 = %08x\n", excPtr->GPR27); |
---|
194 | printk("\t R28 = %08x", excPtr->GPR28); |
---|
195 | printk(" R29 = %08x", excPtr->GPR29); |
---|
196 | printk(" R30 = %08x", excPtr->GPR30); |
---|
197 | printk(" R31 = %08x\n", excPtr->GPR31); |
---|
198 | } else { |
---|
199 | printk("\n"); |
---|
200 | } |
---|
201 | printk("\t CR = %08x\n", excPtr->EXC_CR); |
---|
202 | printk("\t CTR = %08x\n", excPtr->EXC_CTR); |
---|
203 | printk("\t XER = %08x\n", excPtr->EXC_XER); |
---|
204 | printk("\t LR = %08x\n", excPtr->EXC_LR); |
---|
205 | |
---|
206 | /* Would be great to print DAR but unfortunately, |
---|
207 | * that is not portable across different CPUs. |
---|
208 | * AFAIK on classic PPC DAR is SPR 19, on the |
---|
209 | * 405 we have DEAR = SPR 0x3d5 and booE says |
---|
210 | * DEAR = SPR 61 :-( |
---|
211 | */ |
---|
212 | if ( ppc_exc_get_DAR ) { |
---|
213 | printk("\t DAR = %08x\n", ppc_exc_get_DAR()); |
---|
214 | } |
---|
215 | |
---|
216 | BSP_printStackTrace(excPtr); |
---|
217 | |
---|
218 | if (excPtr->_EXC_number == ASM_DEC_VECTOR) |
---|
219 | recoverable = 1; |
---|
220 | if (excPtr->_EXC_number == ASM_SYS_VECTOR) |
---|
221 | #ifdef TEST_RAW_EXCEPTION_CODE |
---|
222 | recoverable = 1; |
---|
223 | #else |
---|
224 | recoverable = 0; |
---|
225 | #endif |
---|
226 | if (!recoverable) { |
---|
227 | if ( id ) { |
---|
228 | printk("Suspending faulting task (0x%08x)\n", id); |
---|
229 | /* Unnest here because rtems_task_suspend() never returns */ |
---|
230 | nest--; |
---|
231 | rtems_task_suspend(id); |
---|
232 | } else { |
---|
233 | printk("unrecoverable exception!!! Push reset button\n"); |
---|
234 | while(1); |
---|
235 | } |
---|
236 | } else { |
---|
237 | nest--; |
---|
238 | } |
---|
239 | } |
---|
240 | |
---|
241 | /*********************************************************** |
---|
242 | * dummy functions for on/off/isOn calls |
---|
243 | * these functions just do nothing fulfill the semantic |
---|
244 | * requirements to enable/disable a certain exception |
---|
245 | */ |
---|
246 | void exception_nop_enable(const rtems_raw_except_connect_data* ptr) |
---|
247 | { |
---|
248 | } |
---|
249 | |
---|
250 | int exception_always_enabled(const rtems_raw_except_connect_data* ptr) |
---|
251 | { |
---|
252 | return 1; |
---|
253 | } |
---|
254 | |
---|
255 | /* Raw exception framework wants to keep a pointer to |
---|
256 | * the prologue so we must keep the ones we generate |
---|
257 | * from templates around... |
---|
258 | */ |
---|
259 | #define NUM_PROLOG 8 /* just a reasonable limit */ |
---|
260 | static int n_prolog = 0; |
---|
261 | static ppc_exc_min_prolog_t prologues[NUM_PROLOG]; |
---|
262 | |
---|
263 | static ppc_exc_min_prolog_template_t prolog_templates[][2] = { |
---|
264 | [ PPC_EXC_CLASSIC ] = |
---|
265 | { |
---|
266 | ppc_exc_min_prolog_sync_tmpl_std, |
---|
267 | ppc_exc_min_prolog_async_tmpl_std, |
---|
268 | }, |
---|
269 | [ PPC_EXC_405_CRITICAL ] = |
---|
270 | { |
---|
271 | ppc_exc_min_prolog_sync_tmpl_p405_crit, |
---|
272 | ppc_exc_min_prolog_async_tmpl_p405_crit, |
---|
273 | }, |
---|
274 | [ PPC_EXC_BOOKE_CRITICAL ] = |
---|
275 | { |
---|
276 | ppc_exc_min_prolog_sync_tmpl_bookE_crit, |
---|
277 | ppc_exc_min_prolog_async_tmpl_bookE_crit, |
---|
278 | }, |
---|
279 | [ PPC_EXC_E500_MACHCHK ] = |
---|
280 | { |
---|
281 | ppc_exc_min_prolog_sync_tmpl_e500_mchk, |
---|
282 | ppc_exc_min_prolog_async_tmpl_e500_mchk, |
---|
283 | }, |
---|
284 | }; |
---|
285 | |
---|
286 | static rtems_raw_except_func |
---|
287 | make_prologue(int vector, ppc_raw_exception_category cat) |
---|
288 | { |
---|
289 | int async = (cat & PPC_EXC_ASYNC) ? 1 : 0 ; |
---|
290 | ppc_exc_min_prolog_template_t tmpl; |
---|
291 | |
---|
292 | cat &= ~PPC_EXC_ASYNC; |
---|
293 | |
---|
294 | if ( n_prolog >= NUM_PROLOG ) { |
---|
295 | rtems_panic("Not enough exception prologue slots; increase NUM_PROLOG (%s)\n",__FILE__); |
---|
296 | } |
---|
297 | |
---|
298 | if ( ! (tmpl = prolog_templates[cat][async]) ) { |
---|
299 | rtems_panic("No exception prologue template for category 0x%02x found\n", cat); |
---|
300 | } |
---|
301 | |
---|
302 | ppc_exc_min_prolog_expand(prologues[n_prolog], tmpl, vector); |
---|
303 | |
---|
304 | return (rtems_raw_except_func)prologues[n_prolog++]; |
---|
305 | } |
---|
306 | |
---|
307 | void ppc_exc_table_init( |
---|
308 | rtems_raw_except_connect_data *exception_table, |
---|
309 | int nEntries) |
---|
310 | { |
---|
311 | unsigned i,v; |
---|
312 | ppc_raw_exception_category cat; |
---|
313 | uintptr_t vaddr; |
---|
314 | |
---|
315 | /* |
---|
316 | * Initialize pointer used by low level execption handling |
---|
317 | */ |
---|
318 | globalExceptHdl = C_exception_handler; |
---|
319 | /* |
---|
320 | * Put default_exception_vector_code_prolog at relevant exception |
---|
321 | * code entry addresses |
---|
322 | */ |
---|
323 | exception_config.exceptSize = nEntries; |
---|
324 | exception_config.rawExceptHdlTbl = exception_table; |
---|
325 | exception_config.defaultRawEntry.exceptIndex = 0; |
---|
326 | exception_config.defaultRawEntry.hdl.vector = 0; |
---|
327 | |
---|
328 | if (ppc_cpu_has_ivpr_and_ivor()) { |
---|
329 | /* Use packed version with 16-byte boundaries for CPUs with IVPR and IVOR registers */ |
---|
330 | exception_config.defaultRawEntry.hdl.raw_hdl = ppc_exc_min_prolog_auto_packed; |
---|
331 | } else { |
---|
332 | /* Note that the 'auto' handler cannot be used for everything; in particular, |
---|
333 | * it assumes classic exceptions with a vector offset aligned on a 256-byte |
---|
334 | * boundary. |
---|
335 | */ |
---|
336 | exception_config.defaultRawEntry.hdl.raw_hdl = ppc_exc_min_prolog_auto; |
---|
337 | } |
---|
338 | |
---|
339 | /* |
---|
340 | * Note that the cast of an array address to an unsigned |
---|
341 | * is not a bug as it is defined by a .set directly in asm... |
---|
342 | */ |
---|
343 | exception_config.defaultRawEntry.hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size; |
---|
344 | |
---|
345 | for (i=0; i < exception_config.exceptSize; i++) { |
---|
346 | |
---|
347 | if ( PPC_EXC_INVALID == (cat = ppc_vector_is_valid ((v=exception_table[i].hdl.vector))) ) { |
---|
348 | continue; |
---|
349 | } |
---|
350 | |
---|
351 | exception_table[i].exceptIndex = i; |
---|
352 | exception_table[v].hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size; |
---|
353 | |
---|
354 | /* special cases */ |
---|
355 | if ( ppc_cpu_has_shadowed_gprs() |
---|
356 | && ( ASM_60X_IMISS_VECTOR == v |
---|
357 | || ASM_60X_DLMISS_VECTOR == v |
---|
358 | || ASM_60X_DSMISS_VECTOR == v ) ) { |
---|
359 | exception_table[i].hdl.raw_hdl = ppc_exc_tgpr_clr_prolog; |
---|
360 | exception_table[i].hdl.raw_hdl_size = (unsigned)ppc_exc_tgpr_clr_prolog_size; |
---|
361 | } else { |
---|
362 | |
---|
363 | vaddr = (uintptr_t)ppc_get_vector_addr( v ); |
---|
364 | |
---|
365 | /* |
---|
366 | * default prolog can handle classic, synchronous exceptions |
---|
367 | * with a vector offset aligned on a 256-byte boundary. |
---|
368 | */ |
---|
369 | if (cat == PPC_EXC_CLASSIC && ((vaddr & 0xff) == 0 || (ppc_cpu_has_ivpr_and_ivor() && (vaddr & 0xf) == 0))) { |
---|
370 | exception_table[i].hdl.raw_hdl_size = exception_config.defaultRawEntry.hdl.raw_hdl_size; |
---|
371 | exception_table[i].hdl.raw_hdl = exception_config.defaultRawEntry.hdl.raw_hdl; |
---|
372 | } else { |
---|
373 | exception_table[i].hdl.raw_hdl_size = (unsigned)ppc_exc_min_prolog_size; |
---|
374 | exception_table[i].hdl.raw_hdl = make_prologue( v, cat ); |
---|
375 | } |
---|
376 | |
---|
377 | } |
---|
378 | exception_table[i].on = exception_nop_enable; |
---|
379 | exception_table[i].off = exception_nop_enable; |
---|
380 | exception_table[i].isOn = exception_always_enabled; |
---|
381 | } |
---|
382 | if (!ppc_init_exceptions(&exception_config)) { |
---|
383 | BSP_panic("Exception handling initialization failed\n"); |
---|
384 | } |
---|
385 | #ifdef RTEMS_DEBUG |
---|
386 | else { |
---|
387 | printk("Exception handling initialization done\n"); |
---|
388 | } |
---|
389 | #endif |
---|
390 | } |
---|
391 | |
---|
392 | |
---|
393 | void ppc_exc_initialize( |
---|
394 | uint32_t interrupt_disable_mask, |
---|
395 | uint32_t interrupt_stack_start, |
---|
396 | uint32_t interrupt_stack_size |
---|
397 | ) |
---|
398 | { |
---|
399 | int i; |
---|
400 | int n = sizeof(exception_table)/sizeof(exception_table[0]); |
---|
401 | |
---|
402 | uint32_t interrupt_stack_end = 0; |
---|
403 | uint32_t interrupt_stack_pointer = 0; |
---|
404 | uint32_t *p = NULL; |
---|
405 | uint32_t r13, sda_base; |
---|
406 | |
---|
407 | /* Assembly code needs SDA_BASE in r13 (SVR4 or EABI). Make sure |
---|
408 | * early init code put it there. |
---|
409 | */ |
---|
410 | asm volatile( |
---|
411 | " lis %0, _SDA_BASE_@h \n" |
---|
412 | " ori %0, %0, _SDA_BASE_@l \n" |
---|
413 | " mr %1, 13 \n" |
---|
414 | :"=r"(sda_base),"=r"(r13) |
---|
415 | ); |
---|
416 | |
---|
417 | if ( sda_base != r13 ) { |
---|
418 | printk("ppc_exc_initialize(): INTERNAL ERROR\n"); |
---|
419 | printk(" your BSP seems to not have loaded _SDA_BASE_\n"); |
---|
420 | printk(" into R13 as required by SVR4/EABI. Check early init code...\n"); |
---|
421 | printk(" _SDA_BASE_: 0x%08x, R13: 0x%08x\n", sda_base, r13); |
---|
422 | while (1) |
---|
423 | ; |
---|
424 | } |
---|
425 | |
---|
426 | /* Interrupt stack end and pointer */ |
---|
427 | interrupt_stack_end = interrupt_stack_start + interrupt_stack_size; |
---|
428 | |
---|
429 | interrupt_stack_pointer = interrupt_stack_end - PPC_MINIMUM_STACK_FRAME_SIZE; |
---|
430 | |
---|
431 | /* Ensure proper interrupt stack alignment */ |
---|
432 | interrupt_stack_pointer &= ~(CPU_STACK_ALIGNMENT - 1); |
---|
433 | |
---|
434 | /* Tag interrupt stack bottom */ |
---|
435 | p = (uint32_t *) interrupt_stack_pointer; |
---|
436 | *p = 0; |
---|
437 | |
---|
438 | /* Move interrupt stack values to special purpose registers */ |
---|
439 | _write_SPRG1( interrupt_stack_pointer); |
---|
440 | _write_SPRG2( interrupt_stack_start); |
---|
441 | |
---|
442 | /* Interrupt disable mask */ |
---|
443 | ppc_interrupt_set_disable_mask( interrupt_disable_mask); |
---|
444 | |
---|
445 | /* Use current MMU / RI settings when running C exception handlers */ |
---|
446 | ppc_exc_msr_bits = _read_MSR() & ( MSR_DR | MSR_IR | MSR_RI ); |
---|
447 | |
---|
448 | for ( i=0; i<n; i++ ) |
---|
449 | exception_table[i].hdl.vector = i; |
---|
450 | ppc_exc_table_init(exception_table, n); |
---|
451 | |
---|
452 | /* If we are on a classic PPC with MSR_DR enabled then |
---|
453 | * assert that the mapping for at least this task's |
---|
454 | * stack is write-back-caching enabled (see README/CAVEATS) |
---|
455 | * Do this only if the cache is physically enabled. |
---|
456 | * Since it is not easy to figure that out in a |
---|
457 | * generic way we need help from the BSP: BSPs |
---|
458 | * which run entirely w/o the cache may set |
---|
459 | * ppc_exc_cache_wb_check to zero prior to calling |
---|
460 | * this routine. |
---|
461 | * |
---|
462 | * We run this check only after exception handling is |
---|
463 | * initialized so that we have some chance to get |
---|
464 | * information printed if it fails. |
---|
465 | * |
---|
466 | * Note that it is unsafe to ignore this issue; if |
---|
467 | * the check fails, do NOT disable it unless caches |
---|
468 | * are always physically disabled. |
---|
469 | */ |
---|
470 | if ( ppc_exc_cache_wb_check && (MSR_DR & ppc_exc_msr_bits) ) { |
---|
471 | /* The size of 63 assumes cache lines are at most 32 bytes */ |
---|
472 | uint8_t dummy[63]; |
---|
473 | uintptr_t p = (uintptr_t)dummy; |
---|
474 | /* If the dcbz instruction raises an alignment exception |
---|
475 | * then the stack is mapped as write-thru or caching-disabled. |
---|
476 | * The low-level code is not capable of dealing with this |
---|
477 | * ATM. |
---|
478 | */ |
---|
479 | p = (p + 31) & ~31; |
---|
480 | asm volatile("dcbz 0, %0"::"b"(p)); |
---|
481 | /* If we make it thru here then things seem to be OK */ |
---|
482 | } |
---|
483 | |
---|
484 | } |
---|