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
_init(void)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
_fini(void)245 _fini(void)
246 {
247 return (EBUSY);
248 }
249
250 /*
251 * _info
252 *
253 * Loadable module support routine.
254 */
255 int
_info(struct modinfo * modinfop)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
scosmb_inbox_handler()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
dp_get_cores(uint16_t cpuid)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
dp_payload_add_cpus(plat_datapath_info_t * dpmsg,nvlist_t * erp,int maxcat)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
dp_trans_event(plat_datapath_info_t * dpmsg,int msgtype)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
scosmb_process_input(void * unused)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
scosmb_process_output(scosmb_msgdata_t * msgdatap)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
scosmb_update_nodename(uint64_t transid)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
scosmb_log_ecc_error(plat_ecc_message_type_t msg_type,void * datap)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