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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains the Starcat Solaris Mailbox Client module. This module 29 * handles mailbox messages from the SC to the OS (as opposed to messages sent 30 * to specific drivers) and vice versa. Two task queues are created upon 31 * startup; one handles reading and processing of all incoming messages, while 32 * the other handles transmission of all outgoing messages. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/sysmacros.h> 39 #include <sys/sunddi.h> 40 #include <sys/errno.h> 41 #include <sys/cmn_err.h> 42 #include <sys/condvar.h> 43 #include <sys/mutex.h> 44 #include <sys/disp.h> 45 #include <sys/thread.h> 46 #include <sys/debug.h> 47 #include <sys/cpu_sgnblk_defs.h> 48 #include <sys/machsystm.h> 49 #include <sys/modctl.h> 50 #include <sys/iosramio.h> 51 #include <sys/mboxsc.h> 52 #include <sys/promif.h> 53 #include <sys/uadmin.h> 54 #include <sys/cred.h> 55 #include <sys/taskq.h> 56 #include <sys/utsname.h> 57 #include <sys/plat_ecc_unum.h> 58 #include <sys/fm/protocol.h> 59 #include <sys/fm/util.h> 60 #include <sys/starcat.h> 61 #include <sys/plat_ecc_dimm.h> 62 #include <sys/plat_datapath.h> 63 64 /* mailbox keys */ 65 #define SCDM_KEY 0x5343444d /* 'S', 'C', 'D', 'M' */ 66 #define DMSC_KEY 0x444d5343 /* 'D', 'M', 'S', 'C' */ 67 68 /* mailbox commands */ 69 #define SCDM_CMD ('S' << 8) /* generic SSP */ 70 #define SCDM_CMD_SUCCESS (SCDM_CMD | 0x1) 71 #define SCDM_GOTO_OBP (SCDM_CMD | 0x2) 72 #define SCDM_GOTO_PANIC (SCDM_CMD | 0x3) 73 #define SCDM_ENVIRON (SCDM_CMD | 0x4) /* environmental intr */ 74 #define SCDM_SHUTDOWN (SCDM_CMD | 0x5) /* setkeyswitch STANDBY */ 75 #define SCDM_GET_NODENAME (SCDM_CMD | 0x6) /* get domain nodename */ 76 #define SCDM_LOG_ECC_ERROR (SCDM_CMD | 0x7) /* ECC error logging */ 77 #define SCDM_LOG_ECC_INDICTMENT (SCDM_CMD | 0x8) /* ECC indictment logging */ 78 #define SCDM_LOG_ECC (SCDM_CMD | 0x9) /* ECC info */ 79 #define SCDM_LOG_ECC_CAP_INIT (SCDM_CMD | 0xa) /* ECC Capability Init */ 80 #define SCDM_LOG_ECC_CAP_RESP (SCDM_CMD | 0xb) /* ECC Capability Response */ 81 #define SCDM_DIMM_SERIAL_ID (SCDM_CMD | 0xc) /* DIMM ser# req/resp */ 82 #define SCDM_DP_ERROR_MSG (SCDM_CMD | 0xd) /* datapath error */ 83 #define SCDM_DP_FAULT_MSG (SCDM_CMD | 0xe) /* datapath fault */ 84 85 /* general constants */ 86 #define GETMSG_TIMEOUT_MS 500 87 #define PUTMSG_TIMEOUT_MS 6000 88 #define MIN_INPUTQ_TASKS 2 89 #define MAX_INPUTQ_TASKS 4 90 #define MIN_OUTPUTQ_TASKS 2 91 #define MAX_OUTPUTQ_TASKS 512 92 #ifndef TRUE 93 #define TRUE 1 94 #endif 95 #ifndef FALSE 96 #define FALSE 0 97 #endif 98 99 clock_t ecc_message_timeout_ms = PUTMSG_TIMEOUT_MS; 100 101 /* 102 * When a message needs to be sent to the SC, an scosmb_msgdata_t should be 103 * populated with the data to be used for the message, and a call to 104 * scosmb_process_output should be dispatched on the scosmb_output_taskq, with 105 * the address of the scosmb_msgdata_t structure as its arg. The "length" and 106 * "data" fields can be used if the message needs to include data beyond the 107 * header fields (type, cmd, and transid) and that information must be recorded 108 * when the message is placed on the taskq. If appropriate for the message type 109 * (e.g. nodename info that should always be the most recent available), the 110 * "data" field can be set to NULL and the additional data can be assembled 111 * immediately prior to sending the message in scosmb_process_output(). 112 * 113 * If log_error is set, any errors in delivering the message cause a 114 * cmn_err() message to be issued. If it is zero, the error is expressed 115 * only through return values. 116 */ 117 typedef struct { 118 uint32_t type; 119 uint32_t cmd; 120 uint64_t transid; 121 uint32_t length; 122 int log_error; 123 void *data; 124 } scosmb_msgdata_t; 125 126 /* 127 * Datapath error and fault messages arrive unsolicited. The message data 128 * is contained in a plat_datapath_info_t structure. 129 */ 130 typedef struct { 131 uint8_t type; /* CDS, DX, EX, CP */ 132 uint8_t pad; /* for alignment */ 133 uint16_t cpuid; /* Safari ID of base CPU */ 134 uint32_t t_value; /* SERD timeout threshold (seconds) */ 135 } plat_datapath_info_t; 136 137 /* externally visible routines */ 138 void scosmb_update_nodename(uint64_t transid); 139 140 /* local routines */ 141 static void scosmb_inbox_handler(); 142 static void scosmb_process_input(void *unused); 143 static int scosmb_process_output(scosmb_msgdata_t *arg); 144 145 /* local variables */ 146 static uint8_t scosmb_mboxsc_failed = FALSE; 147 static uint8_t scosmb_mboxsc_timedout = FALSE; 148 static uint8_t scosmb_nodename_event_pending = FALSE; 149 static char scosmb_hdr[] = "SCOSMB:"; 150 static kmutex_t scosmb_mutex; 151 static taskq_t *scosmb_input_taskq = NULL; 152 static taskq_t *scosmb_output_taskq = NULL; 153 154 static char *dperrtype[] = { 155 DP_ERROR_CDS, 156 DP_ERROR_DX, 157 DP_ERROR_EX, 158 DP_ERROR_CP 159 }; 160 161 /* 162 * Structures from modctl.h used for loadable module support. 163 * SCOSMB is a "miscellaneous" module. 164 */ 165 extern struct mod_ops mod_miscops; 166 167 static struct modlmisc modlmisc = { 168 &mod_miscops, 169 "Sun Fire 15000 OS Mbox Client v1.10", 170 }; 171 172 static struct modlinkage modlinkage = { 173 MODREV_1, 174 (void *)&modlmisc, 175 NULL 176 }; 177 178 179 /* 180 * _init 181 * 182 * Loadable module support routine. Initializes mutex and condition variables 183 * and starts thread. 184 */ 185 int 186 _init(void) 187 { 188 int error; 189 190 /* 191 * Initialize the mailboxes 192 */ 193 if ((error = mboxsc_init(SCDM_KEY, MBOXSC_MBOX_IN, 194 scosmb_inbox_handler)) != 0) { 195 cmn_err(CE_WARN, "%s mboxsc_init failed (0x%x)\n", scosmb_hdr, 196 error); 197 return (error); 198 } 199 200 if ((error = mboxsc_init(DMSC_KEY, MBOXSC_MBOX_OUT, NULL)) != 0) { 201 cmn_err(CE_WARN, "%s mboxsc_init failed (0x%x)\n", scosmb_hdr, 202 error); 203 (void) mboxsc_fini(SCDM_KEY); 204 return (error); 205 } 206 207 /* 208 * Initialize the global lock 209 */ 210 mutex_init(&scosmb_mutex, NULL, MUTEX_DEFAULT, NULL); 211 212 /* 213 * Create the task queues used for processing input and output messages 214 */ 215 scosmb_input_taskq = taskq_create("scosmb_input_taskq", 1, 216 minclsyspri, MIN_INPUTQ_TASKS, MAX_INPUTQ_TASKS, TASKQ_PREPOPULATE); 217 scosmb_output_taskq = taskq_create("scosmb_output_taskq", 1, 218 minclsyspri, MIN_OUTPUTQ_TASKS, MAX_OUTPUTQ_TASKS, 219 TASKQ_PREPOPULATE); 220 221 /* 222 * Attempt to install the module. If unsuccessful, uninitialize 223 * everything. 224 */ 225 error = mod_install(&modlinkage); 226 if (error != 0) { 227 taskq_destroy(scosmb_output_taskq); 228 taskq_destroy(scosmb_input_taskq); 229 mutex_destroy(&scosmb_mutex); 230 (void) mboxsc_fini(DMSC_KEY); 231 (void) mboxsc_fini(SCDM_KEY); 232 } 233 234 return (error); 235 } 236 237 /* 238 * _fini 239 * 240 * Loadable module support routine. Since this routine shouldn't be unloaded (it 241 * provides a critical service, and its symbols may be referenced externally), 242 * EBUSY is returned to prevent unloading. 243 */ 244 int 245 _fini(void) 246 { 247 return (EBUSY); 248 } 249 250 /* 251 * _info 252 * 253 * Loadable module support routine. 254 */ 255 int 256 _info(struct modinfo *modinfop) 257 { 258 int error = 0; 259 260 error = mod_info(&modlinkage, modinfop); 261 return (error); 262 } 263 264 /* 265 * scosmb_inbox_handler() - mbox API event handler. 266 * 267 * This routine adds an entry to the scosmb_input_taskq that will cause the 268 * scosmb_process_input() routine to be called to service the SCDM mailbox. The 269 * possibility that taskq_dispatch may fail when given KM_NOSLEEP is safely 270 * ignored because there can only be one message waiting in the mailbox at any 271 * given time, so the current message will end up being handled by one of the 272 * previously queued jobs (and a previous message presumably timed out before we 273 * got around to reading it). 274 */ 275 static void 276 scosmb_inbox_handler() 277 { 278 (void) taskq_dispatch(scosmb_input_taskq, scosmb_process_input, NULL, 279 KM_NOSLEEP); 280 } 281 282 /* 283 * dp_get_cores() 284 * 285 * Checks cpu implementation for the input cpuid and returns 286 * the number of cores. 287 * If implementation cannot be determined, returns 1 288 */ 289 static int 290 dp_get_cores(uint16_t cpuid) 291 { 292 int exp, ii, impl = 0, nc, slot; 293 294 exp = STARCAT_CPUID_TO_EXPANDER(cpuid); 295 slot = STARCAT_CPUID_TO_BOARDSLOT(cpuid); 296 if (slot == 1) 297 nc = STARCAT_SLOT1_CPU_MAX; 298 else 299 nc = plat_max_cpu_units_per_board(); 300 301 /* find first with valid implementation */ 302 for (ii = 0; ii < nc; ii++) 303 if (cpu[MAKE_CPUID(exp, slot, ii)]) { 304 impl = cpunodes[MAKE_CPUID(exp, slot, ii)]. 305 implementation; 306 break; 307 } 308 309 if (IS_JAGUAR(impl) || IS_PANTHER(impl)) 310 return (2); 311 else 312 return (1); 313 314 } 315 316 /* 317 * dp_payload_add_cpus() 318 * 319 * From datapath mailbox message, determines the number of and safari IDs 320 * for affected cpus, then adds this info to the datapath ereport. 321 * 322 * Input maxcat (if set) is a count of maxcat cpus actually present - it is 323 * a count of cpuids, which takes into account multi-core architecture. 324 */ 325 static int 326 dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp, int maxcat) 327 { 328 int jj = 0, numcpus = 0, nummaxcpus = 0; 329 int count, exp, ii, num, ncores, ret, slot, port; 330 uint16_t *dparray, cpuid; 331 uint64_t *snarray; 332 333 /* check for multiple core architectures */ 334 ncores = dp_get_cores(dpmsg->cpuid); 335 336 /* 337 * Determine the number of cpu cores impacted 338 */ 339 switch (dpmsg->type) { 340 case DP_CDS_TYPE: 341 if (maxcat) 342 nummaxcpus = ncores; 343 else 344 numcpus = ncores; 345 break; 346 347 case DP_DX_TYPE: 348 if (maxcat) 349 nummaxcpus = 2 * ncores; 350 else 351 numcpus = 2 * ncores; 352 break; 353 354 case DP_EX_TYPE: 355 if (maxcat) 356 nummaxcpus = STARCAT_SLOT1_CPU_MAX; 357 else 358 numcpus = plat_max_cpu_units_per_board(); 359 break; 360 361 case DP_CP_TYPE: 362 /* 363 * SC-DE supplies the base cpuid affected, if 364 * maxcat id was given, there's no slot 0 board 365 * present. 366 */ 367 368 if (!maxcat) { 369 /* Slot 0 id was given - set numcpus */ 370 numcpus = plat_max_cpu_units_per_board(); 371 } 372 373 /* there may/may not be maxcats. set a count anyway */ 374 nummaxcpus = STARCAT_SLOT1_CPU_MAX; 375 376 break; 377 378 default: 379 ASSERT(0); 380 return (-1); 381 } 382 383 /* Allocate space for cores */ 384 num = numcpus + nummaxcpus; 385 dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP); 386 387 /* 388 * populate dparray with impacted cores (only those present) 389 */ 390 exp = STARCAT_CPUID_TO_EXPANDER(dpmsg->cpuid); 391 slot = STARCAT_CPUID_TO_BOARDSLOT(dpmsg->cpuid); 392 port = STARCAT_CPUID_TO_LPORT(dpmsg->cpuid); 393 394 mutex_enter(&cpu_lock); 395 396 switch (dpmsg->type) { 397 case DP_CDS_TYPE: 398 /* 399 * For a CDS error, it's the reporting cpuid 400 * and it's other core (if present) 401 */ 402 cpuid = dpmsg->cpuid & 0xFFFB; /* core 0 */ 403 if (cpu[cpuid]) 404 dparray[jj++] = cpuid; 405 406 cpuid = dpmsg->cpuid | 0x4; /* core 1 */ 407 if (cpu[cpuid]) 408 dparray[jj++] = cpuid; 409 break; 410 411 case DP_DX_TYPE: 412 /* 413 * For a DX error, it's the reporting cpuid (all 414 * cores), and the other CPU sharing the same 415 * DX<-->DCDS interface (all cores) 416 */ 417 418 /* reporting cpuid */ 419 cpuid = dpmsg->cpuid & 0xFFFB; /* core 0 */ 420 421 if (cpu[cpuid]) 422 dparray[jj++] = cpuid; 423 424 cpuid = dpmsg->cpuid | 0x4; /* core 1 */ 425 if (cpu[cpuid]) 426 dparray[jj++] = cpuid; 427 428 /* find partner cpuid */ 429 if (port == 0 || port == 2) 430 cpuid = dpmsg->cpuid | 0x1; 431 else 432 cpuid = dpmsg->cpuid & 0xFFFE; 433 434 /* add partner cpuid */ 435 cpuid &= 0xFFFB; /* core 0 */ 436 if (cpu[cpuid]) 437 dparray[jj++] = cpuid; 438 439 cpuid |= 0x4; /* core 1 */ 440 if (cpu[cpuid]) 441 dparray[jj++] = cpuid; 442 break; 443 444 case DP_EX_TYPE: 445 /* 446 * For an EX error, it is all cpuids (all cores) 447 * on the reporting board 448 */ 449 450 if (slot == 1) /* maxcat */ 451 count = nummaxcpus; 452 else 453 count = numcpus; 454 455 for (ii = 0; ii < count; ii++) { 456 cpuid = MAKE_CPUID(exp, slot, ii); 457 if (cpu[cpuid]) 458 dparray[jj++] = cpuid; 459 } 460 break; 461 462 case DP_CP_TYPE: 463 /* 464 * For a CP error, it is all cpuids (all cores) 465 * on both boards (SB & IO) in the boardset 466 */ 467 468 /* Do slot 0 */ 469 for (ii = 0; ii < numcpus; ii++) { 470 cpuid = MAKE_CPUID(exp, 0, ii); 471 if (cpu[cpuid]) 472 dparray[jj++] = cpuid; 473 } 474 475 /* Do slot 1 */ 476 for (ii = 0; ii < nummaxcpus; ii++) { 477 cpuid = MAKE_CPUID(exp, 1, ii); 478 if (cpu[cpuid]) 479 dparray[jj++] = cpuid; 480 } 481 break; 482 } 483 484 mutex_exit(&cpu_lock); 485 486 /* 487 * The datapath message could not be associated with any 488 * configured CPU. 489 */ 490 if (!jj) { 491 kmem_free(dparray, num * sizeof (uint16_t *)); 492 ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj); 493 ASSERT(ret == 0); 494 return (-1); 495 } 496 497 snarray = kmem_zalloc(jj * sizeof (uint64_t *), KM_SLEEP); 498 for (ii = 0; ii < jj; ii++) 499 snarray[ii] = cpunodes[dparray[ii]].device_id; 500 501 ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj); 502 ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj); 503 ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj); 504 ASSERT(ret == 0); 505 506 kmem_free(dparray, num * sizeof (uint16_t *)); 507 kmem_free(snarray, jj * sizeof (uint64_t *)); 508 509 return (0); 510 } 511 512 /* 513 * dp_trans_event() - datapath message handler. 514 * 515 * Process datapath error and fault messages received from the SC. Checks 516 * for, and disregards, messages associated with I/O boards. Otherwise, 517 * extracts message info to produce a datapath ereport. 518 */ 519 static void 520 dp_trans_event(plat_datapath_info_t *dpmsg, int msgtype) 521 { 522 nvlist_t *erp, *detector, *hcelem; 523 char buf[FM_MAX_CLASS]; 524 int exp, slot, i, maxcat = 0; 525 526 /* check for I/O board message */ 527 exp = STARCAT_CPUID_TO_EXPANDER(dpmsg->cpuid); 528 slot = STARCAT_CPUID_TO_BOARDSLOT(dpmsg->cpuid); 529 530 if (slot) { 531 mutex_enter(&cpu_lock); 532 for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) { 533 if (cpu[MAKE_CPUID(exp, slot, i)]) { 534 /* maxcat cpu present */ 535 maxcat++; 536 } 537 } 538 mutex_exit(&cpu_lock); 539 540 /* 541 * Ignore I/O board msg 542 */ 543 if (maxcat == 0) 544 return; 545 } 546 547 /* allocate space for ereport */ 548 erp = fm_nvlist_create(NULL); 549 550 /* 551 * 552 * Member Name Data Type Comments 553 * ----------- --------- ----------- 554 * version uint8 0 555 * class string "asic" 556 * ENA uint64 ENA Format 1 557 * detector fmri aggregated ID data for SC-DE 558 * 559 * Datapath ereport subclasses and data payloads: 560 * There will be two types of ereports (error and fault) which will be 561 * identified by the "type" member. 562 * 563 * ereport.asic.starcat.cds.cds-dp 564 * ereport.asic.starcat.dx.dx-dp 565 * ereport.asic.starcat.sdi.sdi-dp 566 * ereport.asic.starcat.cp.cp-dp 567 * 568 * Member Name Data Type Comments 569 * ----------- --------- ----------- 570 * erptype uint16 derived from message type: error or 571 * fault 572 * t-value uint32 SC's datapath SERD timeout threshold 573 * dp-list-sz uint8 number of dp-list array elements 574 * dp-list array of uint16 Safari IDs of affected cpus 575 * sn-list array of uint64 Serial numbers of affected cpus 576 * 577 */ 578 579 /* compose common ereport elements */ 580 detector = fm_nvlist_create(NULL); 581 582 /* 583 * Create legacy FMRI for the detector 584 */ 585 switch (dpmsg->type) { 586 case DP_CDS_TYPE: 587 case DP_DX_TYPE: 588 if (slot == 1) 589 (void) snprintf(buf, FM_MAX_CLASS, "IO%d", exp); 590 else 591 (void) snprintf(buf, FM_MAX_CLASS, "SB%d", exp); 592 break; 593 594 case DP_EX_TYPE: 595 (void) snprintf(buf, FM_MAX_CLASS, "EX%d", exp); 596 break; 597 598 case DP_CP_TYPE: 599 (void) snprintf(buf, FM_MAX_CLASS, "CP"); 600 break; 601 602 default: 603 (void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN"); 604 break; 605 } 606 607 hcelem = fm_nvlist_create(NULL); 608 609 (void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC); 610 (void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf); 611 612 (void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION); 613 (void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC); 614 (void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, ""); 615 (void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1); 616 (void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1); 617 618 /* build ereport class name */ 619 (void) snprintf(buf, FM_MAX_CLASS, "asic.starcat.%s.%s-%s", 620 dperrtype[dpmsg->type], dperrtype[dpmsg->type], 621 FM_ERROR_DATAPATH); 622 623 fm_ereport_set(erp, FM_EREPORT_VERSION, buf, 624 fm_ena_generate(0, FM_ENA_FMT1), detector, NULL); 625 626 /* add payload elements */ 627 if (msgtype == SCDM_DP_ERROR_MSG) { 628 fm_payload_set(erp, 629 DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL); 630 } else { 631 fm_payload_set(erp, 632 DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL); 633 } 634 635 fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL); 636 637 if (dp_payload_add_cpus(dpmsg, erp, maxcat) == 0) { 638 /* post ereport */ 639 fm_ereport_post(erp, EVCH_SLEEP); 640 } 641 642 /* free ereport memory */ 643 fm_nvlist_destroy(erp, FM_NVA_FREE); 644 fm_nvlist_destroy(detector, FM_NVA_FREE); 645 646 } 647 648 /* 649 * scosmb_process_input() - incoming message processing routine 650 * 651 * this routine attempts to read a message from the SCDM mailbox and, if 652 * successful, processes the command. if an unrecoverable error is encountered, 653 * the scosmb_task thread will be terminated. 654 */ 655 /* ARGSUSED0 */ 656 static void 657 scosmb_process_input(void *unused) 658 { 659 int error; 660 scosmb_msgdata_t msg; 661 proc_t *initpp; 662 plat_capability_data_t *cap; /* capability msg contents ptr */ 663 int cap_size; 664 int cap_ver_len; 665 scosmb_msgdata_t *cap_msgdatap; /* capability msg response */ 666 int max_size; 667 668 /* 669 * Attempt to read a message from the SCDM mailbox. 670 * 671 * Setup a local buffer to read incoming messages from the SC. 672 */ 673 cap_ver_len = strlen(utsname.release) + strlen(utsname.version) + 2; 674 cap_size = sizeof (plat_capability_data_t) + cap_ver_len; 675 max_size = MAX(cap_size, sizeof (plat_dimm_sid_board_data_t)); 676 677 msg.type = 0; 678 msg.cmd = 0; 679 msg.transid = 0; 680 msg.length = max_size; 681 msg.log_error = 0; 682 msg.data = kmem_zalloc(max_size, KM_SLEEP); 683 684 error = mboxsc_getmsg(SCDM_KEY, &msg.type, &msg.cmd, &msg.transid, 685 &msg.length, msg.data, GETMSG_TIMEOUT_MS); 686 687 /* 688 * If EAGAIN or ETIMEDOUT was received, give up. The SC can just try 689 * again if it was important. If any other non-zero error was 690 * encountered, the mailbox service is broken, and there's nothing more 691 * we can do. 692 */ 693 mutex_enter(&scosmb_mutex); 694 if ((error == EAGAIN) || (error == ETIMEDOUT)) { 695 mutex_exit(&scosmb_mutex); 696 return; 697 } else if (error != 0) { 698 /* 699 * The mailbox service appears to be badly broken. If it was 700 * working previously, generate a warning and set a flag to 701 * avoid repeating the warning on subsequent failures. 702 */ 703 if (!scosmb_mboxsc_failed) { 704 scosmb_mboxsc_failed = TRUE; 705 cmn_err(CE_WARN, "%s mboxsc error (0x%x)\n", scosmb_hdr, 706 error); 707 } 708 mutex_exit(&scosmb_mutex); 709 return; 710 } else { 711 /* 712 * If the mailbox module failed previously, it appears to have 713 * recovered, so we'll want to generate a warning if it fails 714 * again. 715 */ 716 scosmb_mboxsc_failed = FALSE; 717 } 718 mutex_exit(&scosmb_mutex); 719 720 /* 721 * A message was successfully received, so go ahead and process it. 722 */ 723 switch (msg.cmd) { 724 725 case SCDM_GOTO_OBP: /* jump to OBP */ 726 debug_enter("SC requested jump to OBP"); 727 break; 728 729 case SCDM_GOTO_PANIC: /* Panic the domain */ 730 cmn_err(CE_PANIC, "%s SC requested PANIC\n", scosmb_hdr); 731 break; 732 733 case SCDM_SHUTDOWN: /* graceful shutdown */ 734 cmn_err(CE_WARN, "%s SC requested a shutdown ", scosmb_hdr); 735 (void) kadmin(A_SHUTDOWN, AD_HALT, NULL, kcred); 736 /* 737 * In the event kadmin does not bring down the 738 * domain, environmental shutdown is forced 739 */ 740 /*FALLTHROUGH*/ 741 case SCDM_ENVIRON: /* environmental shutdown */ 742 /* 743 * Send SIGPWR to init(1) it will run rc0, 744 * which will uadmin to power down. 745 */ 746 mutex_enter(&pidlock); 747 initpp = prfind(P_INITPID); 748 mutex_exit(&pidlock); 749 750 751 /* 752 * If we're still booting and init(1) isn't set up yet, 753 * simply halt. 754 */ 755 if (initpp == NULL) { 756 extern void halt(char *); 757 cmn_err(CE_WARN, "%s Environmental Interrupt", 758 scosmb_hdr); 759 power_down((char *)NULL); 760 halt("Power off the System!\n"); 761 } 762 763 /* 764 * else, graceful shutdown with inittab and all 765 * getting involved 766 */ 767 psignal(initpp, SIGPWR); 768 break; 769 770 case SCDM_GET_NODENAME: 771 scosmb_update_nodename(msg.transid); 772 break; 773 774 case SCDM_LOG_ECC_CAP_RESP: 775 /* 776 * The SC has responded to our initiator capability message 777 * issued during the boot flow via scosmb_update_nodename(). 778 * 779 * Parse the incoming data, and appropriately set SC 780 * capabilities... 781 */ 782 cap = (plat_capability_data_t *)msg.data; 783 plat_ecc_capability_sc_set(cap->capd_capability); 784 break; 785 786 case SCDM_LOG_ECC_CAP_INIT: 787 /* 788 * The SC has initiated a capability messaging exchange with 789 * the OS. 790 * 791 * We start out just as we do for an SC response capability 792 * message, a parse of incoming data to appropriately set SC 793 * described capabilities... 794 */ 795 cap = (plat_capability_data_t *)msg.data; 796 plat_ecc_capability_sc_set(cap->capd_capability); 797 /* 798 * The next step is setting up our Response to the SC. 799 * 800 * Allocate memory for message data, initialize appropriately, 801 * and place a new job on the scosmb_output_taskq for 802 * SCDM_LOG_ECC_CAP_RESP, our OS capability messaging response 803 * to the SC initiated sequence detected here. 804 */ 805 cap_msgdatap = kmem_zalloc(sizeof (scosmb_msgdata_t), KM_SLEEP); 806 cap_msgdatap->type = MBOXSC_MSG_EVENT; 807 cap_msgdatap->cmd = SCDM_LOG_ECC_CAP_RESP; 808 cap_msgdatap->transid = 0; 809 (void) taskq_dispatch(scosmb_output_taskq, 810 (task_func_t *)scosmb_process_output, cap_msgdatap, 811 KM_SLEEP); 812 break; 813 814 case SCDM_DP_ERROR_MSG: 815 case SCDM_DP_FAULT_MSG: 816 dp_trans_event(msg.data, msg.cmd); 817 break; 818 819 case SCDM_DIMM_SERIAL_ID: 820 (void) plat_store_mem_sids(msg.data); 821 break; 822 823 default: 824 cmn_err(CE_WARN, "%s invalid command (0x%x)\n", scosmb_hdr, 825 msg.cmd); 826 break; 827 } 828 829 /* 830 * Free up buffer for incoming messasge data that we allocated earlier 831 */ 832 kmem_free(msg.data, max_size); 833 } 834 835 /* 836 * scosmb_process_output() - outgoing message processing routine 837 * 838 * This routine handles jobs that are queued on the scosmb_output_taskq, or 839 * sent directly from scosmb_log_ecc_error. Each job corresponds to a single 840 * mailbox message that needs to be sent to the SC via the DMSC mailbox. Some 841 * processing of the message may be performed before it is sent to the SC, 842 * depending on the value of the command field. 843 */ 844 static int 845 scosmb_process_output(scosmb_msgdata_t *msgdatap) 846 { 847 int error; 848 int length; 849 char nodename[_SYS_NMLN]; 850 void *free_data; 851 int free_data_len; 852 int cap_size; 853 int cap_ver_len; 854 plat_capability_data_t *cap = NULL; 855 856 /* 857 * This shouldn't ever happen, but it can't hurt to check anyway. 858 */ 859 if (msgdatap == NULL) { 860 return (EINVAL); 861 } 862 863 /* 864 * If data was passed in, we'll need to free it before returning. 865 */ 866 free_data = msgdatap->data; 867 free_data_len = msgdatap->length; 868 869 /* 870 * Some commands may need additional processing prior to transmission. 871 */ 872 switch (msgdatap->cmd) { 873 /* 874 * Since the SC is only interested in the most recent value of 875 * utsname.nodename, we wait until now to collect that data. We 876 * also use a global flag to prevent multiple event-type 877 * nodename messages from being queued at the same time for the 878 * same reason. 879 */ 880 case SCDM_GET_NODENAME: 881 mutex_enter(&scosmb_mutex); 882 length = strlen(utsname.nodename); 883 ASSERT(length < _SYS_NMLN); 884 if (length == 0) { 885 msgdatap->length = 0; 886 msgdatap->data = NULL; 887 } else { 888 bcopy(utsname.nodename, nodename, length); 889 nodename[length++] = '\0'; 890 msgdatap->data = nodename; 891 msgdatap->length = length; 892 } 893 if (msgdatap->transid == 0) { 894 scosmb_nodename_event_pending = FALSE; 895 } 896 mutex_exit(&scosmb_mutex); 897 break; 898 899 /* 900 * SCDM_LOG_ECC_CAP_INIT 901 * Initiator Capability message from OS to SC 902 * 903 * We construct and send an initiator capability message 904 * every time we go through scosmb_update_nodename(), which 905 * works out to getting an "initiator" capability message 906 * sent from the OS to the SC during the OS boot flow. 907 * 908 * The SC also issues a request to scosmb_update_nodename() 909 * during an SC reboot. Which results in an additional 910 * capability message exchange during SC reboot scenarios. 911 * 912 * SCDM_LOG_ECC_CAP_RESP 913 * Response Capability message from SC to OS 914 * 915 * In certain scenarios, the SC could initiate a capability 916 * messaging exchange with the OS. Processing starts in 917 * scosmb_process_input(), where we detect an incoming 918 * initiator capability message from the SC. We finish 919 * processing here, by sending a response capability message 920 * back to the SC that reflects OS capabilities. 921 */ 922 case SCDM_LOG_ECC_CAP_INIT: 923 /*FALLTHROUGH*/ 924 case SCDM_LOG_ECC_CAP_RESP: 925 mutex_enter(&scosmb_mutex); 926 927 cap_ver_len = strlen(utsname.release) + 928 strlen(utsname.version) + 2; 929 930 cap_size = sizeof (plat_capability_data_t) + 931 cap_ver_len; 932 933 cap = kmem_zalloc(cap_size, KM_SLEEP); 934 935 cap->capd_major_version = PLAT_ECC_CAP_VERSION_MAJOR; 936 cap->capd_minor_version = PLAT_ECC_CAP_VERSION_MINOR; 937 cap->capd_msg_type = PLAT_ECC_CAPABILITY_MESSAGE; 938 cap->capd_msg_length = cap_size; 939 940 cap->capd_capability = 941 PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT; 942 943 /* 944 * Build the capability solaris_version string: 945 * utsname.release + " " + utsname.version 946 */ 947 (void) snprintf(cap->capd_solaris_version, 948 cap_ver_len, "%s %s", utsname.release, 949 utsname.version); 950 951 /* 952 * The capability message is constructed, now plug it 953 * into the starcat msgdatap: 954 */ 955 msgdatap->data = (plat_capability_data_t *)cap; 956 msgdatap->length = cap_size; 957 958 /* 959 * Finished with initiator/response capability 960 * message set up. 961 * 962 * Note that after sending an "initiator" capability 963 * message, we can expect a subsequent "response" 964 * capability message from the SC, which we will 965 * pick up and minimally handle later, 966 * in scosmb_process_input(). 967 * 968 * If we're sending a "response" capability message 969 * to the SC, then we're done once the message is sent. 970 */ 971 972 if (msgdatap->transid == 0) { 973 scosmb_nodename_event_pending = FALSE; 974 } 975 mutex_exit(&scosmb_mutex); 976 break; 977 978 default: 979 break; 980 } 981 982 /* 983 * Attempt to send the message. 984 */ 985 error = mboxsc_putmsg(DMSC_KEY, msgdatap->type, msgdatap->cmd, 986 &msgdatap->transid, msgdatap->length, msgdatap->data, 987 ecc_message_timeout_ms); 988 989 /* 990 * Free any allocated memory that was passed in. 991 */ 992 if (free_data != NULL) { 993 kmem_free(free_data, free_data_len); 994 } 995 996 if (cap != NULL) { 997 kmem_free(cap, cap_size); 998 } 999 1000 kmem_free(msgdatap, sizeof (scosmb_msgdata_t)); 1001 1002 /* 1003 * If EAGAIN or ETIMEDOUT was received, give up. The sender can try 1004 * again if it was important. If any other non-zero error was 1005 * encountered, the mailbox service is broken, and there's nothing more 1006 * we can do. 1007 */ 1008 mutex_enter(&scosmb_mutex); 1009 if ((error == EAGAIN) || (error == ETIMEDOUT)) { 1010 if (msgdatap->log_error && !scosmb_mboxsc_timedout) { 1011 /* 1012 * Indictment mailbox messages use the return value to 1013 * indicate a problem in the mailbox. For Error 1014 * mailbox messages, we'll have to use a syslog message. 1015 */ 1016 scosmb_mboxsc_timedout = TRUE; 1017 cmn_err(CE_NOTE, "!Solaris failed to send a message " 1018 "(0x%x/0x%x) to the System Controller. Error: %d", 1019 msgdatap->type, msgdatap->cmd, error); 1020 } 1021 } else if (error != 0) { 1022 /* 1023 * The mailbox service appears to be badly broken. If it was 1024 * working previously, generate a warning and set a flag to 1025 * avoid repeating the warning on subsequent failures. 1026 */ 1027 if (msgdatap->log_error && !scosmb_mboxsc_failed) { 1028 scosmb_mboxsc_failed = TRUE; 1029 cmn_err(CE_NOTE, "!An internal error (%d) occurred " 1030 "while processing this message (0x%x/0x%x)", 1031 error, msgdatap->type, msgdatap->cmd); 1032 } 1033 } else { 1034 /* 1035 * If the mailbox module failed previously, it appears to have 1036 * recovered, so we'll want to generate a warning if it fails 1037 * again. 1038 */ 1039 scosmb_mboxsc_failed = scosmb_mboxsc_timedout = FALSE; 1040 } 1041 mutex_exit(&scosmb_mutex); 1042 return (error); 1043 } 1044 1045 /* 1046 * scosmb_update_nodename() - nodename update routine 1047 * 1048 * this routine, which may be invoked from outside of the scosmb module, will 1049 * cause the current nodename to be sent to the SC. The mailbox message sent to 1050 * the SC will use the indicated transaction ID, and will either be a reply 1051 * message if the ID is non-zero or an event message if it is 0. 1052 * 1053 * Capability messaging enhancements: 1054 * Every time we move through this code flow, we put an "initiator 1055 * capability message" on the message output taskq. This action will 1056 * get a capability message sent to the SC from the OS during boot 1057 * scenarios. A capability message exchange will also happen for 1058 * SC reboot scenarios, as the SC will initiate a nodename update 1059 * as a matter of course while coming back up. 1060 * 1061 * We'll also get an extraneous capability message sent 1062 * to the SC from time to time, but that won't hurt anything. 1063 */ 1064 void 1065 scosmb_update_nodename(uint64_t transid) 1066 { 1067 scosmb_msgdata_t *msgdatap, *cap_msgdatap; 1068 1069 /* 1070 * If we're generating an unsolicited nodename update (presumably having 1071 * been called from platmod:plat_nodename_set()), there's no need to add 1072 * a new job to the queue if there is already one on it that will be 1073 * sending the latest nodename data. 1074 */ 1075 mutex_enter(&scosmb_mutex); 1076 if (transid == 0) { 1077 if (scosmb_nodename_event_pending) { 1078 mutex_exit(&scosmb_mutex); 1079 return; 1080 } else { 1081 scosmb_nodename_event_pending = TRUE; 1082 } 1083 } 1084 mutex_exit(&scosmb_mutex); 1085 1086 /* 1087 * Allocate memory for the message data, initialize it, and place a new 1088 * job on the scosmb_output_taskq for SCDM_GET_NODENAME. 1089 */ 1090 msgdatap = (scosmb_msgdata_t *)kmem_zalloc(sizeof (scosmb_msgdata_t), 1091 KM_SLEEP); 1092 1093 msgdatap->type = (transid == 0) ? MBOXSC_MSG_EVENT : MBOXSC_MSG_REPLY; 1094 msgdatap->cmd = SCDM_GET_NODENAME; 1095 msgdatap->transid = transid; 1096 msgdatap->log_error = 1; 1097 1098 (void) taskq_dispatch(scosmb_output_taskq, 1099 (task_func_t *)scosmb_process_output, msgdatap, KM_SLEEP); 1100 1101 /* 1102 * Next, allocate memory, initialize, and place a new job on the 1103 * scosmb_output_taskq for SCDM_LOG_ECC_CAP_INIT. That's a 1104 * capability message, where we're the initiator. 1105 */ 1106 cap_msgdatap = kmem_zalloc(sizeof (scosmb_msgdata_t), KM_SLEEP); 1107 1108 cap_msgdatap->type = (transid == 0) ? 1109 MBOXSC_MSG_EVENT : MBOXSC_MSG_REPLY; 1110 cap_msgdatap->cmd = SCDM_LOG_ECC_CAP_INIT; 1111 cap_msgdatap->transid = transid; 1112 cap_msgdatap->log_error = 1; 1113 1114 (void) taskq_dispatch(scosmb_output_taskq, 1115 (task_func_t *)scosmb_process_output, cap_msgdatap, KM_SLEEP); 1116 } 1117 1118 /* 1119 * scosmb_log_ecc_error() - Record ECC error information to SC 1120 * For ECC error messages, send the messages through a taskq mechanism 1121 * to prevent impaired system performance during ECC floods. Indictment 1122 * messages have already passed through a taskq, so directly call the 1123 * output function. 1124 */ 1125 int 1126 scosmb_log_ecc_error(plat_ecc_message_type_t msg_type, void *datap) 1127 { 1128 scosmb_msgdata_t *msg_header_ptr; 1129 uint32_t msg_cmd, msg_length; 1130 int sleep_flag, log_error; 1131 int do_queue; /* Set to 1 if taskq needed */ 1132 1133 /* 1134 * Set header type and length for message 1135 */ 1136 switch (msg_type) { 1137 case PLAT_ECC_ERROR_MESSAGE: 1138 /* 1139 * We do not want to sleep in an error logging thread. So, 1140 * we set the NOSLEEP flag and go through a taskq before we 1141 * send the message. 1142 */ 1143 msg_cmd = SCDM_LOG_ECC_ERROR; 1144 msg_length = sizeof (plat_ecc_error_data_t); 1145 sleep_flag = KM_NOSLEEP; 1146 log_error = 1; 1147 do_queue = 1; 1148 break; 1149 case PLAT_ECC_ERROR2_MESSAGE: 1150 msg_cmd = SCDM_LOG_ECC; 1151 msg_length = sizeof (plat_ecc_error2_data_t); 1152 sleep_flag = KM_NOSLEEP; 1153 log_error = 1; 1154 do_queue = 1; 1155 break; 1156 case PLAT_ECC_INDICTMENT_MESSAGE: 1157 /* 1158 * For indictment messages, we're allowed to sleep, and we 1159 * can directly call the output function, since we've already 1160 * gone through a taskq 1161 */ 1162 msg_cmd = SCDM_LOG_ECC_INDICTMENT; 1163 msg_length = sizeof (plat_ecc_indictment_data_t); 1164 sleep_flag = KM_SLEEP; 1165 log_error = 0; 1166 do_queue = 0; 1167 break; 1168 case PLAT_ECC_INDICTMENT2_MESSAGE: 1169 /* 1170 * For indictment2 messages, we're allowed to sleep, and we 1171 * can directly call the output function, since we've already 1172 * gone through a taskq 1173 */ 1174 msg_cmd = SCDM_LOG_ECC; 1175 msg_length = sizeof (plat_ecc_indictment2_data_t); 1176 sleep_flag = KM_SLEEP; 1177 log_error = 0; 1178 do_queue = 0; 1179 break; 1180 1181 case PLAT_ECC_DIMM_SID_MESSAGE: 1182 /* 1183 * For DIMM sid request messages, we're allowed to sleep, and we 1184 * can directly call the output function, since we've already 1185 * gone through a taskq 1186 */ 1187 msg_cmd = SCDM_DIMM_SERIAL_ID; 1188 msg_length = sizeof (plat_dimm_sid_request_data_t); 1189 sleep_flag = KM_SLEEP; 1190 log_error = 0; 1191 do_queue = 0; 1192 break; 1193 1194 default: 1195 return (EINVAL); 1196 } 1197 1198 /* 1199 * Allocate memory for the mailbox message header. 1200 */ 1201 msg_header_ptr = 1202 (scosmb_msgdata_t *)kmem_zalloc(sizeof (scosmb_msgdata_t), 1203 sleep_flag); 1204 1205 if (msg_header_ptr == NULL) { 1206 #ifdef DEBUG 1207 cmn_err(CE_WARN, "failed to allocate space for scosmb " 1208 "message header."); 1209 #endif /* DEBUG */ 1210 return (ENOMEM); 1211 } 1212 1213 msg_header_ptr->type = MBOXSC_MSG_EVENT; 1214 msg_header_ptr->cmd = msg_cmd; 1215 msg_header_ptr->transid = 0; 1216 msg_header_ptr->log_error = log_error; 1217 1218 /* 1219 * Allocate memory for the mailbox message payload. 1220 */ 1221 msg_header_ptr->length = msg_length; 1222 msg_header_ptr->data = kmem_zalloc((size_t)msg_length, sleep_flag); 1223 1224 if (msg_header_ptr->data == NULL) { 1225 #ifdef DEBUG 1226 cmn_err(CE_WARN, "failed to allocate space for scosmb " 1227 "message data."); 1228 #endif /* DEBUG */ 1229 kmem_free(msg_header_ptr, sizeof (scosmb_msgdata_t)); 1230 return (ENOMEM); 1231 } 1232 1233 bcopy(datap, msg_header_ptr->data, (size_t)msg_length); 1234 1235 /* 1236 * Based on our earlier look at the message type, we either go through 1237 * a taskq or directly call the output function. 1238 */ 1239 if (do_queue != 0) { 1240 /* 1241 * Place a new job on the scosmb_output_taskq. 1242 */ 1243 if (taskq_dispatch(scosmb_output_taskq, 1244 (task_func_t *)scosmb_process_output, 1245 (void *)msg_header_ptr, TQ_NOSLEEP) == 0) { 1246 #ifdef DEBUG 1247 cmn_err(CE_WARN, "failed to dispatch a task to send " 1248 "ECC mailbox message."); 1249 #endif /* DEBUG */ 1250 kmem_free(msg_header_ptr->data, msg_header_ptr->length); 1251 kmem_free(msg_header_ptr, sizeof (scosmb_msgdata_t)); 1252 return (ENOMEM); 1253 } 1254 return (0); 1255 } else { 1256 return (scosmb_process_output(msg_header_ptr)); 1257 } 1258 } 1259