1 /* memerror-vector.S -- Memory Error Exception Vector and Handler */
3 /* $Id: //depot/rel/Cottonwood/Xtensa/OS/xtos/memerror-vector.S#3 $ */
6 * Copyright (c) 2006-2010 Tensilica Inc.
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //#include <xtensa/config/specreg.h>
29 #include <xtensa/coreasm.h>
30 #include <xtensa/corebits.h>
32 #if XCHAL_HAVE_MEM_ECC_PARITY
33 # if defined(__SPLIT__vector)
35 // Place this code in the memory error exception vector:
36 .begin literal_prefix .MemoryExceptionVector
37 .section .MemoryExceptionVector.text, "ax"
39 .global _MemErrorVector
42 # if 0 /* XCHAL_HAVE_DEBUG */
43 // Memory errors raise PS.INTLEVEL above DEBUGLEVEL, so
44 // break instructions have no effect within them (debug
45 // exceptions are masked). So leave commented out for now.
46 break 1, 5 // unhandled memory error exception
49 movi a0, _MemErrorHandler
52 .size _MemErrorVector, . - _MemErrorVector
57 # elif defined(__SPLIT__handler)
60 * Some rules and assumptions:
62 * Anything that can interrupt this handler (e.g. NMI):
63 * - must not lock or unlock cache lines
67 #define ICACHE_WAYWIDTH (XCHAL_ICACHE_SETWIDTH + XCHAL_ICACHE_LINEWIDTH) /* LICT's "iis" */
68 #define DCACHE_WAYWIDTH (XCHAL_DCACHE_SETWIDTH + XCHAL_DCACHE_LINEWIDTH) /* LDCT's "dis" */
69 /* NOTE: Memory ECC/parity is not supported on XLMI or on local ROMs: */
70 #define HAVE_LOCAL_RAM (XCHAL_NUM_DATARAM || XCHAL_NUM_INSTRAM /*|| XCHAL_NUM_URAM || XCHAL_NUM_XLMI*/)
73 //.lcomm _MemErrorSave, 8
74 .comm _MemErrorSave, 8, 4
78 .global _MemErrorHandler
81 bbsi.l a0, MESR_DME_SHIFT, .L_fatal_dme
82 # if XCHAL_ICACHE_SIZE > 0 || XCHAL_DCACHE_SIZE > 0
83 bbsi.l a0, MESR_MEMTYPE_SHIFT+1, .L_cache // branch if error on a cache
85 // Error in a local memory.
87 bbsi.l a0, MESR_ERRTYPE_SHIFT, .L_uncorrectable_local
88 // Correctable error in a local memory (IRAM or DRAM).
89 // (MEVADDR has all 32 bits, so XSR preserves a register:)
91 // Note: MEVADDR is always 4-byte aligned,
92 // so we can just do L32I/S32I to correct the error.
93 // However, that's not atomic, and NMI can store in between;
94 // that's usually a problem for D rather than I, avoid the
95 // issue using S32C1I if configured (else NMI must not write DataRAM!?!):
96 # if (XCHAL_HAVE_S32C1I && (XCHAL_NUM_DATARAM /*|| XCHAL_NUM_URAM || XCHAL_NUM_XLMI*/))
97 bbci.l a0, MESR_MEMTYPE_SHIFT, .L_instram // branch if error on InstRAM
98 // Unfortunately we need 3 registers to do S32C1I (data,addr,SCOMPARE1) so
99 // we need to save to _MemErrorSave:
100 movi a0, _MemErrorSave
101 s32i a4, a0, 0 // save a4
102 l32i a4, a2, 0 // load data (re-correct)
103 rsr a0, SCOMPARE1 // save SCOMPARE1
105 s32c1i a4, a2, 0 // store if still contains same value (else other store corrected error)
106 movi a4, _MemErrorSave
107 wsr a0, SCOMPARE1 // restore SCOMPARE1
108 l32i a4, a4, 0 // restore a4
112 l32i a0, a2, 0 // load data (re-correct)
113 s32i a0, a2, 0 // store data to correct ECC bits
115 # endif /* HAVE_LOCAL_RAM */
121 // Weak reference: if unresolved, links okay but with zero value:
122 .weak _xtos_merr_hook_fatal_dme
124 // Fatal (unrecoverable) error, double memory exception
125 movi a0, _xtos_merr_hook_fatal_dme
126 1: beqz a0, 1b // fatal double memory error, no hook, so infinite loop
127 jx a0 // jump to user hook, if present
131 // Weak reference: if unresolved, links okay but with zero value:
132 .weak _xtos_merr_hook_uncorrectable_local
133 .L_uncorrectable_local:
134 // Fatal (unrecoverable) error in IRAM or DRAM: parity or uncorrectable ECC error
135 movi a0, _xtos_merr_hook_uncorrectable_local
136 1: beqz a0, 1b // fatal memory error, no hook provided, so infinite loop
137 jx a0 // jump to user hook, if present
141 # if XCHAL_ICACHE_SIZE > 0 || XCHAL_DCACHE_SIZE > 0
143 // Error in one of the caches.
146 # if XCHAL_ICACHE_SIZE > 0
147 # if XCHAL_DCACHE_SIZE > 0
148 bbsi.l a0, MESR_MEMTYPE_SHIFT, .L_dcache // branch if data cache error
150 // Error in the instruction cache.
151 bbsi.l a0, MESR_ERRTYPE_SHIFT, .L_icache_noncorr // branch if uncorrectable
152 // Correctable error in the instruction cache.
154 // TODO FIXME: remove these 5 lines if waynum is in MEVADDR!? by using III if tag and IHI otherwise!?!?!?:
155 # if XCHAL_ICACHE_WAYS > 1
156 extui a0, a0, MESR_WAYNUM_SHIFT, 2
157 slli a0, a0, ICACHE_WAYWIDTH
158 slli a2, a2, 32 - ICACHE_WAYWIDTH
159 srli a2, a2, 32 - ICACHE_WAYWIDTH
162 iii a2, 0 // invalidate line (whole set!) if not locked
163 # if XCHAL_ICACHE_LINE_LOCKABLE
164 // III has no effect if the line is locked; for that case, need to do more:
166 bbci.l a0, XCHAL_ICACHE_TAG_L_SHIFT, .L_icache_done // branch if unlocked
167 // Correctable error in a locked instruction cache line.
168 // Fix both tag and one word, quicker than figuring out whether error is in tag or data:
169 sict a0, a2 // fix tag
171 sicw a0, a2 // fix data word
178 // Non-correctable error in the instruction cache.
179 bbsi.l a0, MESR_MEMTYPE_SHIFT+2, .L_icache_tag_noncorr // branch if tag error
180 // Non-correctable error in the instruction cache data.
181 // Just invalidate the line if we can.
182 # if XCHAL_ICACHE_LINE_LOCKABLE
183 // If locked, need a different fix sequence.
186 # if XCHAL_ICACHE_WAYS > 1
187 // This sequence is shorter, but does not retain original MEVADDR so
188 // prevents subsequent use of instructions requiring a virtual address
189 // (such as LICW, IPFL, etc):
190 // extui a0, a0, MESR_WAYNUM_SHIFT, 2
191 // slli a0, a0, ICACHE_WAYWIDTH
192 // slli a2, a2, 32 - ICACHE_WAYWIDTH
193 // srli a2, a2, 32 - ICACHE_WAYWIDTH
196 extui a0, a0, MESR_WAYNUM_SHIFT, 2 // id of way with mem error
197 slli a0, a0, ICACHE_WAYWIDTH
198 xor a0, a2, a0 // xor corresponding bits of addr
199 extui a0, a0, ICACHE_WAYWIDTH, 2 // take 2 xor'ed way bits
200 or a2, a2, a0 // save them at bottom of addr
201 slli a0, a0, ICACHE_WAYWIDTH
202 xor a2, a2, a0 // and change 2 way bits of addr
205 bbsi.l a0, XCHAL_ICACHE_TAG_L_SHIFT, .L_icache_locked_uncor // branch if locked
206 // Cache line is not locked, just invalidate:
207 # if XCHAL_ICACHE_WAYS > 1
214 // NOTE: we don't use the LICW/SICW sequence below unless the line is locked,
215 // otherwise the i-cache line might get replaced between LICW and SICW
216 // (if we're not extremely careful), which would be disastrous.
217 // Also, for locked lines, LICW/SICW is much safer than IHU/IHI/IPFL
218 // because it doesn't leave a window where the line is unlocked;
219 // however, if the error is non-correctable, we have no choice.
221 .L_icache_locked_uncor:
222 // If locked and uncorrectable however, the only recourse is relocking.
223 // So we need to recover the virtual address so we can do IPFL.
224 // Note: can't use MEPC instead of MEVADDR, because (a) it might not
225 // point to the correct cache line, and (b) it might be completely wrong
226 // in the case where the mem error happened e.g. during an LICW or IPFL.
227 # if XCHAL_ICACHE_WAYS > 1
228 // Recover virtual address in a2:
229 extui a0, a2, 0, 2 // get saved xor'ed bits at bottom
230 slli a0, a0, ICACHE_WAYWIDTH // line them up
231 xor a2, a2, a0 // restore original MEVADDR
233 ihu a2, 0 // unlock line
234 ihi a2, 0 // invalidate line
235 ipfl a2, 0 // refetch-and-lock the line
237 # else /* LOCKABLE */
239 ihi a0, 0 // invalidate that cache line
241 # endif /* LOCKABLE */
243 .L_icache_tag_noncorr:
244 // Non-correctable error in the instruction cache tag.
245 // Just invalidate the tag or the entire set.
246 # if XCHAL_ICACHE_LINE_LOCKABLE
247 // Note: can't use either IIU or III, as these don't write the entire tag,
248 // so they'll get the exception again. So, have to use SICT.
249 # if XCHAL_ICACHE_WAYS > 1
250 // TODO FIXME: avoid this 8-line alternative if waynum is in MEVADDR!?:
252 extui a0, a0, MESR_WAYNUM_SHIFT, 2
253 slli a0, a0, ICACHE_WAYWIDTH
254 slli a2, a2, 32 - ICACHE_WAYWIDTH
255 srli a2, a2, 32 - ICACHE_WAYWIDTH
257 iiu a2, 0 // unlock line ==> also invalidates! (I-side only)
261 iiu a0, 0 // unlock line ==> also invalidates! (I-side only)
263 // If line was locked, can't recover lock state, need external info to recover.
264 // User can provide an assembler hook routine _xtos_merr_hook_icache_relock
265 // to relock the icache at the index in a2:
266 // - any number of lines might still be locked at that index,
267 // including all of them
268 // - no stack is provided, a0 must be used as starting point to
269 // load a save area and saved registers as necessary
270 // - unless routine just does ret (i.e. does not modify any
271 // register, only possible if it does nothing), it needs to
272 // return by restoring all registers it modified, ending with:
275 // CAVEAT EMPTOR: this hook mechanism is subject to change.
276 .weak _xtos_merr_hook_icache_relock // if unresolved, links with zero value
277 movi a0, _xtos_merr_hook_icache_relock
278 1: beqz a0, 1b // if no hook to recover lock state on icache tag mem error, loop forever
279 callx0 a0 // invoke user hook to relock i-cache (index in MEVADDR)
282 iii a0, 0 // invalidate entire set
285 # endif /* have ICACHE */
288 # if XCHAL_DCACHE_SIZE > 0
289 # if XCHAL_ICACHE_SIZE > 0
292 // Error in the data cache.
293 # if XCHAL_DCACHE_IS_WRITEBACK || XCHAL_DCACHE_LINE_LOCKABLE
294 bbsi.l a0, MESR_ERRTYPE_SHIFT, .L_dcache_noncorr // branch if uncorrectable
295 // Uncorrectable error on a writeback dcache might be unrecoverable:
297 bbsi.l a0, MESR_MEMTYPE_SHIFT+2, .L_dcache_tag // branch if tag error
298 // Error in the data cache data (correctable, or non-correctable in writethru+unlockable cache).
299 // MEVADDR always a real vaddr here; might point to cache-isolate mode area though.
300 # if XCHAL_DCACHE_LINE_LOCKABLE
301 // Correctable error on lockable dcache data.
302 // If locked, need to refetch the line (or load/store its contents, which is less safe):
304 # if XCHAL_DCACHE_WAYS > 1
305 // Need some extra computation to get the correct dcache way's tag:
306 movi a0, _MemErrorSave
307 s32i a4, a0, 0 // save a4
308 s32i a5, a0, 4 // save a5
310 extui a4, a4, MESR_WAYNUM_SHIFT, 2
311 slli a4, a4, DCACHE_WAYWIDTH
312 slli a5, a2, 32 - DCACHE_WAYWIDTH
313 srli a5, a5, 32 - DCACHE_WAYWIDTH
317 l32i a4, a5, 0 // restore a4
318 l32i a5, a5, 4 // restore a5
322 // FIXME: if castout, a2 is a physical address! doesn't work with any address translation.
323 # if 0 /* translation */
324 movi a4, _xtos_vmap_vaddr // FIXME: do we need two variables for full MMU?
325 1: beqz a4, 1b // if no vaddr to use, loop forever (FIXME: caxlt: could assume V==P)
326 rdtlb1 a5, a4 // save current contents
327 ... clear lower bits of a4 ...
328 xx = some function of a2
330 a2 = virtual address, i.e. some function of a2 and a4 ...
331 ... do the sequence below ...
333 wdtlb a5, a4 // restore TLB entry
335 // NOTE: the following sequence leaves the line temporarily unlocked, if locked.
336 // We assume NMI handlers don't lock lines or rely on their being locked.
337 // We could have used "l32i a0,a2,0; s32i a0,a2,0" but that's not atomic on the data.
338 dhu a2, 0 // unlock the cache line, if locked
339 dhwbi a2, 0 // writeback and invalidate cache line
340 bbci.l a0, XCHAL_DCACHE_TAG_L_SHIFT, 1f
341 dpfl a2, 0 // re-prefetch-and-lock the cache line
343 # else /* LOCKABLE */
344 // Error in unlockable data cache data (correctable, or non-correctable in writethru cache).
346 // USELESS NOTE: if writethru dcache and NMI handlers don't store to this, we could use DHI instead:
347 // FIXME: if castout, a0 is a physical address! doesn't work with any address translation.
348 dhwbi a0, 0 // writeback (if correctable) and invalidate that cache line
349 # endif /* LOCKABLE */
353 // Error in data cache tag (correctable, or non-correctable in writethru+unlockable cache).
354 // MEVADDR only contains cache index here (not waynum), don't expect a vaddr (the ISA
355 // says upper bits are undefined; actual hw does put a vaddr, but in future might not).
356 // Whether or not correctable, just invalidate the particular way's line:
358 // NOTE: could remove these 5 lines if hw were designed with waynum in MEVADDR (but is not):
359 # if XCHAL_DCACHE_WAYS > 1
360 extui a0, a0, MESR_WAYNUM_SHIFT, 2
361 slli a0, a0, DCACHE_WAYWIDTH
362 slli a2, a2, 32 - DCACHE_WAYWIDTH
363 srli a2, a2, 32 - DCACHE_WAYWIDTH
366 # if XCHAL_DCACHE_LINE_LOCKABLE
367 ldct a0, a2 // invalidate and unlock that cache tag
368 bbci.l a0, XCHAL_DCACHE_TAG_L_SHIFT, 1f // branch if not locked
369 sdct a0, a2 // if locked, this safely writes whole tag
371 1: diwbi a2, 0 // writeback (if correctable) and invalidate the line
377 # if XCHAL_DCACHE_IS_WRITEBACK || XCHAL_DCACHE_LINE_LOCKABLE
379 // Uncorrectable error on a (writeback and/or lockable) data cache.
380 # if XCHAL_DCACHE_IS_WRITEBACK
381 // On tag errors we don't know whether the line is dirty, so this is unrecoverable:
382 bbsi.l a0, MESR_MEMTYPE_SHIFT+2, .L_uncorrectable_dtag // branch if tag error
383 // Castouts are by definition dirty, uncorrectable errors on these are unrecoverable:
384 bbsi.l a0, MESR_ACCTYPE_SHIFT, .L_uncorrectable_dirty // branch if castout
385 // Note: could still be an error on dirty dcache data, also unrecoverable.
387 bbsi.l a0, MESR_MEMTYPE_SHIFT+2, .L_dcache_tag_noncorr // branch if tag error
389 // Uncorrectable error in dcache data.
390 // May be dirty or locked, so get tag to find out.
392 # if XCHAL_DCACHE_WAYS > 1
393 extui a0, a0, MESR_WAYNUM_SHIFT, 2 // id of way with mem error
394 slli a0, a0, DCACHE_WAYWIDTH
395 xor a0, a2, a0 // xor corresponding bits of addr
396 extui a0, a0, DCACHE_WAYWIDTH, 2 // take 2 xor'ed way bits
397 or a2, a2, a0 // save them at bottom of addr
398 slli a0, a0, DCACHE_WAYWIDTH
399 xor a2, a2, a0 // and change 2 way bits of addr
401 ldct a0, a2 // get dcache tag
402 # if XCHAL_DCACHE_IS_WRITEBACK
403 bbsi.l a0, XCHAL_DCACHE_TAG_D_SHIFT, .L_uncorrectable_dirty_2 // branch if dirty
405 // Data cache line is clean.
406 # if XCHAL_DCACHE_LINE_LOCKABLE
407 bbsi.l a0, XCHAL_DCACHE_TAG_L_SHIFT, .L_dcache_nc_locked
409 // Data cache line is clean and unlocked. Just invalidate it.
410 // FIXME: any stores to this line by an NMI handler will be lost.
411 // On the other hand, if we use DHWBI, any stores by an NMI handler
412 // that don't happen to fix the error result in an unrecoverable castout.
414 # if XCHAL_ICACHE_WAYS > 1
415 // Recover virtual address in a2:
416 extui a0, a2, 0, 2 // get saved xor'ed bits at bottom
417 slli a0, a0, ICACHE_WAYWIDTH // line them up
418 xor a2, a2, a0 // restore original MEVADDR
420 dhi a2, 0 // invalidate that data cache line
424 # if XCHAL_DCACHE_LINE_LOCKABLE
426 # if XCHAL_ICACHE_WAYS > 1
427 // Recover virtual address in a2:
428 extui a0, a2, 0, 2 // get saved xor'ed bits at bottom
429 slli a0, a0, ICACHE_WAYWIDTH // line them up
430 xor a2, a2, a0 // restore original MEVADDR
432 // Unlock, invalidate, and relock it:
433 dhu a2, 0 // unlock that data cache line
434 dhi a2, 0 // invalidate that data cache line
435 dpfl a2, 0 // prefetch-and-lock the line again
440 # if XCHAL_DCACHE_IS_WRITEBACK
441 // Weak reference: if unresolved, links okay but with zero value:
442 .weak _xtos_merr_hook_uncor_dtag
443 .L_uncorrectable_dtag:
444 // Fatal (unrecoverable) error in dcache tag (maybe dirty): parity or uncorrectable ECC error
445 movi a0, _xtos_merr_hook_uncor_dtag
446 1: beqz a0, 1b // fatal non-corr dcache tag, no hook, so infinite loop
447 jx a0 // jump to user hook, if present
449 // Weak reference: if unresolved, links okay but with zero value:
450 .weak _xtos_merr_hook_uncor_dirty
451 .L_uncorrectable_dirty_2:
453 .L_uncorrectable_dirty:
454 // Fatal (unrecoverable) error, parity or non-correctable ECC error on dirty cache data
455 movi a0, _xtos_merr_hook_uncor_dirty
456 1: beqz a0, 1b // fatal non-corr dirty cache line, no hook, so infinite loop
457 jx a0 // jump to user hook, if present
459 .L_dcache_tag_noncorr:
460 // Uncorrectable error on a lockable writethru data cache tag.
461 // We have to invalidate the line, but that way we lose the lock bit.
462 // Provide a hook to relock if necessary (using knowledge outside this module
463 // about what needs to be locked). See _xtos_merr_hook_icache_relock for details.
464 // CAVEAT EMPTOR: this hook mechanism is subject to change.
465 .weak _xtos_merr_hook_dcache_relock // if unresolved, links with zero value
466 movi a0, _xtos_merr_hook_dcache_relock
467 1: beqz a0, 1b // if no hook to recover lock state on dcache tag mem error, loop forever
468 callx0 a0 // invoke user hook to relock d-cache (index in MEVADDR)
472 # endif /* DCACHE IS WRITEBACK || LINE_LOCKABLE */
474 # endif /* have DCACHE */
476 .size _MemErrorHandler, . - _MemErrorHandler
480 # endif /* splitting */
481 #endif /* XCHAL_HAVE_MEM_ECC_PARITY */