| // |
| // mem_ecc_parity.S - utility routines for the local memory ECC/parity option |
| // (memory error checking and exceptions) |
| // |
| // $Id: //depot/rel/Eaglenest/Xtensa/OS/hal/mem_ecc_parity.S#1 $ |
| |
| // Copyright (c) 2006-2010 Tensilica Inc. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining |
| // a copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to |
| // permit persons to whom the Software is furnished to do so, subject to |
| // the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| #include <xtensa/coreasm.h> |
| |
| |
| /* |
| * For most functions, the link-time HAL defines two entry points: |
| * xthal_...() and xthal_..._nw(). The former is the main entry point |
| * invoked from C code, or assembly code that follows the C ABI. |
| * The latter is for use in assembly code that cannot easily follow |
| * all the requirements of the windowed ABI, e.g. in exception handlers; |
| * these use the call0 ABI instead (in most cases; some use their own conventions). |
| * |
| * When software tools are configured to use the call0 ABI, both variants |
| * are identical (with some exceptions as noted). To avoid duplicating |
| * code, we define both labels for the same function body. The Makefile |
| * defines __SPLIT__..._nw macros with windowed ABI but not with Call0 ABI. |
| * Use SYM_NW() for the _nw variants defined with the __SPLIT_..._nw macros, |
| * i.e. for call0 ABI variants when windowed ABI is in use; these are not |
| * C callable so SYM_NW() does not specify .type information. |
| * Use SYMBOL() otherwise, which defines both symbols if call0 ABI is selected. |
| */ |
| |
| #if defined (__XTENSA_CALL0_ABI__) |
| # define SYMBOL(x) .global x ; .type x,@function ; \ |
| .global x ## _nw ; .type x ## _nw,@function ; \ |
| .align 4 ; x: ; x ## _nw: |
| #else |
| # define SYMBOL(x) .global x ; .type x,@function ; .align 4 ; x: |
| #endif |
| #define SYM_NW(x) .global x ; .align 4 ; x: |
| |
| |
| /* Compute smaller of I and D cache line sizes: */ |
| #if XCHAL_ICACHE_LINEWIDTH < XCHAL_DCACHE_LINEWIDTH && XCHAL_ICACHE_SIZE > 0 |
| # define CACHE_LINEWIDTH_MIN XCHAL_ICACHE_LINEWIDTH |
| # define CACHE_LINESIZE_MIN XCHAL_ICACHE_LINESIZE |
| #else |
| # define CACHE_LINEWIDTH_MIN XCHAL_DCACHE_LINEWIDTH |
| # define CACHE_LINESIZE_MIN XCHAL_DCACHE_LINESIZE |
| #endif |
| |
| |
| .text |
| |
| //------------------------------------------------------------------------ |
| // Inject errors into instruction and/or data RAMs, or cache data or tags |
| //------------------------------------------------------------------------ |
| |
| #if defined(__SPLIT__memep_inject_error) |
| |
| // void xthal_memep_inject_error(void *addr, int size, int flags); |
| // where: |
| // addr (a2) pointer to local memory, or cache address |
| // size (a3) size in bytes (gets aligned to words or lines) |
| // flags (a4) is a combination of the following bits: |
| // bit 31-5: (reserved) |
| // bit 4: 0 = inject non-correctable error, |
| // 16 = inject correctable error (if ECC) |
| // bit 3: (reserved) |
| // bit 2: 0 = local memory, 4 = cache |
| // bit 1: 0 = data cache, 2 = instruction cache |
| // bit 0: 0 = cache data, 1 = cache tag |
| // |
| // (note: data cache data is handled same as local memories; |
| // to access specific dcache data entries, you have to setup |
| // a region or page in cache-isolate mode yourself) |
| |
| SYMBOL(xthal_memep_inject_error) |
| abi_entry |
| |
| #if XCHAL_HAVE_MEM_ECC_PARITY |
| |
| // These MOVIs may be L32Rs, load them before enabling test mode: |
| movi a6, 0x02020202 // XOR'ing this creates a correctable error |
| bbsi.l a4, 4, 1f // branch if correctable error requested |
| movi a6, 0x03030303 // XOR'ing this creates a non-correctable error |
| 1: |
| |
| // Lock out all interrupts, to avoid interrupt handlers running with |
| // test mode enabled (corrupting their stores, likely leading to |
| // non-correctable memory errors). |
| // |
| // If NMI is possible, you're toast |
| // (no stores during NMI handler will have properly computed ECC/parity bits) |
| // although you might make the NMI handler check MESR.ERRTEST and save/clear |
| // it if it's set on entry, so that its stores work correctly. |
| // |
| // If memory exceptions are possible, might be okay as long as the |
| // handler checks whether test mode is on, and turns it off temporarily |
| // to do its work. |
| // |
| # if XCHAL_HAVE_INTERRUPTS |
| rsil a11, 15 |
| # endif |
| |
| // Save current MESR and set test mode: |
| |
| rsr a8, MESR |
| bbsi.l a8, MESR_ERRTEST_SHIFT, .Lproceed // already in test mode? |
| addmi a9, a8, MESR_ERRTEST // enable test mode |
| bbci.l a8, MESR_ERRENAB_SHIFT, 1f |
| addmi a9, a9, - MESR_ERRENAB // disable error checks |
| 1: xsr a9, MESR |
| beq a8, a9, .Lproceed // clean update, continue |
| bbci.l a9, MESR_RCE_SHIFT, .Lproceed // we likely restored a lost RCE, just keep it |
| // At this point, either we: |
| // a) cleared an RCE record that got created between RSR and XSR |
| // b) cleared LCE bits that got set between RSR and XSR |
| // c) more eclectic, and presumably much less likely, cases of |
| // RCE/LCE bits being cleared and set again between RSR and XSR |
| // due to multiple memory errors and memory error exceptions |
| // in that period; for now, we ignore this possibility |
| // (decreasing returns on addressing these arbitrarily complex cases) |
| // Assuming (a) or (b), restore the bits we took away. |
| //addmi a8, a8, MESR_ERRTEST |
| addmi a9, a9, MESR_ERRTEST |
| bbci.l a9, MESR_ERRENAB_SHIFT, 1f |
| addmi a9, a9, - MESR_ERRENAB // disable error checks |
| 1: wsr a9, MESR |
| //xsr a9, MESR |
| //beq a8, a9, .Lproceed // updated fine, continue |
| // |
| // Above we could have used XSR instead of WSR. |
| // However, it's not clear at this point what's the cleanest thing |
| // to do if what we read back doesn't match what we expected, |
| // because at that point we have multiple errors to deal with. |
| // Unless we have code here to handle (fix and/or log) these errors, |
| // we have to chuck something away or write a bunch more code to |
| // handle another LCE bit getting set etc (also starting to be |
| // a low probability occurrence). |
| .Lproceed: |
| // Test mode enabled. From this point until we restore MESR, |
| // the only loads and stores done are for injecting errors. |
| |
| # if XCHAL_ICACHE_SIZE || XCHAL_DCACHE_SIZE |
| bbci.l a4, 2, .L_inject_local // branch if injecting to local memory |
| bbsi.l a4, 1, .L_inject_icache // branch if injecting to icache |
| // Inject errors in dcache: |
| bbci.l a4, 0, .L_inject_local // branch if injecting to dcache data |
| # if XCHAL_DCACHE_SIZE && XCHAL_HAVE_DCACHE_TEST |
| // Inject errors in dcache tags: |
| |
| // Round addr/size to fully rather than partially cover |
| // all aligned cache lines: |
| extui a9, a2, 0, XCHAL_DCACHE_LINEWIDTH |
| sub a2, a2, a9 |
| add a3, a3, a9 |
| addi a3, a3, XCHAL_DCACHE_LINESIZE-1 |
| srli a3, a3, XCHAL_DCACHE_LINEWIDTH // size in cache lines |
| |
| floopgtz a3, .Ldctagloop |
| ldct a9, a2 // load dcache line tag |
| rsr a7, MECR // get check bits |
| xor a7, a7, a6 // ECC: single-bit error; Parity: NO-OP |
| wsr a7, MECR // setup modified check bits |
| sdct a9, a2 // store tag with modified check bits |
| addi a2, a2, XCHAL_DCACHE_LINESIZE // increment to next line |
| floopend a3, .Ldctagloop |
| # endif /* have dcache */ |
| j .L_inject_done |
| |
| // Inject errors in icache: |
| .L_inject_icache: |
| # if XCHAL_ICACHE_SIZE && XCHAL_HAVE_ICACHE_TEST |
| bbci.l a4, 0, .L_inject_icw // branch if injecting to icache data |
| |
| // Inject errors in icache tags: |
| // Round addr/size to fully rather than partially cover |
| // all aligned cache lines: |
| extui a9, a2, 0, XCHAL_ICACHE_LINEWIDTH |
| sub a2, a2, a9 |
| add a3, a3, a9 |
| addi a3, a3, XCHAL_ICACHE_LINESIZE-1 |
| srli a3, a3, XCHAL_ICACHE_LINEWIDTH // size in cache lines |
| |
| floopgtz a3, .Lictagloop |
| lict a9, a2 // load icache line tag |
| rsr a7, MECR // get check bits |
| xor a7, a7, a6 // ECC: single-bit error; Parity: NO-OP |
| wsr a7, MECR // setup modified check bits |
| sict a9, a2 // store tag with modified check bits |
| addi a2, a2, XCHAL_ICACHE_LINESIZE // increment to next line |
| floopend a3, .Lictagloop |
| j .L_inject_done |
| |
| .L_inject_icw: |
| # if XCHAL_ICACHE_ACCESS_SIZE <= 4 /* SICW does not work usefully (replicates data) if accessWidth > 32 bits */ |
| // Inject errors in icache data words: |
| // Round addr/size to fully rather than partially cover |
| // all aligned 32-bit words: |
| extui a9, a2, 0, 2 |
| sub a2, a2, a9 |
| add a3, a3, a9 |
| addi a3, a3, 3 |
| srli a3, a3, 2 // size in words |
| |
| floopgtz a3, .Licwloop |
| licw a9, a2 // load word of icache line data |
| rsr a7, MECR // get check bits |
| xor a7, a7, a6 // ECC: single-bit error; Parity: NO-OP |
| wsr a7, MECR // setup modified check bits |
| sicw a9, a2 // store data with modified check bits |
| addi a2, a2, 4 // increment to next word |
| floopend a3, .Licwloop |
| # endif |
| # endif /* have icache */ |
| j .L_inject_done |
| # endif /* have icache or dcache */ |
| |
| .L_inject_local: |
| // Round addr/size to fully rather than partially cover |
| // all aligned 32-bit words: |
| extui a9, a2, 0, 2 |
| sub a2, a2, a9 |
| add a3, a3, a9 |
| addi a3, a3, 3 |
| srli a3, a3, 2 // size in words |
| |
| floopgtz a3, .Lendloop |
| l32i a9, a2, 0 // load data |
| rsr a7, MECR // get check bits |
| xor a7, a7, a6 // ECC: single-bit error; Parity: NO-OP |
| wsr a7, MECR // setup modified check bits |
| s32i a9, a2, 0 // store data with modified check bits |
| addi a2, a2, 4 // increment to next word |
| floopend a3, .Lendloop |
| |
| .L_inject_done: |
| // Restore MESR (a8 is the saved original MESR): |
| bbsi.l a8, MESR_ERRTEST_SHIFT, 2f // was already in test mode |
| rsr a6, MESR |
| addmi a9, a6, - MESR_ERRTEST // disable test mode |
| bbci.l a8, MESR_ERRENAB_SHIFT, 1f |
| addmi a9, a9, MESR_ERRENAB // enable error checks |
| 1: xsr a9, MESR |
| beq a6, a9, 2f // clean update, done |
| bbci.l a9, MESR_RCE_SHIFT, 2f // we likely restored a lost RCE, just keep it |
| addmi a9, a9, - MESR_ERRTEST |
| bbci.l a8, MESR_ERRENAB_SHIFT, 1f |
| addmi a9, a9, MESR_ERRENAB // disable error checks |
| 1: wsr a9, MESR |
| 2: |
| |
| // Restore PS.INTLEVEL: |
| # if XCHAL_HAVE_INTERRUPTS |
| wsr a11, PS |
| rsync |
| # endif |
| #endif /* XCHAL_HAVE_MEM_ECC_PARITY */ |
| |
| abi_return |
| |
| .size xthal_memep_inject_error, . - xthal_memep_inject_error |
| |
| |
| #endif /*split*/ |
| |
| //---------------------------------------------------------------------- |
| |