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