Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * HPMC (High Priority Machine Check) handler. |
| 3 | * |
| 4 | * Copyright (C) 1999 Philipp Rumpf <prumpf@tux.org> |
| 5 | * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) |
| 6 | * Copyright (C) 2000 Hewlett-Packard (John Marvin) |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; either version 2, or (at your option) |
| 11 | * any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program; if not, write to the Free Software |
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 21 | */ |
| 22 | |
| 23 | |
| 24 | /* |
| 25 | * This HPMC handler retrieves the HPMC pim data, resets IO and |
| 26 | * returns to the default trap handler with code set to 1 (HPMC). |
| 27 | * The default trap handler calls handle interruption, which |
| 28 | * does a stack and register dump. This at least allows kernel |
| 29 | * developers to get back to C code in virtual mode, where they |
| 30 | * have the option to examine and print values from memory that |
| 31 | * would help in debugging an HPMC caused by a software bug. |
| 32 | * |
| 33 | * There is more to do here: |
| 34 | * |
| 35 | * 1) On MP systems we need to synchronize processors |
| 36 | * before calling pdc/iodc. |
| 37 | * 2) We should be checking the system state and not |
| 38 | * returning to the fault handler if things are really |
| 39 | * bad. |
| 40 | * |
| 41 | */ |
| 42 | |
| 43 | .level 1.1 |
| 44 | .data |
| 45 | |
| 46 | #include <asm/assembly.h> |
| 47 | #include <asm/pdc.h> |
| 48 | |
Helge Deller | 8e9e984 | 2007-01-24 22:36:32 +0100 | [diff] [blame] | 49 | #include <linux/linkage.h> |
| 50 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | /* |
| 52 | * stack for os_hpmc, the HPMC handler. |
| 53 | * buffer for IODC procedures (for the HPMC handler). |
| 54 | * |
| 55 | * IODC requires 7K byte stack. That leaves 1K byte for os_hpmc. |
| 56 | */ |
| 57 | |
| 58 | .align 4096 |
| 59 | hpmc_stack: |
| 60 | .block 16384 |
| 61 | |
| 62 | #define HPMC_IODC_BUF_SIZE 0x8000 |
| 63 | |
| 64 | .align 4096 |
| 65 | hpmc_iodc_buf: |
| 66 | .block HPMC_IODC_BUF_SIZE |
| 67 | |
| 68 | .align 8 |
| 69 | hpmc_raddr: |
| 70 | .block 128 |
| 71 | |
| 72 | #define HPMC_PIM_DATA_SIZE 896 /* Enough to hold all architected 2.0 state */ |
| 73 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 74 | .align 8 |
Helge Deller | 8e9e984 | 2007-01-24 22:36:32 +0100 | [diff] [blame] | 75 | ENTRY(hpmc_pim_data) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 | .block HPMC_PIM_DATA_SIZE |
Helge Deller | 8e9e984 | 2007-01-24 22:36:32 +0100 | [diff] [blame] | 77 | END(hpmc_pim_data) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | |
| 79 | .text |
| 80 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 81 | .import intr_save, code |
Helge Deller | 8e9e984 | 2007-01-24 22:36:32 +0100 | [diff] [blame] | 82 | ENTRY(os_hpmc) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | |
| 84 | /* |
| 85 | * registers modified: |
| 86 | * |
| 87 | * Using callee saves registers without saving them. The |
| 88 | * original values are in the pim dump if we need them. |
| 89 | * |
| 90 | * r2 (rp) return pointer |
| 91 | * r3 address of PDCE_PROC |
| 92 | * r4 scratch |
| 93 | * r5 scratch |
| 94 | * r23 (arg3) procedure arg |
| 95 | * r24 (arg2) procedure arg |
| 96 | * r25 (arg1) procedure arg |
| 97 | * r26 (arg0) procedure arg |
| 98 | * r30 (sp) stack pointer |
| 99 | * |
| 100 | * registers read: |
| 101 | * |
| 102 | * r26 contains address of PDCE_PROC on entry |
| 103 | * r28 (ret0) return value from procedure |
| 104 | */ |
| 105 | |
| 106 | copy arg0, %r3 /* save address of PDCE_PROC */ |
| 107 | |
| 108 | /* |
| 109 | * disable nested HPMCs |
| 110 | * |
| 111 | * Increment os_hpmc checksum to invalidate it. |
| 112 | * Do this before turning the PSW M bit off. |
| 113 | */ |
| 114 | |
| 115 | mfctl %cr14, %r4 |
| 116 | ldw 52(%r4),%r5 |
| 117 | addi 1,%r5,%r5 |
| 118 | stw %r5,52(%r4) |
| 119 | |
| 120 | /* MP_FIXME: synchronize all processors. */ |
| 121 | |
| 122 | /* Setup stack pointer. */ |
| 123 | |
| 124 | load32 PA(hpmc_stack),sp |
| 125 | |
| 126 | ldo 128(sp),sp /* leave room for arguments */ |
| 127 | |
| 128 | /* |
| 129 | * Most PDC routines require that the M bit be off. |
| 130 | * So turn on the Q bit and turn off the M bit. |
| 131 | */ |
| 132 | |
| 133 | ldo 8(%r0),%r4 /* PSW Q on, PSW M off */ |
| 134 | mtctl %r4,ipsw |
| 135 | mtctl %r0,pcsq |
| 136 | mtctl %r0,pcsq |
| 137 | load32 PA(os_hpmc_1),%r4 |
| 138 | mtctl %r4,pcoq |
| 139 | ldo 4(%r4),%r4 |
| 140 | mtctl %r4,pcoq |
| 141 | rfi |
| 142 | nop |
| 143 | |
| 144 | os_hpmc_1: |
| 145 | |
| 146 | /* Call PDC_PIM to get HPMC pim info */ |
| 147 | |
| 148 | /* |
| 149 | * Note that on some newer boxes, PDC_PIM must be called |
| 150 | * before PDC_IO if you want IO to be reset. PDC_PIM sets |
| 151 | * a flag that PDC_IO examines. |
| 152 | */ |
| 153 | |
| 154 | ldo PDC_PIM(%r0), arg0 |
| 155 | ldo PDC_PIM_HPMC(%r0),arg1 /* Transfer HPMC data */ |
| 156 | load32 PA(hpmc_raddr),arg2 |
| 157 | load32 PA(hpmc_pim_data),arg3 |
| 158 | load32 HPMC_PIM_DATA_SIZE,%r4 |
| 159 | stw %r4,-52(sp) |
| 160 | |
| 161 | ldil L%PA(os_hpmc_2), rp |
| 162 | bv (r3) /* call pdce_proc */ |
| 163 | ldo R%PA(os_hpmc_2)(rp), rp |
| 164 | |
| 165 | os_hpmc_2: |
| 166 | comib,<> 0,ret0, os_hpmc_fail |
| 167 | |
| 168 | /* Reset IO by calling the hversion dependent PDC_IO routine */ |
| 169 | |
| 170 | ldo PDC_IO(%r0),arg0 |
| 171 | ldo 0(%r0),arg1 /* log IO errors */ |
| 172 | ldo 0(%r0),arg2 /* reserved */ |
| 173 | ldo 0(%r0),arg3 /* reserved */ |
| 174 | stw %r0,-52(sp) /* reserved */ |
| 175 | |
| 176 | ldil L%PA(os_hpmc_3),rp |
| 177 | bv (%r3) /* call pdce_proc */ |
| 178 | ldo R%PA(os_hpmc_3)(rp),rp |
| 179 | |
| 180 | os_hpmc_3: |
| 181 | |
| 182 | /* FIXME? Check for errors from PDC_IO (-1 might be OK) */ |
| 183 | |
| 184 | /* |
| 185 | * Initialize the IODC console device (HPA,SPA, path etc. |
| 186 | * are stored on page 0. |
| 187 | */ |
| 188 | |
| 189 | /* |
| 190 | * Load IODC into hpmc_iodc_buf by calling PDC_IODC. |
| 191 | * Note that PDC_IODC handles flushing the appropriate |
| 192 | * data and instruction cache lines. |
| 193 | */ |
| 194 | |
| 195 | ldo PDC_IODC(%r0),arg0 |
| 196 | ldo PDC_IODC_READ(%r0),arg1 |
| 197 | load32 PA(hpmc_raddr),arg2 |
| 198 | ldw BOOT_CONSOLE_HPA_OFFSET(%r0),arg3 /* console hpa */ |
| 199 | ldo PDC_IODC_RI_INIT(%r0),%r4 |
| 200 | stw %r4,-52(sp) |
| 201 | load32 PA(hpmc_iodc_buf),%r4 |
| 202 | stw %r4,-56(sp) |
| 203 | load32 HPMC_IODC_BUF_SIZE,%r4 |
| 204 | stw %r4,-60(sp) |
| 205 | |
| 206 | ldil L%PA(os_hpmc_4),rp |
| 207 | bv (%r3) /* call pdce_proc */ |
| 208 | ldo R%PA(os_hpmc_4)(rp),rp |
| 209 | |
| 210 | os_hpmc_4: |
| 211 | comib,<> 0,ret0,os_hpmc_fail |
| 212 | |
| 213 | /* Call the entry init (just loaded by PDC_IODC) */ |
| 214 | |
| 215 | ldw BOOT_CONSOLE_HPA_OFFSET(%r0),arg0 /* console hpa */ |
| 216 | ldo ENTRY_INIT_MOD_DEV(%r0), arg1 |
| 217 | ldw BOOT_CONSOLE_SPA_OFFSET(%r0),arg2 /* console spa */ |
| 218 | depi 0,31,11,arg2 /* clear bits 21-31 */ |
| 219 | ldo BOOT_CONSOLE_PATH_OFFSET(%r0),arg3 /* console path */ |
| 220 | load32 PA(hpmc_raddr),%r4 |
| 221 | stw %r4, -52(sp) |
| 222 | stw %r0, -56(sp) /* HV */ |
| 223 | stw %r0, -60(sp) /* HV */ |
| 224 | stw %r0, -64(sp) /* HV */ |
| 225 | stw %r0, -68(sp) /* lang, must be zero */ |
| 226 | |
| 227 | load32 PA(hpmc_iodc_buf),%r5 |
| 228 | ldil L%PA(os_hpmc_5),rp |
| 229 | bv (%r5) |
| 230 | ldo R%PA(os_hpmc_5)(rp),rp |
| 231 | |
| 232 | os_hpmc_5: |
| 233 | comib,<> 0,ret0,os_hpmc_fail |
| 234 | |
| 235 | /* Prepare to call intr_save */ |
| 236 | |
| 237 | /* |
| 238 | * Load kernel page directory (load into user also, since |
| 239 | * we don't intend to ever return to user land anyway) |
| 240 | */ |
| 241 | |
| 242 | load32 PA(swapper_pg_dir),%r4 |
| 243 | mtctl %r4,%cr24 /* Initialize kernel root pointer */ |
| 244 | mtctl %r4,%cr25 /* Initialize user root pointer */ |
| 245 | |
| 246 | /* Clear sr4-sr7 */ |
| 247 | |
| 248 | mtsp %r0, %sr4 |
| 249 | mtsp %r0, %sr5 |
| 250 | mtsp %r0, %sr6 |
| 251 | mtsp %r0, %sr7 |
| 252 | |
| 253 | tovirt_r1 %r30 /* make sp virtual */ |
| 254 | |
| 255 | rsm 8,%r0 /* Clear Q bit */ |
| 256 | ldi 1,%r8 /* Set trap code to "1" for HPMC */ |
| 257 | load32 PA(intr_save),%r1 |
| 258 | be 0(%sr7,%r1) |
| 259 | nop |
| 260 | |
| 261 | os_hpmc_fail: |
| 262 | |
| 263 | /* |
| 264 | * Reset the system |
| 265 | * |
| 266 | * Some systems may lockup from a broadcast reset, so try the |
| 267 | * hversion PDC_BROADCAST_RESET() first. |
| 268 | * MP_FIXME: reset all processors if more than one central bus. |
| 269 | */ |
| 270 | |
| 271 | /* PDC_BROADCAST_RESET() */ |
| 272 | |
| 273 | ldo PDC_BROADCAST_RESET(%r0),arg0 |
| 274 | ldo 0(%r0),arg1 /* do reset */ |
| 275 | |
| 276 | ldil L%PA(os_hpmc_6),rp |
| 277 | bv (%r3) /* call pdce_proc */ |
| 278 | ldo R%PA(os_hpmc_6)(rp),rp |
| 279 | |
| 280 | os_hpmc_6: |
| 281 | |
| 282 | /* |
| 283 | * possible return values: |
| 284 | * -1 non-existent procedure |
| 285 | * -2 non-existent option |
| 286 | * -16 unaligned stack |
| 287 | * |
| 288 | * If call returned, do a broadcast reset. |
| 289 | */ |
| 290 | |
| 291 | ldil L%0xfffc0000,%r4 /* IO_BROADCAST */ |
| 292 | ldo 5(%r0),%r5 |
| 293 | stw %r5,48(%r4) /* CMD_RESET to IO_COMMAND offset */ |
| 294 | |
| 295 | b . |
| 296 | nop |
Helge Deller | 8e9e984 | 2007-01-24 22:36:32 +0100 | [diff] [blame] | 297 | ENDPROC(os_hpmc) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 298 | |
| 299 | /* this label used to compute os_hpmc checksum */ |
Helge Deller | 8e9e984 | 2007-01-24 22:36:32 +0100 | [diff] [blame] | 300 | ENTRY(os_hpmc_end) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 301 | |
| 302 | nop |