| 1 | |
| 2 | |
| 3 | * Machine check handling for Tsunami/Typhoon systems.
|
| 4 | * Author: Magnus Lindholm <[email protected]>
|
| 5 | * Based on Titan error handling by Jeff Wiedemeier (Compaq).
|
| 6 | * Register definitions: 21272 Hardware Reference Manual, revision 4.0.
|
| 7 |
|
| 8 | #include <linux/init.h>
|
| 9 | #include <linux/printk.h>
|
| 10 | #include <linux/pci.h>
|
| 11 | #include <linux/sched.h>
|
| 12 | #include <asm/io.h>
|
| 13 | #include <asm/hwrpb.h>
|
| 14 | #include <asm/core_tsunami.h>
|
| 15 | #include <asm/smp.h>
|
| 16 | #include <asm/err_common.h>
|
| 17 | #include <asm/irq_regs.h>
|
| 18 | #include "err_impl.h"
|
| 19 | #include "proto.h"
|
| 20 | #include "pci_impl.h"
|
| 21 |
|
| 22 | #define TSUNAMI_PERROR_ERRMASK 0xdffUL
|
| 23 | #define TSUNAMI_PERROR_ECCMASK (perror_m_uecc | perror_m_cre)
|
| 24 | #define TSUNAMI_PERROR_INV (1UL << 51)
|
| 25 | #define TSUNAMI_MISC_NXM (1UL << 28)
|
| 26 |
|
| 27 | static int
|
| 28 | tsunami_frame_valid(struct el_common *header, size_t size)
|
| 29 | {
|
| 30 | return !((header->sys_offset | header->size) & 7) &&
|
| 31 | header->sys_offset >= sizeof(*header) &&
|
| 32 | header->sys_offset <= header->size &&
|
| 33 | size <= header->size - header->sys_offset;
|
| 34 | }
|
| 35 |
|
| 36 | static int
|
| 37 | tsunami_parse_p_perror(int which, u64 perror, int print)
|
| 38 | {
|
| 39 | static const char * const errors[] = {
|
| 40 | "Lost error", "SERR asserted", "PERR asserted",
|
| 41 | "Delayed-completion retry timeout", "Invalid scatter-gather PTE",
|
| 42 | "Address parity error", "Target abort", "Read data parity error",
|
| 43 | "No DEVSEL (master abort)", NULL,
|
| 44 | "Uncorrectable ECC error", "Correctable ECC error"
|
| 45 | };
|
| 46 | static const char * const pci_cmd[] = {
|
| 47 | "Interrupt acknowledge", "Special cycle", "I/O read", "I/O write",
|
| 48 | "Reserved", "Reserved", "Memory read", "Memory write",
|
| 49 | "Reserved", "Reserved", "Configuration read", "Configuration write",
|
| 50 | "Memory read multiple", "Dual address cycle",
|
| 51 | "Memory read line", "Memory write and invalidate"
|
| 52 | };
|
| 53 | unsigned long addr;
|
| 54 | unsigned int cmd;
|
| 55 | int i;
|
| 56 |
|
| 57 | if (!(perror & TSUNAMI_PERROR_ERRMASK))
|
| 58 | return MCHK_DISPOSITION_UNKNOWN_ERROR;
|
| 59 |
|
| 60 | if (!print)
|
| 61 | return MCHK_DISPOSITION_REPORT;
|
| 62 |
|
| 63 | printk("%s Pchip %d PERROR: %016llx\n", err_print_prefix, which, perror);
|
| 64 | for (i = 0; i < ARRAY_SIZE(errors); i++)
|
| 65 | if ((perror & TSUNAMI_PERROR_ERRMASK & (1UL << i)) && errors[i])
|
| 66 | printk("%s %s\n", err_print_prefix, errors[i]);
|
| 67 |
|
| 68 | |
| 69 | * INV invalidates SYN, CMD and ADDR. HRM 8.8.2.6 leaves command/address
|
| 70 | * undefined whenever SERR is set, including with other error bits.
|
| 71 |
|
| 72 | if (perror & (TSUNAMI_PERROR_INV | perror_m_serr)) {
|
| 73 | printk("%s Transaction information unavailable\n", err_print_prefix);
|
| 74 | return MCHK_DISPOSITION_REPORT;
|
| 75 | }
|
| 76 | cmd = (perror & perror_m_cmd) >> 52;
|
| 77 | |
| 78 | * Table 10-42 selects the ECC fields whenever CRE or UECC is set.
|
| 79 | * The first error freezes PERROR (10.2.5.6); mixed error bits were
|
| 80 | * detected together. Only LOST can be set afterwards.
|
| 81 |
|
| 82 | if (perror & TSUNAMI_PERROR_ECCMASK) {
|
| 83 | const char *command = "Reserved";
|
| 84 |
|
| 85 | if (cmd == 0)
|
| 86 | command = "DMA read";
|
| 87 | else if (cmd == 1)
|
| 88 | command = "DMA RMW";
|
| 89 | else if (cmd == 3)
|
| 90 | command = "SGTE read";
|
| 91 | addr = ((perror >> 19) & 0xffffffffUL) << 3;
|
| 92 | printk("%s Command: %s (%x), syndrome: %02llx\n",
|
| 93 | err_print_prefix, command, cmd, (perror & perror_m_syn) >> 56);
|
| 94 | printk("%s System address: %lx\n", err_print_prefix, addr);
|
| 95 | } else {
|
| 96 | addr = ((perror >> 18) & 0x3fffffffUL) << 2;
|
| 97 |
|
| 98 | if (perror & perror_m_sge)
|
| 99 | addr &= ~0x1fffUL;
|
| 100 | printk("%s Command: %s (%x), PCI address: %lx%s\n",
|
| 101 | err_print_prefix, pci_cmd[cmd], cmd, addr,
|
| 102 | (perror & perror_m_sge) ? " (8KB page)" : "");
|
| 103 | if (perror & (1UL << 17))
|
| 104 | printk("%s Monster window\n", err_print_prefix);
|
| 105 | else if (perror & (1UL << 16))
|
| 106 | printk("%s DAC scatter-gather window 3\n", err_print_prefix);
|
| 107 | }
|
| 108 | return MCHK_DISPOSITION_REPORT;
|
| 109 | }
|
| 110 |
|
| 111 |
|
| 112 | static u64
|
| 113 | tsunami_pchip_error(struct el_TSUNAMI_sysdata_mcheck *frame, int which)
|
| 114 | {
|
| 115 | u64 perror = which ? frame->p1_perror : frame->p0_perror;
|
| 116 |
|
| 117 | if (frame->sesf & (1UL << which))
|
| 118 | return perror;
|
| 119 | if ((frame->sesf & 4) && (perror & TSUNAMI_PERROR_ECCMASK))
|
| 120 | return perror;
|
| 121 | return 0;
|
| 122 | }
|
| 123 |
|
| 124 | static struct el_TSUNAMI_sysdata_mcheck *
|
| 125 | tsunami_system_frame(struct el_common *header)
|
| 126 | {
|
| 127 | if (!tsunami_frame_valid(header, sizeof(struct el_TSUNAMI_sysdata_mcheck)))
|
| 128 | return NULL;
|
| 129 | return (void *)header + header->sys_offset;
|
| 130 | }
|
| 131 |
|
| 132 | static int
|
| 133 | tsunami_process_logout_frame(struct el_common *header, int print)
|
| 134 | {
|
| 135 | struct el_TSUNAMI_sysdata_mcheck *frame = tsunami_system_frame(header);
|
| 136 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR;
|
| 137 | int i;
|
| 138 |
|
| 139 | if (!frame) {
|
| 140 | if (print) {
|
| 141 | printk("%s Invalid Tsunami system frame; raw frame follows\n",
|
| 142 | err_print_prefix);
|
| 143 | mchk_dump_logout_frame_raw(header);
|
| 144 | }
|
| 145 | return MCHK_DISPOSITION_UNKNOWN_ERROR;
|
| 146 | }
|
| 147 | if (print)
|
| 148 | printk("%s SESF: %016llx DIR: %016llx MISC: %016llx\n",
|
| 149 | err_print_prefix, frame->sesf, frame->dir, frame->misc);
|
| 150 | if (frame->misc & TSUNAMI_MISC_NXM) {
|
| 151 | status = MCHK_DISPOSITION_REPORT;
|
| 152 | if (print)
|
| 153 | printk("%s Non-existent memory, NXS: %llu\n",
|
| 154 | err_print_prefix, (frame->misc >> 29) & 7);
|
| 155 | }
|
| 156 | for (i = 0; i < 2; i++)
|
| 157 | status |= tsunami_parse_p_perror(i, tsunami_pchip_error(frame, i), print);
|
| 158 | return status;
|
| 159 | }
|
| 160 |
|
| 161 | struct tsunami_error_state {
|
| 162 | u64 perror[2];
|
| 163 | u64 misc;
|
| 164 | };
|
| 165 |
|
| 166 | static void
|
| 167 | tsunami_ack_pchip(tsunami_pchip *pchip, u64 perror)
|
| 168 | {
|
| 169 | if (!(perror & TSUNAMI_PERROR_ERRMASK))
|
| 170 | return;
|
| 171 | pchip->perror.csr = perror & TSUNAMI_PERROR_ERRMASK;
|
| 172 |
|
| 173 | mb();
|
| 174 | pchip->perror.csr;
|
| 175 | }
|
| 176 |
|
| 177 | static void
|
| 178 | tsunami_clear_errors(struct tsunami_error_state *state)
|
| 179 | {
|
| 180 | bool pchip1 = TSUNAMI_cchip->csc.csr & (1UL << 14);
|
| 181 |
|
| 182 |
|
| 183 | state->perror[0] = TSUNAMI_pchip0->perror.csr;
|
| 184 | state->perror[1] = pchip1 ? TSUNAMI_pchip1->perror.csr : 0;
|
| 185 | state->misc = TSUNAMI_cchip->misc.csr;
|
| 186 |
|
| 187 | |
| 188 | * Acknowledge both present Pchips: the saved frame may predate
|
| 189 | * live errors. This can consume another chip's pending error
|
| 190 | * with INV set before it reaches DRIR, losing its transaction
|
| 191 | * details and subsequent system-error notification.
|
| 192 |
|
| 193 | tsunami_ack_pchip(TSUNAMI_pchip0, state->perror[0]);
|
| 194 |
|
| 195 | if (pchip1)
|
| 196 | tsunami_ack_pchip(TSUNAMI_pchip1, state->perror[1]);
|
| 197 | if (state->misc & TSUNAMI_MISC_NXM) {
|
| 198 |
|
| 199 | TSUNAMI_cchip->misc.csr = TSUNAMI_MISC_NXM;
|
| 200 |
|
| 201 | mb();
|
| 202 | TSUNAMI_cchip->misc.csr;
|
| 203 | }
|
| 204 | }
|
| 205 |
|
| 206 | static void
|
| 207 | tsunami_report_live_errors(struct tsunami_error_state *state,
|
| 208 | struct el_TSUNAMI_sysdata_mcheck *frame)
|
| 209 | {
|
| 210 | int i;
|
| 211 |
|
| 212 |
|
| 213 | for (i = 0; i < 2; i++) {
|
| 214 | if (!(state->perror[i] & TSUNAMI_PERROR_ERRMASK) ||
|
| 215 | (frame && state->perror[i] == tsunami_pchip_error(frame, i)))
|
| 216 | continue;
|
| 217 | printk("%s Live Pchip status before acknowledgment:\n", err_print_prefix);
|
| 218 | tsunami_parse_p_perror(i, state->perror[i], 1);
|
| 219 | }
|
| 220 | if ((state->misc & TSUNAMI_MISC_NXM) &&
|
| 221 | (!frame || ((state->misc ^ frame->misc) & (0xfUL << 28))))
|
| 222 | printk("%s Live MISC: %016llx, non-existent memory, NXS: %llu\n",
|
| 223 | err_print_prefix, state->misc, (state->misc >> 29) & 7);
|
| 224 | }
|
| 225 |
|
| 226 | void
|
| 227 | tsunami_machine_check(unsigned long vector, unsigned long la_ptr)
|
| 228 | {
|
| 229 | struct el_common *header = (void *)la_ptr;
|
| 230 | struct el_TSUNAMI_sysdata_mcheck *frame = NULL;
|
| 231 | struct tsunami_error_state live = {};
|
| 232 | char *saved_err_prefix = err_print_prefix;
|
| 233 | bool show_regs = vector == SCB_Q_SYSMCHK;
|
| 234 |
|
| 235 |
|
| 236 | mb();
|
| 237 | mb();
|
| 238 | draina();
|
| 239 |
|
| 240 | if (vector == SCB_Q_SYSMCHK || vector == SCB_Q_SYSERR) {
|
| 241 | tsunami_clear_errors(&live);
|
| 242 | frame = tsunami_system_frame(header);
|
| 243 | }
|
| 244 |
|
| 245 | #ifdef CONFIG_VERBOSE_MCHECK
|
| 246 | show_regs |= alpha_verbose_mcheck != 0;
|
| 247 | #endif
|
| 248 | err_print_prefix = KERN_CRIT;
|
| 249 | if (vector == SCB_Q_SYSMCHK || vector == SCB_Q_SYSERR) {
|
| 250 | printk("%sSystem %s error (vector %lx, code %x) on CPU %d:\n",
|
| 251 | err_print_prefix, vector == SCB_Q_SYSERR ? "correctable" :
|
| 252 | "uncorrectable", vector, header->code, smp_processor_id());
|
| 253 | tsunami_process_logout_frame(header, 1);
|
| 254 | if (show_regs)
|
| 255 | dik_show_regs(get_irq_regs(), NULL);
|
| 256 | tsunami_report_live_errors(&live, frame);
|
| 257 | } else if (vector == SCB_Q_SYSEVENT) {
|
| 258 | printk("%sTsunami environmental event (code %x): layout unknown\n",
|
| 259 | err_print_prefix, header->code);
|
| 260 | mchk_dump_logout_frame_raw(header);
|
| 261 | }
|
| 262 | err_print_prefix = saved_err_prefix;
|
| 263 |
|
| 264 | if (vector != SCB_Q_SYSMCHK && vector != SCB_Q_SYSERR &&
|
| 265 | vector != SCB_Q_SYSEVENT) {
|
| 266 |
|
| 267 | ev6_machine_check(vector, la_ptr);
|
| 268 | } else {
|
| 269 | wrmces(0x7);
|
| 270 |
|
| 271 | mb();
|
| 272 | }
|
| 273 | }
|
| 274 |
|
| 275 | static const char * const CLIPPER_EnvQW1SMIR[] = {
|
| 276 | "",
|
| 277 | "OCP or RMC halt detected",
|
| 278 | "Sys_DC_Notok failure detected",
|
| 279 | "",
|
| 280 | "System temperature over 50 degrees C failure",
|
| 281 | "PCI Bus #0 is in reset",
|
| 282 | "PCI Bus #1 is in reset",
|
| 283 | "System is being reset"
|
| 284 | };
|
| 285 |
|
| 286 | static const char * const CLIPPER_EnvQW2CPUIR[] = {
|
| 287 | "CPU0 regulator is enabled",
|
| 288 | "CPU1 regulator is enabled",
|
| 289 | "CPU2 regulator is enabled",
|
| 290 | "CPU3 regulator is enabled",
|
| 291 | "CPU0 regulator or configuration sequence fail",
|
| 292 | "CPU1 regulator or configuration sequence fail",
|
| 293 | "CPU2 regulator or configuration sequence fail",
|
| 294 | "CPU3 regulator or configuration sequence fail"
|
| 295 | };
|
| 296 |
|
| 297 | static const char * const CLIPPER_EnvQW3PSIR[] = {
|
| 298 | "Power Supply 0 is enabled",
|
| 299 | "Power Supply 1 is enabled",
|
| 300 | "Power Supply 2 is enabled",
|
| 301 | "",
|
| 302 | "Power Supply 0 was enabled but failed",
|
| 303 | "Power Supply 1 was enabled but failed",
|
| 304 | "Power Supply 2 was enabled but failed"
|
| 305 | };
|
| 306 |
|
| 307 | static const char * const CLIPPER_EnvQW4LM78ISR[] = {
|
| 308 | "PS +3.3V out of tolerance",
|
| 309 | "PS +5V out of tolerance",
|
| 310 | "PS +12V out of tolerance",
|
| 311 | "VTERM out of tolerance",
|
| 312 | "Temperature zone 0 (PCI Backplane slots 1-3 area) over limit failure",
|
| 313 | "LM75 CPU0-3 Temperature over limit failure",
|
| 314 | "System Fan 1 failure",
|
| 315 | "System Fan 2 failure",
|
| 316 | "CTERM out of tolerance",
|
| 317 | "",
|
| 318 | "-12V out of tolerance",
|
| 319 | "",
|
| 320 | "",
|
| 321 | "",
|
| 322 | "",
|
| 323 | "",
|
| 324 | "CPU0_VCORE +2V out of tolerance",
|
| 325 | "CPU0_VIO +1.5V out of tolerance",
|
| 326 | "CPU1_VCORE +2V out of tolerance",
|
| 327 | "CPU1_VIO +1.5V out of tolerance",
|
| 328 | "Temperature zone 1 (PCI Backplane slots 7-10 area) over limit failure",
|
| 329 | "",
|
| 330 | "System Fan 4 failure",
|
| 331 | "System Fan 5 failure",
|
| 332 | "",
|
| 333 | "",
|
| 334 | "",
|
| 335 | "",
|
| 336 | "",
|
| 337 | "",
|
| 338 | "",
|
| 339 | "",
|
| 340 | "CPU2_VCORE +2V out of tolerance",
|
| 341 | "CPU2_VIO +1.5V out of tolerance",
|
| 342 | "CPU3_VCORE +2V out of tolerance",
|
| 343 | "CPU3_VIO +1.5V out of tolerance",
|
| 344 | "Temperature zone 2 (PCI Backplane slots 4-6 area) over limit failure",
|
| 345 | "",
|
| 346 | "System Fan 3 failure",
|
| 347 | "System Fan 6 failure",
|
| 348 | "",
|
| 349 | "",
|
| 350 | "Power supply 3.3V rail above high amperage warning",
|
| 351 | "Power supply 5.0V rail above high amperage warning",
|
| 352 | "Power supply 12V rail above high amperage warning",
|
| 353 | "Power supply high temperature warning",
|
| 354 | "Power supply AC input low limit warning",
|
| 355 | "Power supply AC input high limit warning"
|
| 356 | };
|
| 357 |
|
| 358 | static const char * const CLIPPER_EnvQW5DOOR[] = {
|
| 359 | "",
|
| 360 | "System CPU door is open",
|
| 361 | "System Fan door is open",
|
| 362 | "System PCI door is open",
|
| 363 | "",
|
| 364 | "System CPU door is closed",
|
| 365 | "System Fan door is closed",
|
| 366 | "System PCI door is closed"
|
| 367 | };
|
| 368 |
|
| 369 | static const char * const CLIPPER_EnvQW6TEMP[] = {
|
| 370 | "CPU0 temperature warning fault has occurred",
|
| 371 | "CPU1 temperature warning fault has occurred",
|
| 372 | "CPU2 temperature warning fault has occurred",
|
| 373 | "CPU3 temperature warning fault has occurred",
|
| 374 | "System temperature zone 0 warning fault has occurred",
|
| 375 | "System temperature zone 1 warning fault has occurred",
|
| 376 | "System temperature zone 2 warning fault has occurred"
|
| 377 | };
|
| 378 |
|
| 379 | static const char * const CLIPPER_EnvQW7FAN[] = {
|
| 380 | "System Fan 1 is not responding to RMC Commands",
|
| 381 | "System Fan 2 is not responding to RMC Commands",
|
| 382 | "System Fan 3 is not responding to RMC Commands",
|
| 383 | "System Fan 4 is not responding to RMC Commands",
|
| 384 | "System Fan 5 is not responding to RMC Commands",
|
| 385 | "System Fan 6 is not responding to RMC Commands",
|
| 386 | "",
|
| 387 | "",
|
| 388 | "CPU fans 5/6 at maximum speed",
|
| 389 | "CPU fans 5/6 reduced speed from maximum",
|
| 390 | "PCI fans 1-4 at maximum speed",
|
| 391 | "PCI fans 1-4 reduced speed from maximum"
|
| 392 | };
|
| 393 |
|
| 394 | #define CLIPPER_ENV_LM78ISR_MASK 0xfcdf00df05ffUL
|
| 395 |
|
| 396 |
|
| 397 | struct el_clipper_envdata {
|
| 398 | u64 summary;
|
| 399 | u64 dir;
|
| 400 | u64 smir;
|
| 401 | u64 cpuir;
|
| 402 | u64 psir;
|
| 403 | u64 fault;
|
| 404 | u64 doors;
|
| 405 | u64 temp_warn;
|
| 406 | u64 fan_ctrl;
|
| 407 | u64 fatal_power_down;
|
| 408 | u64 reserved;
|
| 409 | };
|
| 410 |
|
| 411 | static bool
|
| 412 | clipper_print_bits(const char * const *messages, size_t count,
|
| 413 | u64 value, u64 mask)
|
| 414 | {
|
| 415 | size_t i;
|
| 416 |
|
| 417 | if (!(value & mask))
|
| 418 | return false;
|
| 419 | for (i = 0; i < count; i++)
|
| 420 | if (value & mask & (1UL << i))
|
| 421 | printk("%s %s\n", err_print_prefix, messages[i]);
|
| 422 | return true;
|
| 423 | }
|
| 424 |
|
| 425 | #define CLIPPER_BITS(table, value, mask) \
|
| 426 | clipper_print_bits(table, ARRAY_SIZE(table), value, mask)
|
| 427 |
|
| 428 | static void
|
| 429 | clipper_process_environment(struct el_common *header)
|
| 430 | {
|
| 431 | struct el_clipper_envdata *frame;
|
| 432 | bool fault = false;
|
| 433 |
|
| 434 | if (!tsunami_frame_valid(header, sizeof(*frame))) {
|
| 435 | printk("%s Truncated Clipper environmental frame\n", err_print_prefix);
|
| 436 | mchk_dump_logout_frame_raw(header);
|
| 437 | return;
|
| 438 | }
|
| 439 | frame = (void *)header + header->sys_offset;
|
| 440 |
|
| 441 |
|
| 442 | fault |= CLIPPER_BITS(CLIPPER_EnvQW1SMIR, frame->smir, 0x14);
|
| 443 | fault |= CLIPPER_BITS(CLIPPER_EnvQW2CPUIR, frame->cpuir, 0xf0);
|
| 444 | fault |= CLIPPER_BITS(CLIPPER_EnvQW3PSIR, frame->psir, 0x70);
|
| 445 | fault |= CLIPPER_BITS(CLIPPER_EnvQW4LM78ISR,
|
| 446 | frame->fault, CLIPPER_ENV_LM78ISR_MASK);
|
| 447 | fault |= CLIPPER_BITS(CLIPPER_EnvQW5DOOR, frame->doors, 0x0e);
|
| 448 | fault |= CLIPPER_BITS(CLIPPER_EnvQW6TEMP, frame->temp_warn, 0x7f);
|
| 449 | fault |= CLIPPER_BITS(CLIPPER_EnvQW7FAN, frame->fan_ctrl, 0x3f);
|
| 450 |
|
| 451 | if (!fault)
|
| 452 | printk("%s No recognized environmental fault bits set\n",
|
| 453 | err_print_prefix);
|
| 454 | printk("%s Environmental summary: %016llx DIR: %016llx\n",
|
| 455 | err_print_prefix, frame->summary, frame->dir);
|
| 456 | printk("%s SMIR: %016llx (PSU summary/halt/reset bits undecoded)\n",
|
| 457 | err_print_prefix, frame->smir);
|
| 458 | CLIPPER_BITS(CLIPPER_EnvQW2CPUIR, frame->cpuir, 0x0f);
|
| 459 | CLIPPER_BITS(CLIPPER_EnvQW3PSIR, frame->psir, 0x07);
|
| 460 | CLIPPER_BITS(CLIPPER_EnvQW5DOOR, frame->doors, 0xe0);
|
| 461 | CLIPPER_BITS(CLIPPER_EnvQW7FAN, frame->fan_ctrl, 0xf00);
|
| 462 | if (frame->fault & (0x3fUL << 42))
|
| 463 | printk("%s Power supply warning source: %llu (3 = reserved)\n",
|
| 464 | err_print_prefix, (frame->fault >> 40) & 3);
|
| 465 | }
|
| 466 |
|
| 467 | void
|
| 468 | clipper_machine_check(unsigned long vector, unsigned long la_ptr)
|
| 469 | {
|
| 470 | struct el_common *header = (void *)la_ptr;
|
| 471 | char *saved_err_prefix = err_print_prefix;
|
| 472 |
|
| 473 | if (vector != SCB_Q_SYSEVENT) {
|
| 474 | tsunami_machine_check(vector, la_ptr);
|
| 475 | return;
|
| 476 | }
|
| 477 |
|
| 478 | mb();
|
| 479 | draina();
|
| 480 | err_print_prefix = KERN_CRIT;
|
| 481 | printk("%sSystem correctable environmental event (code %x) on CPU %d:\n",
|
| 482 | err_print_prefix, header->code, smp_processor_id());
|
| 483 | if (header->code == 0x206)
|
| 484 | clipper_process_environment(header);
|
| 485 | else {
|
| 486 | printk("%s Unrecognized environmental event; raw frame follows\n",
|
| 487 | err_print_prefix);
|
| 488 | mchk_dump_logout_frame_raw(header);
|
| 489 | }
|
| 490 | err_print_prefix = saved_err_prefix;
|
| 491 | wrmces(0x7);
|
| 492 |
|
| 493 | mb();
|
| 494 | }
|
| 495 |
|
| 496 | static struct el_subpacket * __init
|
| 497 | tsunami_process_subpacket(struct el_subpacket *packet)
|
| 498 | {
|
| 499 | struct el_common *frame;
|
| 500 | size_t offset = offsetof(struct el_subpacket, by_type.regatta_frame.data_start);
|
| 501 | int status;
|
| 502 |
|
| 503 |
|
| 504 | if (packet->length < offsetof(struct el_subpacket, by_type) ||
|
| 505 | (packet->length & 7))
|
| 506 | return NULL;
|
| 507 | if (packet->revision != 1) {
|
| 508 | el_annotate_subpacket(packet);
|
| 509 | return (void *)packet + packet->length;
|
| 510 | }
|
| 511 |
|
| 512 | switch (packet->type) {
|
| 513 | case EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME:
|
| 514 | case EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT:
|
| 515 | case EL_TYPE__REGATTA__SYSTEM_ERROR_FRAME:
|
| 516 | case EL_TYPE__REGATTA__SYSTEM_DBL_ERROR_HALT:
|
| 517 | if (packet->length < offset + sizeof(*frame))
|
| 518 | return NULL;
|
| 519 | frame = (void *)packet + offset;
|
| 520 | if (frame->size > packet->length - offset || frame->size < sizeof(*frame))
|
| 521 | return NULL;
|
| 522 | if (packet->type == EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME ||
|
| 523 | packet->type == EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT) {
|
| 524 | printk("%s ** Occurred on CPU %d:\n", err_print_prefix,
|
| 525 | (int)packet->by_type.regatta_frame.cpuid);
|
| 526 | status = ev6_process_logout_frame(frame, 0);
|
| 527 | if (status != MCHK_DISPOSITION_DISMISS)
|
| 528 | ev6_process_logout_frame(frame, 1);
|
| 529 | } else {
|
| 530 | tsunami_process_logout_frame(frame, 1);
|
| 531 | }
|
| 532 | break;
|
| 533 | case EL_TYPE__REGATTA__ENVIRONMENTAL_FRAME:
|
| 534 | |
| 535 | * ES40 Table D-18 gives sys_offset=0x20, but places the
|
| 536 | * summary at frame+0x18. Preserve the complete packet until
|
| 537 | * the console layout is verified; do not apply the live layout.
|
| 538 |
|
| 539 | printk("%s Console environmental layout unverified; raw packet follows\n",
|
| 540 | err_print_prefix);
|
| 541 | el_annotate_subpacket(packet);
|
| 542 | break;
|
| 543 | default:
|
| 544 | el_annotate_subpacket(packet);
|
| 545 | break;
|
| 546 | }
|
| 547 | return (void *)packet + packet->length;
|
| 548 | }
|
| 549 |
|
| 550 | |
| 551 | * Clipper console logs have an HWRPB byte count. Do not use the generic
|
| 552 | * unbounded dispatcher: a rejected record must not acknowledge the log.
|
| 553 |
|
| 554 | static bool __init
|
| 555 | tsunami_console_log(void *data, size_t remaining)
|
| 556 | {
|
| 557 | struct el_subpacket *packet;
|
| 558 | unsigned char *pos = data;
|
| 559 | size_t size, length;
|
| 560 | u32 count;
|
| 561 | union el_timestamp timestamp;
|
| 562 | const char *name;
|
| 563 |
|
| 564 | while (remaining >= offsetof(struct el_subpacket, by_type)) {
|
| 565 | packet = (void *)pos;
|
| 566 | size = packet->length;
|
| 567 | if (size < offsetof(struct el_subpacket, by_type) ||
|
| 568 | size > remaining || (size & 7))
|
| 569 | return false;
|
| 570 | if (packet->class == EL_CLASS__TERMINATION)
|
| 571 | return packet->type == EL_TYPE__TERMINATION__TERMINATION;
|
| 572 | if (packet->class != EL_CLASS__HEADER || packet->revision != 1)
|
| 573 | return false;
|
| 574 |
|
| 575 | timestamp.as_int = 0;
|
| 576 |
|
| 577 | #define CONSOLE_HEADER_SIZE(member) \
|
| 578 | (offsetof(struct el_subpacket, by_type.member) + \
|
| 579 | sizeof(packet->by_type.member))
|
| 580 | switch (packet->type) {
|
| 581 | case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME:
|
| 582 | if (size < CONSOLE_HEADER_SIZE(sys_err))
|
| 583 | return false;
|
| 584 | name = "SYSTEM ERROR";
|
| 585 | length = packet->by_type.sys_err.frame_length;
|
| 586 | count = packet->by_type.sys_err.frame_packet_count;
|
| 587 | break;
|
| 588 | case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME:
|
| 589 | if (size < CONSOLE_HEADER_SIZE(sys_event))
|
| 590 | return false;
|
| 591 | name = "SYSTEM EVENT";
|
| 592 | timestamp = packet->by_type.sys_event.timestamp;
|
| 593 | length = packet->by_type.sys_event.frame_length;
|
| 594 | count = packet->by_type.sys_event.frame_packet_count;
|
| 595 | break;
|
| 596 | case EL_TYPE__HEADER__HALT_FRAME:
|
| 597 | if (size < CONSOLE_HEADER_SIZE(err_halt))
|
| 598 | return false;
|
| 599 | name = "ERROR HALT";
|
| 600 | timestamp = packet->by_type.err_halt.timestamp;
|
| 601 | length = packet->by_type.err_halt.frame_length;
|
| 602 | count = packet->by_type.err_halt.frame_packet_count;
|
| 603 | break;
|
| 604 | case EL_TYPE__HEADER__LOGOUT_FRAME:
|
| 605 | if (size < CONSOLE_HEADER_SIZE(logout_header))
|
| 606 | return false;
|
| 607 | name = "LOGOUT FRAME";
|
| 608 | length = packet->by_type.logout_header.frame_length;
|
| 609 | count = 1;
|
| 610 | break;
|
| 611 | default:
|
| 612 | return false;
|
| 613 | }
|
| 614 | #undef CONSOLE_HEADER_SIZE
|
| 615 | if (length > remaining - size || (length & 7) ||
|
| 616 | count > length / offsetof(struct el_subpacket, by_type))
|
| 617 | return false;
|
| 618 | printk("%s*** %s: CLASS %u, TYPE %u\n", err_print_prefix,
|
| 619 | name, packet->class, packet->type);
|
| 620 | el_print_timestamp(×tamp);
|
| 621 | if (packet->type == EL_TYPE__HEADER__HALT_FRAME)
|
| 622 | printk("%s HALT CODE: %04x\n", err_print_prefix,
|
| 623 | packet->by_type.err_halt.halt_code);
|
| 624 | pos += size;
|
| 625 | remaining -= size + length;
|
| 626 | while (count--) {
|
| 627 | if (length < offsetof(struct el_subpacket, by_type))
|
| 628 | return false;
|
| 629 | packet = (void *)pos;
|
| 630 | size = packet->length;
|
| 631 | if (size < offsetof(struct el_subpacket, by_type) ||
|
| 632 | size > length || (size & 7) ||
|
| 633 | packet->class != EL_CLASS__REGATTA_FAMILY)
|
| 634 | return false;
|
| 635 |
|
| 636 | if (tsunami_process_subpacket(packet) != (void *)pos + size)
|
| 637 | return false;
|
| 638 | pos += size;
|
| 639 | length -= size;
|
| 640 | }
|
| 641 | if (length)
|
| 642 | return false;
|
| 643 | }
|
| 644 |
|
| 645 | return false;
|
| 646 | }
|
| 647 |
|
| 648 | static void __init
|
| 649 | tsunami_console_data_log(int cpu, struct percpu_struct *pcpu)
|
| 650 | {
|
| 651 | unsigned char *data = (void *)(IDENT_ADDR | pcpu->console_data_log_pa);
|
| 652 | size_t length = pcpu->console_data_log_length;
|
| 653 | size_t dump = length < 1024 ? length : 1024;
|
| 654 |
|
| 655 | printk("%sConsole data log for CPU %d (%zu bytes):\n",
|
| 656 | err_print_prefix, cpu, length);
|
| 657 | if (!(pcpu->console_data_log_pa & 7) &&
|
| 658 | length <= ~0UL - (unsigned long)data &&
|
| 659 | tsunami_console_log(data, length)) {
|
| 660 |
|
| 661 | pcpu->console_data_log_pa = 0;
|
| 662 | return;
|
| 663 | }
|
| 664 | printk("%s Incomplete console log; retaining firmware pointer\n",
|
| 665 | err_print_prefix);
|
| 666 |
|
| 667 | if (dump > ~0UL - (unsigned long)data)
|
| 668 | return;
|
| 669 | print_hex_dump(err_print_prefix, " ", DUMP_PREFIX_OFFSET, 16, 1,
|
| 670 | data, dump, false);
|
| 671 | if (dump < length)
|
| 672 | printk("%s Raw console log truncated to %zu bytes\n",
|
| 673 | err_print_prefix, dump);
|
| 674 | }
|
| 675 |
|
| 676 | void __init
|
| 677 | tsunami_check_console_data_log(void)
|
| 678 | {
|
| 679 | struct percpu_struct *pcpu;
|
| 680 | unsigned long cpu;
|
| 681 |
|
| 682 | for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) {
|
| 683 | pcpu = (void *)hwrpb + hwrpb->processor_offset +
|
| 684 | cpu * hwrpb->processor_size;
|
| 685 | if (pcpu->console_data_log_pa)
|
| 686 | tsunami_console_data_log(cpu, pcpu);
|
| 687 | }
|
| 688 | }
|