1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/plat_ecc_unum.h> 27 #include <sys/utsname.h> 28 #include <sys/cmn_err.h> 29 #include <sys/async.h> 30 #include <sys/errno.h> 31 #include <sys/fm/protocol.h> 32 #include <sys/fm/cpu/UltraSPARC-III.h> 33 #include <sys/bl.h> 34 #include <sys/taskq.h> 35 #include <sys/condvar.h> 36 #include <sys/plat_ecc_dimm.h> 37 38 /* 39 * Pointer to platform specific function to initialize a cache of DIMM 40 * serial ids 41 */ 42 int (*p2init_sid_cache)(void); 43 44 /* 45 * This file contains the common code that is used for parsing 46 * ecc unum data and logging it appropriately as the platform 47 * that calls this code implements. 48 */ 49 50 int plat_ecc_dispatch_task(plat_ecc_message_t *); 51 static void plat_ecc_send_msg(void *); 52 53 #define CHECK_UNUM \ 54 if (unum_ptr == NULL) { \ 55 break; \ 56 } 57 58 /* 59 * See plat_ecc_unum.h for the meaning of these variables. 60 */ 61 int ecc_log_fruid_enable = ECC_FRUID_ENABLE_DEFAULT; 62 63 uint32_t plat_ecc_capability_map_domain = PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT; 64 uint32_t plat_ecc_capability_map_sc = PLAT_ECC_CAPABILITY_SC_DEFAULT; 65 uint16_t ecc_error2_mailbox_flags = PLAT_ECC_ERROR2_SEND_DEFAULT; 66 uint16_t ecc_indictment2_mailbox_flags = PLAT_ECC_SEND_INDICT2_DEFAULT; 67 68 /* 69 * We log all ECC errors using the function that is defined as 70 * plat_send_ecc_mailbox_msg(); We first parse the unum string and 71 * then pass the data to be logged to the plat_send_ecc_mailbox_msg 72 * function for logging. Each platform that uses this code needs to 73 * implement a suitable function for this purpose. 74 */ 75 void 76 plat_log_fruid_error(int synd_code, struct async_flt *ecc, char *unum, 77 uint64_t afsr_bit) 78 { 79 plat_ecc_error_data_t ecc_error_data; 80 enum plat_ecc_type ecc_type = PLAT_ECC_UNKNOWN; 81 int board_num; 82 int proc_position; 83 int invalid_unum = 1; 84 85 bzero(&ecc_error_data, sizeof (plat_ecc_error_data_t)); 86 ecc_error_data.version = PLAT_ECC_VERSION; 87 88 switch (afsr_bit) { 89 case C_AFSR_CE: 90 ecc_error_data.error_code = PLAT_ERROR_CODE_CE; 91 break; 92 case C_AFSR_UE: 93 ecc_error_data.error_code = PLAT_ERROR_CODE_UE; 94 break; 95 case C_AFSR_EDC: 96 ecc_error_data.error_code = PLAT_ERROR_CODE_EDC; 97 break; 98 case C_AFSR_EDU: 99 ecc_error_data.error_code = PLAT_ERROR_CODE_EDU; 100 break; 101 case C_AFSR_WDC: 102 ecc_error_data.error_code = PLAT_ERROR_CODE_WDC; 103 break; 104 case C_AFSR_WDU: 105 ecc_error_data.error_code = PLAT_ERROR_CODE_WDU; 106 break; 107 case C_AFSR_CPC: 108 ecc_error_data.error_code = PLAT_ERROR_CODE_CPC; 109 break; 110 case C_AFSR_CPU: 111 ecc_error_data.error_code = PLAT_ERROR_CODE_CPU; 112 break; 113 case C_AFSR_UCC: 114 ecc_error_data.error_code = PLAT_ERROR_CODE_UCC; 115 break; 116 case C_AFSR_UCU: 117 ecc_error_data.error_code = PLAT_ERROR_CODE_UCU; 118 break; 119 case C_AFSR_EMC: 120 ecc_error_data.error_code = PLAT_ERROR_CODE_EMC; 121 break; 122 case C_AFSR_EMU: 123 ecc_error_data.error_code = PLAT_ERROR_CODE_EMU; 124 break; 125 default: 126 /* 127 * Do not send messages with unknown error codes, since 128 * the SC will not be able to tell what type of error 129 * occurred. 130 */ 131 return; 132 } 133 134 ecc_error_data.detecting_proc = ecc->flt_bus_id; 135 136 if (ecc->flt_in_memory) 137 ecc_type = PLAT_ECC_MEMORY; 138 else if (ecc->flt_status & ECC_ECACHE) 139 ecc_type = PLAT_ECC_ECACHE; 140 141 switch (ecc_type) { 142 case PLAT_ECC_MEMORY: { 143 /* 144 * The unum string is expected to be in this form: 145 * "/N0/SB12/P0/B0/D2 J13500, ..." 146 * for serengeti. As this code is shared with Starcat 147 * if N is missing then it is set to 0. 148 * From that we will extract the bank number, dimm 149 * number, and Jnumber. 150 */ 151 char *unum_ptr = unum; 152 char *jno_ptr = ecc_error_data.Jnumber; 153 int i; 154 155 /* 156 * On Serengeti we expect to find 'N' in the unum string 157 * however, on Starcat 'N' does not appear in the unum string. 158 * We do not want this code to break at this point, so the 159 * unum_ptr is reset to the start of unum string if we fail 160 * to find an 'N'. 161 */ 162 unum_ptr = strchr(unum_ptr, 'N'); 163 if (unum_ptr == NULL) { 164 ecc_error_data.node_no = 0; 165 unum_ptr = unum; 166 } else { 167 unum_ptr++; 168 ecc_error_data.node_no = stoi(&unum_ptr); 169 } 170 171 /* 172 * Now pull out the SB number 173 */ 174 unum_ptr = strstr(unum_ptr, "SB"); 175 CHECK_UNUM; 176 unum_ptr += 2; 177 board_num = stoi(&unum_ptr); 178 179 /* 180 * Now pull out the Proc position (relative to the board) 181 */ 182 unum_ptr = strchr(unum_ptr, 'P'); 183 CHECK_UNUM; 184 unum_ptr++; 185 proc_position = stoi(&unum_ptr); 186 187 /* 188 * Using the SB number and Proc position we create a FRU 189 * cpu id. 190 */ 191 ecc_error_data.proc_num = 192 plat_make_fru_cpuid(board_num, 0, proc_position); 193 194 /* 195 * Now pull out the Memory Bank number 196 */ 197 unum_ptr = strchr(unum_ptr, 'B'); 198 CHECK_UNUM; 199 unum_ptr++; 200 ecc_error_data.bank_no = (stoi(&unum_ptr) & 0x01); 201 202 /* 203 * Now pull out the Dimm number within the Memory Bank. 204 */ 205 unum_ptr = strchr(unum_ptr, 'D'); 206 CHECK_UNUM; 207 unum_ptr++; 208 ecc_error_data.ecache_dimm_no = (stoi(&unum_ptr) & 0x03); 209 210 /* 211 * Now pull out the J-number. 212 */ 213 unum_ptr = strchr(unum_ptr, 'J'); 214 CHECK_UNUM; 215 unum_ptr++; 216 for (i = PLAT_ECC_JNUMBER_LENGTH; 217 i > 0 && *unum_ptr >= '0' && *unum_ptr <= '9'; i--) 218 *jno_ptr++ = *unum_ptr++; 219 *jno_ptr = '\0'; 220 221 /* 222 * If we get here, we can assume the unum is valid 223 */ 224 invalid_unum = 0; 225 break; 226 } 227 case PLAT_ECC_ECACHE: { 228 /* 229 * The unum string is expected to be in this form: 230 * "[/N0/][SB|IO]12/P0/E0 J13500, ..." 231 * for serengeti. As this code is shared with Starcat 232 * if N is missing then it is set to 0. IO may only appear 233 * on Starcats. From that we will extract the bank number, 234 * dimm number, and Jnumber. 235 */ 236 char *unum_ptr = unum; 237 char *jno_ptr = ecc_error_data.Jnumber; 238 int is_maxcat = 0; 239 int i; 240 241 /* 242 * On Serengeti we expect to find 'N' in the unum string 243 * however, on Starcat 'N' does not appear in the unum string. 244 * We do not want this code to break at this point, so the 245 * unum_ptr is reset to the start of unum string if we fail 246 * to find an 'N'. 247 */ 248 unum_ptr = strchr(unum_ptr, 'N'); 249 if (unum_ptr == NULL) { 250 ecc_error_data.node_no = 0; 251 unum_ptr = unum; 252 } else { 253 unum_ptr++; 254 ecc_error_data.node_no = stoi(&unum_ptr); 255 } 256 257 /* 258 * Now pull out the SB/IO number 259 */ 260 unum_ptr = strstr(unum_ptr, "SB"); 261 if (unum_ptr == NULL) { 262 263 /* 264 * Since this is an E$ error, it must have occurred on 265 * either a System Board (represented by "SB" in the 266 * unum string) or a Maxcat board ("IO" in the unum 267 * string). Since we failed the "SB" check, we'll 268 * assume this is a maxcat board. 269 */ 270 is_maxcat = 1; 271 unum_ptr = strstr(unum, "IO"); 272 } 273 CHECK_UNUM; 274 unum_ptr += 2; 275 board_num = stoi(&unum_ptr); 276 277 /* 278 * Now pull out the Proc position (relative to the board) 279 */ 280 unum_ptr = strchr(unum_ptr, 'P'); 281 CHECK_UNUM; 282 unum_ptr++; 283 proc_position = stoi(&unum_ptr); 284 285 /* 286 * Using the SB/IO number, slot 0/1 value (is_maxcat), and 287 * proc position, we create the cpu id. 288 */ 289 ecc_error_data.proc_num = plat_make_fru_cpuid(board_num, 290 is_maxcat, proc_position); 291 292 ecc_error_data.bank_no = 0; /* not used */ 293 294 unum_ptr = strchr(unum_ptr, 'E'); 295 CHECK_UNUM; 296 unum_ptr++; 297 ecc_error_data.ecache_dimm_no = (stoi(&unum_ptr) & 0x01); 298 299 unum_ptr = strchr(unum_ptr, 'J'); 300 CHECK_UNUM; 301 unum_ptr++; 302 for (i = PLAT_ECC_JNUMBER_LENGTH; 303 i > 0 && *unum_ptr >= '0' && *unum_ptr <= '9'; i--) 304 *jno_ptr++ = *unum_ptr++; 305 *jno_ptr = '\0'; 306 307 /* 308 * If we get here, we can assume the unum is valid 309 */ 310 invalid_unum = 0; 311 break; 312 } 313 default: 314 /* 315 * Unknown error 316 */ 317 break; 318 } 319 320 /* 321 * This is where CHECK_UNUM goes when it finds an error 322 */ 323 324 if (ECC_SYND_DATA_BEGIN <= synd_code && 325 synd_code < ECC_SYND_ECC_BEGIN) { 326 ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE; 327 ecc_error_data.databit_type = PLAT_BIT_TYPE_DATA; 328 ecc_error_data.databit_no = synd_code; 329 } else if (ECC_SYND_ECC_BEGIN <= synd_code && 330 synd_code < ECC_SYND_MTAG_BEGIN) { 331 ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE; 332 ecc_error_data.databit_type = PLAT_BIT_TYPE_ECC; 333 ecc_error_data.databit_no = synd_code - ECC_SYND_ECC_BEGIN; 334 } else if (ECC_SYND_MTAG_BEGIN <= synd_code && 335 synd_code < ECC_SYND_MECC_BEGIN) { 336 ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE; 337 ecc_error_data.databit_type = PLAT_BIT_TYPE_MTAG_D; 338 ecc_error_data.databit_no = synd_code - ECC_SYND_MTAG_BEGIN; 339 } else if (ECC_SYND_MECC_BEGIN <= synd_code && 340 synd_code < ECC_SYND_M2) { 341 ecc_error_data.error_type = PLAT_ERROR_TYPE_SINGLE; 342 ecc_error_data.databit_type = PLAT_BIT_TYPE_MTAG_E; 343 ecc_error_data.databit_no = synd_code - ECC_SYND_MECC_BEGIN; 344 } else { 345 switch (synd_code) { 346 case ECC_SYND_M2: 347 ecc_error_data.error_type = PLAT_ERROR_TYPE_M2; 348 break; 349 case ECC_SYND_M3: 350 ecc_error_data.error_type = PLAT_ERROR_TYPE_M3; 351 break; 352 case ECC_SYND_M4: 353 ecc_error_data.error_type = PLAT_ERROR_TYPE_M4; 354 break; 355 case ECC_SYND_M: 356 ecc_error_data.error_type = PLAT_ERROR_TYPE_M; 357 break; 358 default: 359 ecc_error_data.error_type = PLAT_ERROR_TYPE_UNK; 360 break; 361 } 362 ecc_error_data.databit_type = PLAT_BIT_TYPE_MULTI; 363 ecc_error_data.databit_no = 0; /* not used */ 364 } 365 366 #ifdef DEBUG 367 if (invalid_unum && 368 (ecc_error_data.error_code != PLAT_ERROR_CODE_UE) && 369 unum && *unum) 370 cmn_err(CE_WARN, "Unexpected unum string format: %s\n", unum); 371 #endif 372 373 /* 374 * Send this data off as a mailbox message to the SC. 375 */ 376 (void) plat_send_ecc_mailbox_msg(PLAT_ECC_ERROR_MESSAGE, 377 &ecc_error_data); 378 } 379 380 /* 381 * The unum string for memory is expected to be in this form: 382 * "[/N0/]SB12/P0/B0/D2 [J13500]" 383 * Or if the unum was generated as the result of a UE: 384 * "[/N0/]SB12/P0/B0 [J13500, ...]" 385 * From that we will extract the board number, processor position, 386 * bank number and jnumber. 387 * 388 * Return (1) for an invalid unum string. If the unum is for an 389 * individual DIMM and there is no jnumber, jnumber will be set 390 * to -1 and the caller can decide if the unum is valid. This 391 * is because Serengeti does not have jnumbers for bank unums 392 * which may be used to create DIMM unums (e.g. for acquiring 393 * DIMM serial ids). 394 */ 395 396 int 397 parse_unum_memory(char *unum, int *board, int *pos, int *bank, int *dimm, 398 int *jnumber) 399 { 400 char *c; 401 402 if ((c = strstr(unum, "SB")) == NULL) 403 return (1); 404 c += 2; 405 *board = (uint8_t)stoi(&c); 406 407 if (*c++ != '/' || *c++ != 'P') 408 return (1); 409 *pos = stoi(&c); 410 411 if (*c++ != '/' || *c++ != 'B') 412 return (1); 413 *bank = stoi(&c); 414 415 if ((c = strchr(c, 'D')) == NULL) { 416 *dimm = -1; 417 *jnumber = 0; 418 return (0); 419 } 420 c++; 421 *dimm = stoi(&c); 422 423 if ((c = strchr(c, 'J')) == NULL) { 424 *jnumber = -1; 425 return (0); 426 } 427 428 c++; 429 *jnumber = (uint16_t)stoi(&c); 430 431 return (0); 432 } 433 434 /* 435 * The unum string for ecache is expected to be in this form: 436 * "[/N0/][SB|IO]12/P0/E0 J13500, ..." 437 * From that we will extract the board number, processor position and 438 * junmber. 439 * 440 * return (1) for any invalid unum string. 441 */ 442 static int 443 parse_unum_ecache(char *unum, int *board, int *pos, int *jnumber, int *maxcat) 444 { 445 char *c; 446 447 if ((c = strstr(unum, "SB")) == NULL) { 448 /* 449 * Since this is an E$ error, it must have occurred on 450 * either a System Board (represented by "SB" in the 451 * unum string) or a Maxcat board ("IO" in the unum 452 * string). 453 */ 454 if ((c = strstr(unum, "IO")) == NULL) 455 return (1); 456 *maxcat = 1; 457 } 458 459 c += 2; 460 *board = (uint8_t)stoi(&c); 461 462 if (*c++ != '/' || *c++ != 'P') 463 return (1); 464 *pos = stoi(&c); 465 466 if ((c = strchr(c, 'J')) == NULL) 467 return (1); 468 469 c++; 470 *jnumber = (uint16_t)stoi(&c); 471 472 return (0); 473 } 474 475 /* The following array maps the error to its corresponding set */ 476 static int plat_ecc_e2d_map[PLAT_ECC_ERROR2_NUMVALS] = { 477 PLAT_ECC_ERROR2_NONE, /* 0x00 */ 478 PLAT_ECC_ERROR2_SEND_L2_XXC, /* 0x01 */ 479 PLAT_ECC_ERROR2_SEND_L2_XXU, /* 0x02 */ 480 PLAT_ECC_ERROR2_SEND_L3_XXC, /* 0x03 */ 481 PLAT_ECC_ERROR2_SEND_L3_XXU, /* 0x04 */ 482 PLAT_ECC_ERROR2_SEND_MEM_ERRS, /* 0x05 */ 483 PLAT_ECC_ERROR2_SEND_MEM_ERRS, /* 0x06 */ 484 PLAT_ECC_ERROR2_SEND_MEM_ERRS, /* 0x07 */ 485 PLAT_ECC_ERROR2_SEND_BUS_ERRS, /* 0x08 */ 486 PLAT_ECC_ERROR2_SEND_BUS_ERRS, /* 0x09 */ 487 PLAT_ECC_ERROR2_SEND_BUS_ERRS, /* 0x0a */ 488 PLAT_ECC_ERROR2_SEND_BUS_ERRS, /* 0x0b */ 489 PLAT_ECC_ERROR2_SEND_L2_TAG_ERRS, /* 0x0c */ 490 PLAT_ECC_ERROR2_SEND_L2_TAG_ERRS, /* 0x0d */ 491 PLAT_ECC_ERROR2_SEND_L3_TAG_ERRS, /* 0x0e */ 492 PLAT_ECC_ERROR2_SEND_L3_TAG_ERRS, /* 0x0f */ 493 PLAT_ECC_ERROR2_SEND_L1_PARITY, /* 0x10 */ 494 PLAT_ECC_ERROR2_SEND_L1_PARITY, /* 0x11 */ 495 PLAT_ECC_ERROR2_SEND_TLB_PARITY, /* 0x12 */ 496 PLAT_ECC_ERROR2_SEND_TLB_PARITY, /* 0x13 */ 497 PLAT_ECC_ERROR2_SEND_IV_ERRS, /* 0x14 */ 498 PLAT_ECC_ERROR2_SEND_IV_ERRS, /* 0x15 */ 499 PLAT_ECC_ERROR2_SEND_MTAG_XXC, /* 0x16 */ 500 PLAT_ECC_ERROR2_SEND_IV_MTAG_XXC, /* 0x17 */ 501 PLAT_ECC_ERROR2_SEND_L3_XXC, /* 0x18 */ 502 PLAT_ECC_ERROR2_SEND_PCACHE /* 0x19 */ 503 }; 504 505 /* 506 * log enhanced error information to SC. 507 */ 508 void 509 plat_log_fruid_error2(int msg_type, char *unum, struct async_flt *aflt, 510 plat_ecc_ch_async_flt_t *ecc_ch_flt) 511 { 512 plat_ecc_error2_data_t e2d = {0}; 513 int board, pos, bank, dimm, jnumber; 514 int maxcat = 0; 515 uint16_t flags; 516 517 /* Check the flags */ 518 flags = plat_ecc_e2d_map[msg_type]; 519 if ((ecc_error2_mailbox_flags & flags) == 0) 520 return; 521 522 /* Fill the header */ 523 e2d.ee2d_major_version = PLAT_ECC_ERROR2_VERSION_MAJOR; 524 e2d.ee2d_minor_version = PLAT_ECC_ERROR2_VERSION_MINOR; 525 e2d.ee2d_msg_type = PLAT_ECC_ERROR2_MESSAGE; 526 e2d.ee2d_msg_length = sizeof (plat_ecc_error2_data_t); 527 528 /* Fill the data */ 529 if (aflt->flt_in_memory) { 530 if (parse_unum_memory(unum, &board, &pos, &bank, &dimm, 531 &jnumber) || (dimm != -1 && jnumber == -1)) 532 return; 533 /* 534 * Using the SB number and Proc position we create a FRU 535 * cpu id. 536 */ 537 e2d.ee2d_owning_proc = plat_make_fru_cpuid(board, 0, pos); 538 e2d.ee2d_jnumber = jnumber; 539 e2d.ee2d_bank_number = bank; 540 } else if (aflt->flt_status & ECC_ECACHE) { 541 if (parse_unum_ecache(unum, &board, &pos, &jnumber, &maxcat)) 542 return; 543 /* 544 * Using the SB number and Proc position we create a FRU 545 * cpu id. 546 */ 547 e2d.ee2d_owning_proc = plat_make_fru_cpuid(board, maxcat, pos); 548 e2d.ee2d_jnumber = jnumber; 549 e2d.ee2d_bank_number = (uint8_t)-1; 550 } else { 551 /* 552 * L1 Cache 553 */ 554 e2d.ee2d_owning_proc = aflt->flt_bus_id; 555 e2d.ee2d_jnumber = (uint16_t)-1; 556 e2d.ee2d_bank_number = (uint8_t)-1; 557 } 558 559 e2d.ee2d_type = (uint8_t)msg_type; 560 e2d.ee2d_afar_status = (uint8_t)ecc_ch_flt->ecaf_afar_status; 561 e2d.ee2d_synd_status = (uint8_t)ecc_ch_flt->ecaf_synd_status; 562 e2d.ee2d_detecting_proc = aflt->flt_bus_id; 563 e2d.ee2d_cpu_impl = cpunodes[e2d.ee2d_owning_proc].implementation; 564 e2d.ee2d_timestamp = aflt->flt_id; 565 e2d.ee2d_afsr = aflt->flt_stat; 566 e2d.ee2d_afar = aflt->flt_addr; 567 568 e2d.ee2d_sdw_afsr = ecc_ch_flt->ecaf_sdw_afsr; 569 e2d.ee2d_sdw_afar = ecc_ch_flt->ecaf_sdw_afar; 570 e2d.ee2d_afsr_ext = ecc_ch_flt->ecaf_afsr_ext; 571 e2d.ee2d_sdw_afsr_ext = ecc_ch_flt->ecaf_sdw_afsr_ext; 572 573 /* Send the message to SC */ 574 (void) plat_send_ecc_mailbox_msg(PLAT_ECC_ERROR2_MESSAGE, &e2d); 575 } 576 577 uint8_t ecc_indictment_mailbox_disable = PLAT_ECC_INDICTMENT_OK; 578 uint8_t ecc_indictment_mailbox_flags = PLAT_ECC_SEND_DEFAULT_INDICT; 579 580 /* 581 * We log all Solaris indictments of failing hardware. We pull the system 582 * board number and jnumber out of the unum string, and calculate the cpuid 583 * from some members of the unum string. The rest of the structure is filled 584 * in through the other arguments. The data structure is then passed to 585 * plat_ecc_dispatch_task(). This function should only be loaded into memory 586 * or called on platforms that define a plat_send_ecc_mailbox_msg() function. 587 */ 588 static int 589 plat_log_fruid_indictment(int msg_type, struct async_flt *aflt, char *unum) 590 { 591 plat_ecc_message_t *wrapperp; 592 plat_ecc_indict_msg_contents_t *contentsp; 593 char *unum_ptr; 594 int is_maxcat = 0; 595 596 switch (ecc_indictment_mailbox_disable) { 597 case (PLAT_ECC_INDICTMENT_OK): 598 case (PLAT_ECC_INDICTMENT_SUSPECT): 599 break; 600 case (PLAT_ECC_INDICTMENT_NO_SEND): 601 default: 602 return (ECONNREFUSED); 603 } 604 605 switch (msg_type) { 606 case (PLAT_ECC_INDICT_DIMM): 607 if ((ecc_indictment_mailbox_flags & 608 PLAT_ECC_SEND_DIMM_INDICT) == 0) 609 return (ECONNREFUSED); 610 break; 611 case (PLAT_ECC_INDICT_ECACHE_CORRECTABLES): 612 if ((ecc_indictment_mailbox_flags & 613 PLAT_ECC_SEND_ECACHE_XXC_INDICT) == 0) 614 return (ECONNREFUSED); 615 break; 616 case (PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE): 617 if ((ecc_indictment_mailbox_flags & 618 PLAT_ECC_SEND_ECACHE_XXU_INDICT) == 0) 619 return (ECONNREFUSED); 620 break; 621 default: 622 return (ECONNREFUSED); 623 } 624 625 /* LINTED: E_TRUE_LOGICAL_EXPR */ 626 ASSERT(sizeof (plat_ecc_indictment_data_t) == PLAT_ECC_INDICT_SIZE); 627 628 wrapperp = (plat_ecc_message_t *) 629 kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP); 630 631 wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE; 632 wrapperp->ecc_msg_type = PLAT_ECC_INDICTMENT_MESSAGE; 633 wrapperp->ecc_msg_len = sizeof (plat_ecc_indictment_data_t); 634 wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP); 635 636 contentsp = &(((plat_ecc_indictment_data_t *) 637 wrapperp->ecc_msg_data)->msg_contents); 638 639 /* 640 * Find board_num, jnumber, and proc position from the unum string. 641 * Use the board number, is_maxcat, and proc position to calculate 642 * cpuid. 643 */ 644 unum_ptr = strstr(unum, "SB"); 645 if (unum_ptr == NULL) { 646 is_maxcat = 1; 647 unum_ptr = strstr(unum, "IO"); 648 if (unum_ptr == NULL) { 649 kmem_free(wrapperp->ecc_msg_data, 650 wrapperp->ecc_msg_len); 651 kmem_free(wrapperp, sizeof (plat_ecc_message_t)); 652 return (EINVAL); 653 } 654 } 655 unum_ptr += 2; 656 contentsp->board_num = (uint8_t)stoi(&unum_ptr); 657 658 unum_ptr = strchr(unum_ptr, 'P'); 659 if (unum_ptr == NULL) { 660 kmem_free(wrapperp->ecc_msg_data, wrapperp->ecc_msg_len); 661 kmem_free(wrapperp, sizeof (plat_ecc_message_t)); 662 return (EINVAL); 663 } 664 unum_ptr++; 665 contentsp->detecting_proc = 666 (uint16_t)plat_make_fru_cpuid(contentsp->board_num, is_maxcat, 667 stoi(&unum_ptr)); 668 669 unum_ptr = strchr(unum_ptr, 'J'); 670 if (unum_ptr == NULL) { 671 kmem_free(wrapperp->ecc_msg_data, wrapperp->ecc_msg_len); 672 kmem_free(wrapperp, sizeof (plat_ecc_message_t)); 673 return (EINVAL); 674 } 675 unum_ptr++; 676 contentsp->jnumber = (uint16_t)stoi(&unum_ptr); 677 678 /* 679 * Fill in the rest of the data 680 */ 681 contentsp->version = PLAT_ECC_INDICTMENT_VERSION; 682 contentsp->indictment_type = msg_type; 683 contentsp->indictment_uncertain = ecc_indictment_mailbox_disable; 684 contentsp->syndrome = aflt->flt_synd; 685 contentsp->afsr = aflt->flt_stat; 686 contentsp->afar = aflt->flt_addr; 687 688 /* 689 * Build the solaris_version string: 690 */ 691 (void) snprintf(contentsp->solaris_version, 692 PLAT_ECC_VERSION_LENGTH, "%s %s", utsname.release, utsname.version); 693 694 /* 695 * Send the data on to the queuing function 696 */ 697 return (plat_ecc_dispatch_task(wrapperp)); 698 } 699 700 /* The following array maps the indictment to its corresponding set */ 701 static int plat_ecc_i2d_map[PLAT_ECC_INDICT2_NUMVALS] = { 702 PLAT_ECC_INDICT2_NONE, /* 0x00 */ 703 PLAT_ECC_SEND_INDICT2_L2_XXU, /* 0x01 */ 704 PLAT_ECC_SEND_INDICT2_L2_XXC_SERD, /* 0x02 */ 705 PLAT_ECC_SEND_INDICT2_L2_TAG_SERD, /* 0x03 */ 706 PLAT_ECC_SEND_INDICT2_L3_XXU, /* 0x04 */ 707 PLAT_ECC_SEND_INDICT2_L3_XXC_SERD, /* 0x05 */ 708 PLAT_ECC_SEND_INDICT2_L3_TAG_SERD, /* 0x06 */ 709 PLAT_ECC_SEND_INDICT2_L1_SERD, /* 0x07 */ 710 PLAT_ECC_SEND_INDICT2_L1_SERD, /* 0x08 */ 711 PLAT_ECC_SEND_INDICT2_TLB_SERD, /* 0x09 */ 712 PLAT_ECC_SEND_INDICT2_TLB_SERD, /* 0x0a */ 713 PLAT_ECC_SEND_INDICT2_FPU, /* 0x0b */ 714 PLAT_ECC_SEND_INDICT2_PCACHE_SERD /* 0x0c */ 715 }; 716 717 static int 718 plat_log_fruid_indictment2(int msg_type, struct async_flt *aflt, char *unum) 719 { 720 plat_ecc_message_t *wrapperp; 721 plat_ecc_indictment2_data_t *i2d; 722 int board, pos, jnumber; 723 int maxcat = 0; 724 uint16_t flags; 725 726 /* 727 * If the unum is null or empty, skip parsing it 728 */ 729 if (unum && unum[0] != '\0') { 730 if (parse_unum_ecache(unum, &board, &pos, &jnumber, &maxcat)) 731 return (EINVAL); 732 } 733 734 if ((ecc_indictment_mailbox_disable != PLAT_ECC_INDICTMENT_OK) && 735 (ecc_indictment_mailbox_disable != PLAT_ECC_INDICTMENT_SUSPECT)) 736 return (ECONNREFUSED); 737 738 /* Check the flags */ 739 flags = plat_ecc_i2d_map[msg_type]; 740 if ((ecc_indictment2_mailbox_flags & flags) == 0) 741 return (ECONNREFUSED); 742 743 wrapperp = (plat_ecc_message_t *) 744 kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP); 745 746 /* Initialize the wrapper */ 747 wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE; 748 wrapperp->ecc_msg_type = PLAT_ECC_INDICTMENT2_MESSAGE; 749 wrapperp->ecc_msg_len = sizeof (plat_ecc_indictment2_data_t); 750 wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP); 751 752 i2d = (plat_ecc_indictment2_data_t *)wrapperp->ecc_msg_data; 753 754 /* Fill the header */ 755 i2d->ei2d_major_version = PLAT_ECC_INDICT2_MAJOR_VERSION; 756 i2d->ei2d_minor_version = PLAT_ECC_INDICT2_MINOR_VERSION; 757 i2d->ei2d_msg_type = PLAT_ECC_INDICTMENT2_MESSAGE; 758 i2d->ei2d_msg_length = sizeof (plat_ecc_indictment2_data_t); 759 760 /* Fill the data */ 761 if (unum && unum[0] != '\0') { 762 i2d->ei2d_arraigned_proc = plat_make_fru_cpuid(board, maxcat, 763 pos); 764 i2d->ei2d_board_num = board; 765 i2d->ei2d_jnumber = jnumber; 766 } else { 767 i2d->ei2d_arraigned_proc = aflt->flt_inst; 768 i2d->ei2d_board_num = (uint8_t) 769 plat_make_fru_boardnum(i2d->ei2d_arraigned_proc); 770 i2d->ei2d_jnumber = (uint16_t)-1; 771 } 772 773 i2d->ei2d_type = msg_type; 774 i2d->ei2d_uncertain = ecc_indictment_mailbox_disable; 775 i2d->ei2d_cpu_impl = cpunodes[i2d->ei2d_arraigned_proc].implementation; 776 i2d->ei2d_timestamp = aflt->flt_id; 777 778 /* 779 * Send the data on to the queuing function 780 */ 781 return (plat_ecc_dispatch_task(wrapperp)); 782 } 783 784 int 785 plat_ecc_capability_send(void) 786 { 787 plat_ecc_message_t *wrapperp; 788 plat_capability_data_t *cap; 789 int ver_len; 790 791 wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP); 792 793 ver_len = strlen(utsname.release) + strlen(utsname.version) + 2; 794 795 /* Initialize the wrapper */ 796 wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE; 797 wrapperp->ecc_msg_type = PLAT_ECC_CAPABILITY_MESSAGE; 798 wrapperp->ecc_msg_len = sizeof (plat_capability_data_t) + ver_len; 799 wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP); 800 801 cap = (plat_capability_data_t *)wrapperp->ecc_msg_data; 802 803 /* Fill the header */ 804 cap->capd_major_version = PLAT_ECC_CAP_VERSION_MAJOR; 805 cap->capd_minor_version = PLAT_ECC_CAP_VERSION_MINOR; 806 cap->capd_msg_type = PLAT_ECC_CAPABILITY_MESSAGE; 807 cap->capd_msg_length = wrapperp->ecc_msg_len; 808 809 /* Set the default domain capability */ 810 cap->capd_capability = PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT; 811 812 /* 813 * Build the solaris_version string: 814 * utsname.release + " " + utsname.version 815 */ 816 (void) snprintf(cap->capd_solaris_version, ver_len, "%s %s", 817 utsname.release, utsname.version); 818 819 /* 820 * Send the data on to the queuing function 821 */ 822 return (plat_ecc_dispatch_task(wrapperp)); 823 } 824 825 int 826 plat_ecc_capability_sc_get(int type) 827 { 828 switch (type) { 829 case PLAT_ECC_ERROR_MESSAGE: 830 if (ecc_log_fruid_enable && 831 (!(plat_ecc_capability_map_sc & 832 PLAT_ECC_CAPABILITY_ERROR2))) 833 return (1); 834 break; 835 case PLAT_ECC_ERROR2_MESSAGE: 836 if (plat_ecc_capability_map_sc & 837 PLAT_ECC_CAPABILITY_ERROR2) 838 return (1); 839 break; 840 case PLAT_ECC_INDICTMENT_MESSAGE: 841 if (!(plat_ecc_capability_map_sc & 842 PLAT_ECC_CAPABILITY_INDICT2) || 843 !(plat_ecc_capability_map_domain & 844 PLAT_ECC_CAPABILITY_FMA)) 845 return (1); 846 break; 847 case PLAT_ECC_INDICTMENT2_MESSAGE: 848 if (plat_ecc_capability_map_sc & 849 PLAT_ECC_CAPABILITY_INDICT2) 850 return (1); 851 break; 852 case PLAT_ECC_DIMM_SID_MESSAGE: 853 if (plat_ecc_capability_map_sc & 854 PLAT_ECC_CAPABILITY_DIMM_SID) 855 return (1); 856 /* FALLTHROUGH */ 857 default: 858 return (0); 859 } 860 return (0); 861 } 862 863 int plat_ecc_cap_sc_set_cnt = 0; 864 865 void 866 plat_ecc_capability_sc_set(uint32_t cap) 867 { 868 plat_ecc_capability_map_sc = cap; 869 870 if (!plat_ecc_cap_sc_set_cnt && (cap & PLAT_ECC_CAPABILITY_DIMM_SID)) 871 if (p2init_sid_cache) 872 p2init_sid_cache(); 873 874 plat_ecc_cap_sc_set_cnt++; 875 } 876 877 /* 878 * The following table represents mapping between the indictment1 reason 879 * to its type. 880 */ 881 882 static plat_ecc_bl_map_t plat_ecc_bl_map_v1[] = { 883 { "l2cachedata", PLAT_ECC_INDICT_ECACHE_CORRECTABLES }, 884 { "l3cachedata", PLAT_ECC_INDICT_ECACHE_CORRECTABLES }, 885 { "l2cachedata", PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE }, 886 { "l3cachedata", PLAT_ECC_INDICT_ECACHE_UNCORRECTABLE } 887 }; 888 889 /* 890 * The following table represents mapping between the indictment2 reason 891 * to its type. 892 */ 893 894 static plat_ecc_bl_map_t plat_ecc_bl_map_v2[] = { 895 { "l2cachedata", PLAT_ECC_INDICT2_L2_SERD }, 896 { "l3cachedata", PLAT_ECC_INDICT2_L3_SERD }, 897 { "l2cachedata", PLAT_ECC_INDICT2_L2_UE }, 898 { "l3cachedata", PLAT_ECC_INDICT2_L3_UE }, 899 { "l2cachetag", PLAT_ECC_INDICT2_L2_TAG_SERD }, 900 { "l3cachetag", PLAT_ECC_INDICT2_L3_TAG_SERD }, 901 { "icache", PLAT_ECC_INDICT2_ICACHE_SERD }, 902 { "dcache", PLAT_ECC_INDICT2_DCACHE_SERD }, 903 { "pcache", PLAT_ECC_INDICT2_PCACHE_SERD }, 904 { "itlb", PLAT_ECC_INDICT2_ITLB_SERD }, 905 { "dtlb", PLAT_ECC_INDICT2_DTLB_SERD }, 906 { "fpu", PLAT_ECC_INDICT2_FPU } 907 }; 908 909 /* 910 * The following function returns the indictment type for a given version 911 */ 912 static int 913 flt_name_to_msg_type(const char *fault, int indict_version) 914 { 915 plat_ecc_bl_map_t *mapp; 916 char *fltnm = "fault.cpu."; 917 int mapsz; 918 char *p; 919 int i; 920 921 /* Check if it starts with proper fault name */ 922 if (strncmp(fault, fltnm, strlen(fltnm)) != 0) 923 return (PLAT_ECC_INDICT_NONE); 924 925 fault += strlen(fltnm); /* c = "ultraSPARC-IV.icache" */ 926 927 /* Skip the cpu type */ 928 if ((p = strchr(fault, '.')) == NULL) 929 return (PLAT_ECC_INDICT_NONE); 930 931 p++; /* skip the "." */ 932 933 if (indict_version == 0) { 934 mapp = plat_ecc_bl_map_v1; 935 mapsz = sizeof (plat_ecc_bl_map_v1) / 936 sizeof (plat_ecc_bl_map_t); 937 } else { 938 mapp = plat_ecc_bl_map_v2; 939 mapsz = sizeof (plat_ecc_bl_map_v2) / 940 sizeof (plat_ecc_bl_map_t); 941 } 942 for (i = 0; i < mapsz; i++) { 943 if (strcmp(p, mapp[i].ebm_reason) == 0) { 944 return (mapp[i].ebm_type); 945 } 946 } 947 return (PLAT_ECC_INDICT_NONE); 948 } 949 950 /* 951 * Blacklisting 952 */ 953 int 954 plat_blacklist(int cmd, const char *scheme, nvlist_t *fmri, const char *class) 955 { 956 struct async_flt aflt; 957 char *unum; 958 int msg_type, is_old_indict; 959 960 if (fmri == NULL) 961 return (EINVAL); 962 if (cmd != BLIOC_INSERT) 963 return (ENOTSUP); 964 965 /* 966 * We support both the blacklisting of CPUs via mem-schemed 967 * FMRIs that name E$ J-numbers, and CPUs via cpu-schemed FMRIs 968 * that name the cpuid. 969 */ 970 if (strcmp(scheme, FM_FMRI_SCHEME_MEM) == 0) { 971 if (nvlist_lookup_string(fmri, FM_FMRI_MEM_UNUM, &unum)) 972 return (EINVAL); 973 aflt.flt_inst = (uint_t)-1; 974 } else if (strcmp(scheme, FM_FMRI_SCHEME_CPU) == 0) { 975 if (nvlist_lookup_uint32(fmri, FM_FMRI_CPU_ID, &aflt.flt_inst)) 976 return (EINVAL); 977 unum = NULL; 978 } else { 979 return (ENOTSUP); 980 } 981 982 /* 983 * If the SC cannot handle indictment2, so fall back to old one. 984 * Also if the domain does not support FMA, then send only the old one. 985 */ 986 987 is_old_indict = plat_ecc_capability_sc_get(PLAT_ECC_INDICTMENT_MESSAGE); 988 989 if (is_old_indict) 990 msg_type = flt_name_to_msg_type(class, 0); 991 else 992 msg_type = flt_name_to_msg_type(class, 1); 993 994 if (msg_type == PLAT_ECC_INDICT_NONE) 995 return (ENOTSUP); 996 997 /* 998 * The current blacklisting interfaces are designed for a world where 999 * the SC is much more involved in the diagnosis and error reporting 1000 * process than it is in the FMA world. As such, the existing 1001 * interfaces want all kinds of information about the error that's 1002 * triggering the blacklist. In the FMA world, we don't have access 1003 * to any of that information by the time we're doing the blacklist, 1004 * so we fake values. 1005 */ 1006 aflt.flt_id = gethrtime(); 1007 aflt.flt_addr = -1; 1008 aflt.flt_stat = -1; 1009 aflt.flt_synd = (ushort_t)-1; 1010 1011 if (is_old_indict) { 1012 if (unum && unum[0] != '\0') 1013 return (plat_log_fruid_indictment(msg_type, &aflt, 1014 unum)); 1015 else 1016 return (ENOTSUP); 1017 } else { 1018 return (plat_log_fruid_indictment2(msg_type, &aflt, unum)); 1019 } 1020 } 1021 1022 static kcondvar_t plat_ecc_condvar; 1023 static kmutex_t plat_ecc_mutex; 1024 static taskq_t *plat_ecc_taskq; 1025 1026 /* 1027 * plat_ecc_dispatch_task: Dispatch the task on a taskq and wait for the 1028 * return value. We use cv_wait_sig to wait for the return values. If a 1029 * signal interrupts us, we return EINTR. Otherwise, we return the value 1030 * returned by the mailbox functions. 1031 * 1032 * To avoid overloading the lower-level mailbox routines, we use a taskq 1033 * to serialize all messages. Currently, it is expected that only one 1034 * process (fmd) will use this ioctl, so the delay caused by the taskq 1035 * should not have much of an effect. 1036 */ 1037 int 1038 plat_ecc_dispatch_task(plat_ecc_message_t *msg) 1039 { 1040 int ret; 1041 1042 ASSERT(msg != NULL); 1043 ASSERT(plat_ecc_taskq != NULL); 1044 1045 if (taskq_dispatch(plat_ecc_taskq, plat_ecc_send_msg, 1046 (void *)msg, TQ_NOSLEEP) == TASKQID_INVALID) { 1047 kmem_free(msg->ecc_msg_data, msg->ecc_msg_len); 1048 kmem_free(msg, sizeof (plat_ecc_message_t)); 1049 return (ENOMEM); 1050 } 1051 mutex_enter(&plat_ecc_mutex); 1052 1053 /* 1054 * It's possible that the taskq function completed before we 1055 * acquired the mutex. Check for this first. If this did not 1056 * happen, we wait for the taskq function to signal us, or an 1057 * interrupt. We also check ecc_msg_status to protect against 1058 * spurious wakeups from cv_wait_sig. 1059 */ 1060 if (msg->ecc_msg_status == PLAT_ECC_MSG_SENT) { 1061 ret = msg->ecc_msg_ret; 1062 kmem_free(msg->ecc_msg_data, msg->ecc_msg_len); 1063 kmem_free(msg, sizeof (plat_ecc_message_t)); 1064 } else { 1065 msg->ecc_msg_status = PLAT_ECC_TASK_DISPATCHED; 1066 1067 while ((ret = cv_wait_sig(&plat_ecc_condvar, 1068 &plat_ecc_mutex)) != 0 && 1069 msg->ecc_msg_status == PLAT_ECC_TASK_DISPATCHED) 1070 ; 1071 1072 if ((ret == 0) && (msg->ecc_msg_status != PLAT_ECC_MSG_SENT)) { 1073 /* An interrupt was received */ 1074 msg->ecc_msg_status = PLAT_ECC_INTERRUPT_RECEIVED; 1075 ret = EINTR; 1076 } else { 1077 ret = msg->ecc_msg_ret; 1078 kmem_free(msg->ecc_msg_data, msg->ecc_msg_len); 1079 kmem_free(msg, sizeof (plat_ecc_message_t)); 1080 } 1081 } 1082 mutex_exit(&plat_ecc_mutex); 1083 return (ret); 1084 } 1085 1086 static void 1087 plat_ecc_send_msg(void *arg) 1088 { 1089 plat_ecc_message_t *msg = arg; 1090 int ret; 1091 1092 /* 1093 * Send this data off as a mailbox message to the SC. 1094 */ 1095 ret = plat_send_ecc_mailbox_msg(msg->ecc_msg_type, msg->ecc_msg_data); 1096 1097 mutex_enter(&plat_ecc_mutex); 1098 1099 /* 1100 * If the dispatching function received an interrupt, don't bother 1101 * signalling it, and throw away the results. Otherwise, set the 1102 * return value and signal the condvar. 1103 */ 1104 if (msg->ecc_msg_status == PLAT_ECC_INTERRUPT_RECEIVED) { 1105 kmem_free(msg->ecc_msg_data, msg->ecc_msg_len); 1106 kmem_free(msg, sizeof (plat_ecc_message_t)); 1107 } else { 1108 msg->ecc_msg_ret = ret; 1109 msg->ecc_msg_status = PLAT_ECC_MSG_SENT; 1110 cv_broadcast(&plat_ecc_condvar); 1111 } 1112 1113 mutex_exit(&plat_ecc_mutex); 1114 } 1115 1116 void 1117 plat_ecc_init(void) 1118 { 1119 int bd; 1120 1121 mutex_init(&plat_ecc_mutex, NULL, MUTEX_DEFAULT, NULL); 1122 cv_init(&plat_ecc_condvar, NULL, CV_DEFAULT, NULL); 1123 plat_ecc_taskq = taskq_create("plat_ecc_taskq", 1, minclsyspri, 1124 PLAT_ECC_TASKQ_MIN, PLAT_ECC_TASKQ_MAX, TASKQ_PREPOPULATE); 1125 ASSERT(plat_ecc_taskq != NULL); 1126 1127 for (bd = 0; bd < plat_max_cpumem_boards(); bd++) { 1128 mutex_init(&domain_dimm_sids[bd].pdsb_lock, 1129 NULL, MUTEX_DEFAULT, NULL); 1130 } 1131 1132 } 1133