Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2001,2002,2003 Broadcom Corporation |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License |
| 6 | * as published by the Free Software Foundation; either version 2 |
| 7 | * of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, write to the Free Software |
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 17 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | |
| 19 | #include <asm/asm.h> |
| 20 | #include <asm/regdef.h> |
| 21 | #include <asm/mipsregs.h> |
| 22 | #include <asm/stackframe.h> |
| 23 | #include <asm/cacheops.h> |
| 24 | #include <asm/sibyte/board.h> |
| 25 | |
Ralf Baechle | 7034228 | 2013-01-22 12:59:30 +0100 | [diff] [blame] | 26 | #define C0_ERRCTL $26 /* CP0: Error info */ |
| 27 | #define C0_CERR_I $27 /* CP0: Icache error */ |
| 28 | #define C0_CERR_D $27,1 /* CP0: Dcache error */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | |
| 30 | /* |
| 31 | * Based on SiByte sample software cache-err/cerr.S |
| 32 | * CVS revision 1.8. Only the 'unrecoverable' case |
| 33 | * is changed. |
| 34 | */ |
| 35 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 | .set mips64 |
| 37 | .set noreorder |
| 38 | .set noat |
| 39 | |
| 40 | /* |
| 41 | * sb1_cerr_vec: code to be copied to the Cache Error |
| 42 | * Exception vector. The code must be pushed out to memory |
| 43 | * (either by copying to Kseg0 and Kseg1 both, or by flushing |
| 44 | * the L1 and L2) since it is fetched as 0xa0000100. |
| 45 | * |
| 46 | * NOTE: Be sure this handler is at most 28 instructions long |
| 47 | * since the final 16 bytes of the exception vector memory |
| 48 | * (0x170-0x17f) are used to preserve k0, k1, and ra. |
| 49 | */ |
| 50 | |
| 51 | LEAF(except_vec2_sb1) |
| 52 | /* |
| 53 | * If this error is recoverable, we need to exit the handler |
| 54 | * without having dirtied any registers. To do this, |
| 55 | * save/restore k0 and k1 from low memory (Useg is direct |
| 56 | * mapped while ERL=1). Note that we can't save to a |
| 57 | * CPU-specific location without ruining a register in the |
| 58 | * process. This means we are vulnerable to data corruption |
| 59 | * whenever the handler is reentered by a second CPU. |
| 60 | */ |
| 61 | sd k0,0x170($0) |
| 62 | sd k1,0x178($0) |
| 63 | |
Ralf Baechle | 51939fb | 2005-11-10 16:35:03 +0000 | [diff] [blame] | 64 | #ifdef CONFIG_SB1_CEX_ALWAYS_FATAL |
Andrew Isaacson | a4b5bd9 | 2005-10-19 23:57:40 -0700 | [diff] [blame] | 65 | j handle_vec2_sb1 |
| 66 | nop |
| 67 | #else |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 | /* |
| 69 | * M_ERRCTL_RECOVERABLE is bit 31, which makes it easy to tell |
| 70 | * if we can fast-path out of here for a h/w-recovered error. |
| 71 | */ |
| 72 | mfc0 k1,C0_ERRCTL |
| 73 | bgtz k1,attempt_recovery |
| 74 | sll k0,k1,1 |
| 75 | |
| 76 | recovered_dcache: |
| 77 | /* |
| 78 | * Unlock CacheErr-D (which in turn unlocks CacheErr-DPA). |
Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 79 | * Ought to log the occurrence of this recovered dcache error. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 | */ |
| 81 | b recovered |
| 82 | mtc0 $0,C0_CERR_D |
| 83 | |
| 84 | attempt_recovery: |
| 85 | /* |
| 86 | * k0 has C0_ERRCTL << 1, which puts 'DC' at bit 31. Any |
| 87 | * Dcache errors we can recover from will take more extensive |
Ralf Baechle | 7034228 | 2013-01-22 12:59:30 +0100 | [diff] [blame] | 88 | * processing. For now, they are considered "unrecoverable". |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | * Note that 'DC' becoming set (outside of ERL mode) will |
| 90 | * cause 'IC' to clear; so if there's an Icache error, we'll |
| 91 | * only find out about it if we recover from this error and |
| 92 | * continue executing. |
| 93 | */ |
| 94 | bltz k0,unrecoverable |
| 95 | sll k0,1 |
| 96 | |
| 97 | /* |
| 98 | * k0 has C0_ERRCTL << 2, which puts 'IC' at bit 31. If an |
| 99 | * Icache error isn't indicated, I'm not sure why we got here. |
| 100 | * Consider that case "unrecoverable" for now. |
| 101 | */ |
| 102 | bgez k0,unrecoverable |
| 103 | |
| 104 | attempt_icache_recovery: |
| 105 | /* |
| 106 | * External icache errors are due to uncorrectable ECC errors |
| 107 | * in the L2 cache or Memory Controller and cannot be |
| 108 | * recovered here. |
| 109 | */ |
| 110 | mfc0 k0,C0_CERR_I /* delay slot */ |
| 111 | li k1,1 << 26 /* ICACHE_EXTERNAL */ |
| 112 | and k1,k0 |
| 113 | bnez k1,unrecoverable |
| 114 | andi k0,0x1fe0 |
| 115 | |
| 116 | /* |
| 117 | * Since the error is internal, the 'IDX' field from |
| 118 | * CacheErr-I is valid and we can just invalidate all blocks |
| 119 | * in that set. |
| 120 | */ |
| 121 | cache Index_Invalidate_I,(0<<13)(k0) |
| 122 | cache Index_Invalidate_I,(1<<13)(k0) |
| 123 | cache Index_Invalidate_I,(2<<13)(k0) |
| 124 | cache Index_Invalidate_I,(3<<13)(k0) |
| 125 | |
| 126 | /* Ought to log this recovered icache error */ |
| 127 | |
| 128 | recovered: |
| 129 | /* Restore the saved registers */ |
| 130 | ld k0,0x170($0) |
| 131 | ld k1,0x178($0) |
| 132 | eret |
| 133 | |
| 134 | unrecoverable: |
| 135 | /* Unrecoverable Icache or Dcache error; log it and/or fail */ |
| 136 | j handle_vec2_sb1 |
| 137 | nop |
Andrew Isaacson | a4b5bd9 | 2005-10-19 23:57:40 -0700 | [diff] [blame] | 138 | #endif |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | |
| 140 | END(except_vec2_sb1) |
| 141 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 142 | LEAF(handle_vec2_sb1) |
| 143 | mfc0 k0,CP0_CONFIG |
| 144 | li k1,~CONF_CM_CMASK |
| 145 | and k0,k0,k1 |
| 146 | ori k0,k0,CONF_CM_UNCACHED |
| 147 | mtc0 k0,CP0_CONFIG |
| 148 | |
| 149 | SSNOP |
| 150 | SSNOP |
| 151 | SSNOP |
| 152 | SSNOP |
| 153 | bnezl $0, 1f |
| 154 | 1: |
| 155 | mfc0 k0, CP0_STATUS |
| 156 | sll k0, k0, 3 # check CU0 (kernel?) |
| 157 | bltz k0, 2f |
| 158 | nop |
| 159 | |
| 160 | /* Get a valid Kseg0 stack pointer. Any task's stack pointer |
| 161 | * will do, although if we ever want to resume execution we |
| 162 | * better not have corrupted any state. */ |
| 163 | get_saved_sp |
| 164 | move sp, k1 |
| 165 | |
| 166 | 2: |
| 167 | j sb1_cache_error |
| 168 | nop |
| 169 | |
| 170 | END(handle_vec2_sb1) |