xref: /illumos-gate/usr/src/uts/common/os/fm.c (revision 3ddd2818ac8897094b3a1e147b4788ccbe199a0f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Fault Management Architecture (FMA) Resource and Protocol Support
31  *
32  * The routines contained herein provide services to support kernel subsystems
33  * in publishing fault management telemetry (see PSARC 2002/412 and 2003/089).
34  *
35  * Name-Value Pair Lists
36  *
37  * The embodiment of an FMA protocol element (event, fmri or authority) is a
38  * name-value pair list (nvlist_t).  FMA-specific nvlist construtor and
39  * destructor functions, fm_nvlist_create() and fm_nvlist_destroy(), are used
40  * to create an nvpair list using custom allocators.  Callers may choose to
41  * allocate either from the kernel memory allocator, or from a preallocated
42  * buffer, useful in constrained contexts like high-level interrupt routines.
43  *
44  * Protocol Event and FMRI Construction
45  *
46  * Convenience routines are provided to construct nvlist events according to
47  * the FMA Event Protocol and Naming Schema specification for ereports and
48  * FMRIs for the dev, cpu, hc, mem, legacy hc and de schemes.
49  *
50  * ENA Manipulation
51  *
52  * Routines to generate ENA formats 0, 1 and 2 are available as well as
53  * routines to increment formats 1 and 2.  Individual fields within the
54  * ENA are extractable via fm_ena_time_get(), fm_ena_id_get(),
55  * fm_ena_format_get() and fm_ena_gen_get().
56  */
57 
58 #include <sys/types.h>
59 #include <sys/time.h>
60 #include <sys/sysevent.h>
61 #include <sys/sysevent_impl.h>
62 #include <sys/nvpair.h>
63 #include <sys/cmn_err.h>
64 #include <sys/cpuvar.h>
65 #include <sys/sysmacros.h>
66 #include <sys/systm.h>
67 #include <sys/ddifm.h>
68 #include <sys/ddifm_impl.h>
69 #include <sys/spl.h>
70 #include <sys/dumphdr.h>
71 #include <sys/compress.h>
72 #include <sys/cpuvar.h>
73 #include <sys/console.h>
74 #include <sys/panic.h>
75 #include <sys/kobj.h>
76 #include <sys/sunddi.h>
77 #include <sys/systeminfo.h>
78 #include <sys/sysevent/eventdefs.h>
79 #include <sys/fm/util.h>
80 #include <sys/fm/protocol.h>
81 
82 /*
83  * URL and SUNW-MSG-ID value to display for fm_panic(), defined below.  These
84  * values must be kept in sync with the FMA source code in usr/src/cmd/fm.
85  */
86 static const char *fm_url = "http://www.sun.com/msg";
87 static const char *fm_msgid = "SUNOS-8000-0G";
88 static char *volatile fm_panicstr = NULL;
89 
90 errorq_t *ereport_errorq;
91 void *ereport_dumpbuf;
92 size_t ereport_dumplen;
93 
94 static uint_t ereport_chanlen = ERPT_EVCH_MAX;
95 static evchan_t *ereport_chan = NULL;
96 static ulong_t ereport_qlen = 0;
97 static size_t ereport_size = 0;
98 static int ereport_cols = 80;
99 
100 /*
101  * Common fault management kstats to record ereport generation
102  * failures
103  */
104 
105 struct erpt_kstat {
106 	kstat_named_t	erpt_dropped;		/* num erpts dropped on post */
107 	kstat_named_t	erpt_set_failed;	/* num erpt set failures */
108 	kstat_named_t	fmri_set_failed;	/* num fmri set failures */
109 	kstat_named_t	payload_set_failed;	/* num payload set failures */
110 };
111 
112 static struct erpt_kstat erpt_kstat_data = {
113 	{ "erpt-dropped", KSTAT_DATA_UINT64 },
114 	{ "erpt-set-failed", KSTAT_DATA_UINT64 },
115 	{ "fmri-set-failed", KSTAT_DATA_UINT64 },
116 	{ "payload-set-failed", KSTAT_DATA_UINT64 }
117 };
118 
119 /*ARGSUSED*/
120 static void
121 fm_drain(void *private, void *data, errorq_elem_t *eep)
122 {
123 	nvlist_t *nvl = errorq_elem_nvl(ereport_errorq, eep);
124 
125 	if (!panicstr)
126 		(void) fm_ereport_post(nvl, EVCH_TRYHARD);
127 	else
128 		fm_nvprint(nvl);
129 }
130 
131 void
132 fm_init(void)
133 {
134 	kstat_t *ksp;
135 
136 	(void) sysevent_evc_bind(FM_ERROR_CHAN,
137 	    &ereport_chan, EVCH_CREAT | EVCH_HOLD_PEND);
138 
139 	(void) sysevent_evc_control(ereport_chan,
140 	    EVCH_SET_CHAN_LEN, &ereport_chanlen);
141 
142 	if (ereport_qlen == 0)
143 		ereport_qlen = ERPT_MAX_ERRS * MAX(max_ncpus, 4);
144 
145 	if (ereport_size == 0)
146 		ereport_size = ERPT_DATA_SZ;
147 
148 	ereport_errorq = errorq_nvcreate("fm_ereport_queue",
149 	    (errorq_func_t)fm_drain, NULL, ereport_qlen, ereport_size,
150 	    FM_ERR_PIL, ERRORQ_VITAL);
151 	if (ereport_errorq == NULL)
152 		panic("failed to create required ereport error queue");
153 
154 	ereport_dumpbuf = kmem_alloc(ereport_size, KM_SLEEP);
155 	ereport_dumplen = ereport_size;
156 
157 	/* Initialize ereport allocation and generation kstats */
158 	ksp = kstat_create("unix", 0, "fm", "misc", KSTAT_TYPE_NAMED,
159 	    sizeof (struct erpt_kstat) / sizeof (kstat_named_t),
160 	    KSTAT_FLAG_VIRTUAL);
161 
162 	if (ksp != NULL) {
163 		ksp->ks_data = &erpt_kstat_data;
164 		kstat_install(ksp);
165 	} else {
166 		cmn_err(CE_NOTE, "failed to create fm/misc kstat\n");
167 
168 	}
169 }
170 
171 /*
172  * Formatting utility function for fm_nvprintr.  We attempt to wrap chunks of
173  * output so they aren't split across console lines, and return the end column.
174  */
175 /*PRINTFLIKE4*/
176 static int
177 fm_printf(int depth, int c, int cols, const char *format, ...)
178 {
179 	va_list ap;
180 	int width;
181 	char c1;
182 
183 	va_start(ap, format);
184 	width = vsnprintf(&c1, sizeof (c1), format, ap);
185 	va_end(ap);
186 
187 	if (c + width >= cols) {
188 		console_printf("\n\r");
189 		c = 0;
190 		if (format[0] != ' ' && depth > 0) {
191 			console_printf(" ");
192 			c++;
193 		}
194 	}
195 
196 	va_start(ap, format);
197 	console_vprintf(format, ap);
198 	va_end(ap);
199 
200 	return ((c + width) % cols);
201 }
202 
203 /*
204  * Recursively print a nvlist in the specified column width and return the
205  * column we end up in.  This function is called recursively by fm_nvprint(),
206  * below.  We generically format the entire nvpair using hexadecimal
207  * integers and strings, and elide any integer arrays.  Arrays are basically
208  * used for cache dumps right now, so we suppress them so as not to overwhelm
209  * the amount of console output we produce at panic time.  This can be further
210  * enhanced as FMA technology grows based upon the needs of consumers.  All
211  * FMA telemetry is logged using the dump device transport, so the console
212  * output serves only as a fallback in case this procedure is unsuccessful.
213  */
214 static int
215 fm_nvprintr(nvlist_t *nvl, int d, int c, int cols)
216 {
217 	nvpair_t *nvp;
218 
219 	for (nvp = nvlist_next_nvpair(nvl, NULL);
220 	    nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
221 
222 		data_type_t type = nvpair_type(nvp);
223 		const char *name = nvpair_name(nvp);
224 
225 		boolean_t b;
226 		uint8_t i8;
227 		uint16_t i16;
228 		uint32_t i32;
229 		uint64_t i64;
230 		char *str;
231 		nvlist_t *cnv;
232 
233 		if (strcmp(name, FM_CLASS) == 0)
234 			continue; /* already printed by caller */
235 
236 		c = fm_printf(d, c, cols, " %s=", name);
237 
238 		switch (type) {
239 		case DATA_TYPE_BOOLEAN:
240 			c = fm_printf(d + 1, c, cols, " 1");
241 			break;
242 
243 		case DATA_TYPE_BOOLEAN_VALUE:
244 			(void) nvpair_value_boolean_value(nvp, &b);
245 			c = fm_printf(d + 1, c, cols, b ? "1" : "0");
246 			break;
247 
248 		case DATA_TYPE_BYTE:
249 			(void) nvpair_value_byte(nvp, &i8);
250 			c = fm_printf(d + 1, c, cols, "%x", i8);
251 			break;
252 
253 		case DATA_TYPE_INT8:
254 			(void) nvpair_value_int8(nvp, (void *)&i8);
255 			c = fm_printf(d + 1, c, cols, "%x", i8);
256 			break;
257 
258 		case DATA_TYPE_UINT8:
259 			(void) nvpair_value_uint8(nvp, &i8);
260 			c = fm_printf(d + 1, c, cols, "%x", i8);
261 			break;
262 
263 		case DATA_TYPE_INT16:
264 			(void) nvpair_value_int16(nvp, (void *)&i16);
265 			c = fm_printf(d + 1, c, cols, "%x", i16);
266 			break;
267 
268 		case DATA_TYPE_UINT16:
269 			(void) nvpair_value_uint16(nvp, &i16);
270 			c = fm_printf(d + 1, c, cols, "%x", i16);
271 			break;
272 
273 		case DATA_TYPE_INT32:
274 			(void) nvpair_value_int32(nvp, (void *)&i32);
275 			c = fm_printf(d + 1, c, cols, "%x", i32);
276 			break;
277 
278 		case DATA_TYPE_UINT32:
279 			(void) nvpair_value_uint32(nvp, &i32);
280 			c = fm_printf(d + 1, c, cols, "%x", i32);
281 			break;
282 
283 		case DATA_TYPE_INT64:
284 			(void) nvpair_value_int64(nvp, (void *)&i64);
285 			c = fm_printf(d + 1, c, cols, "%llx",
286 			    (u_longlong_t)i64);
287 			break;
288 
289 		case DATA_TYPE_UINT64:
290 			(void) nvpair_value_uint64(nvp, &i64);
291 			c = fm_printf(d + 1, c, cols, "%llx",
292 			    (u_longlong_t)i64);
293 			break;
294 
295 		case DATA_TYPE_HRTIME:
296 			(void) nvpair_value_hrtime(nvp, (void *)&i64);
297 			c = fm_printf(d + 1, c, cols, "%llx",
298 			    (u_longlong_t)i64);
299 			break;
300 
301 		case DATA_TYPE_STRING:
302 			(void) nvpair_value_string(nvp, &str);
303 			c = fm_printf(d + 1, c, cols, "\"%s\"",
304 			    str ? str : "<NULL>");
305 			break;
306 
307 		case DATA_TYPE_NVLIST:
308 			c = fm_printf(d + 1, c, cols, "[");
309 			(void) nvpair_value_nvlist(nvp, &cnv);
310 			c = fm_nvprintr(cnv, d + 1, c, cols);
311 			c = fm_printf(d + 1, c, cols, " ]");
312 			break;
313 
314 		case DATA_TYPE_BOOLEAN_ARRAY:
315 		case DATA_TYPE_BYTE_ARRAY:
316 		case DATA_TYPE_INT8_ARRAY:
317 		case DATA_TYPE_UINT8_ARRAY:
318 		case DATA_TYPE_INT16_ARRAY:
319 		case DATA_TYPE_UINT16_ARRAY:
320 		case DATA_TYPE_INT32_ARRAY:
321 		case DATA_TYPE_UINT32_ARRAY:
322 		case DATA_TYPE_INT64_ARRAY:
323 		case DATA_TYPE_UINT64_ARRAY:
324 		case DATA_TYPE_STRING_ARRAY:
325 		case DATA_TYPE_NVLIST_ARRAY:
326 			c = fm_printf(d + 1, c, cols, "[...]");
327 			break;
328 		case DATA_TYPE_UNKNOWN:
329 			c = fm_printf(d + 1, c, cols, "<unknown>");
330 			break;
331 		}
332 	}
333 
334 	return (c);
335 }
336 
337 void
338 fm_nvprint(nvlist_t *nvl)
339 {
340 	char *class;
341 	int c = 0;
342 
343 	console_printf("\r");
344 
345 	if (nvlist_lookup_string(nvl, FM_CLASS, &class) == 0)
346 		c = fm_printf(0, c, ereport_cols, "%s", class);
347 
348 	if (fm_nvprintr(nvl, 0, c, ereport_cols) != 0)
349 		console_printf("\n");
350 
351 	console_printf("\n");
352 }
353 
354 /*
355  * Wrapper for panic() that first produces an FMA-style message for admins.
356  * Normally such messages are generated by fmd(1M)'s syslog-msgs agent: this
357  * is the one exception to that rule and the only error that gets messaged.
358  * This function is intended for use by subsystems that have detected a fatal
359  * error and enqueued appropriate ereports and wish to then force a panic.
360  */
361 /*PRINTFLIKE1*/
362 void
363 fm_panic(const char *format, ...)
364 {
365 	va_list ap;
366 
367 	(void) casptr((void *)&fm_panicstr, NULL, (void *)format);
368 	va_start(ap, format);
369 	vpanic(format, ap);
370 	va_end(ap);
371 }
372 
373 /*
374  * Print any appropriate FMA banner message before the panic message.  This
375  * function is called by panicsys() and prints the message for fm_panic().
376  * We print the message here so that it comes after the system is quiesced.
377  * A one-line summary is recorded in the log only (cmn_err(9F) with "!" prefix).
378  * The rest of the message is for the console only and not needed in the log,
379  * so it is printed using console_printf().  We break it up into multiple
380  * chunks so as to avoid overflowing any small legacy prom_printf() buffers.
381  */
382 void
383 fm_banner(void)
384 {
385 	timespec_t tod;
386 	hrtime_t now;
387 
388 	if (!fm_panicstr)
389 		return; /* panic was not initiated by fm_panic(); do nothing */
390 
391 	if (panicstr) {
392 		tod = panic_hrestime;
393 		now = panic_hrtime;
394 	} else {
395 		gethrestime(&tod);
396 		now = gethrtime_waitfree();
397 	}
398 
399 	cmn_err(CE_NOTE, "!SUNW-MSG-ID: %s, "
400 	    "TYPE: Error, VER: 1, SEVERITY: Major\n", fm_msgid);
401 
402 	console_printf(
403 "\n\rSUNW-MSG-ID: %s, TYPE: Error, VER: 1, SEVERITY: Major\n"
404 "EVENT-TIME: 0x%lx.0x%lx (0x%llx)\n",
405 	    fm_msgid, tod.tv_sec, tod.tv_nsec, (u_longlong_t)now);
406 
407 	console_printf(
408 "PLATFORM: %s, CSN: -, HOSTNAME: %s\n"
409 "SOURCE: %s, REV: %s %s\n",
410 	    platform, utsname.nodename, utsname.sysname,
411 	    utsname.release, utsname.version);
412 
413 	console_printf(
414 "DESC: Errors have been detected that require a reboot to ensure system\n"
415 "integrity.  See %s/%s for more information.\n",
416 	    fm_url, fm_msgid);
417 
418 	console_printf(
419 "AUTO-RESPONSE: Solaris will attempt to save and diagnose the error telemetry\n"
420 "IMPACT: The system will sync files, save a crash dump if needed, and reboot\n"
421 "REC-ACTION: Save the error summary below in case telemetry cannot be saved\n");
422 
423 	console_printf("\n");
424 }
425 
426 /*
427  * Utility function to write all of the pending ereports to the dump device.
428  * This function is called at either normal reboot or panic time, and simply
429  * iterates over the in-transit messages in the ereport sysevent channel.
430  */
431 void
432 fm_ereport_dump(void)
433 {
434 	evchanq_t *chq;
435 	sysevent_t *sep;
436 	erpt_dump_t ed;
437 
438 	timespec_t tod;
439 	hrtime_t now;
440 	char *buf;
441 	size_t len;
442 
443 	if (panicstr) {
444 		tod = panic_hrestime;
445 		now = panic_hrtime;
446 	} else {
447 		if (ereport_errorq != NULL)
448 			errorq_drain(ereport_errorq);
449 		gethrestime(&tod);
450 		now = gethrtime_waitfree();
451 	}
452 
453 	/*
454 	 * In the panic case, sysevent_evc_walk_init() will return NULL.
455 	 */
456 	if ((chq = sysevent_evc_walk_init(ereport_chan, NULL)) == NULL &&
457 	    !panicstr)
458 		return; /* event channel isn't initialized yet */
459 
460 	while ((sep = sysevent_evc_walk_step(chq)) != NULL) {
461 		if ((buf = sysevent_evc_event_attr(sep, &len)) == NULL)
462 			break;
463 
464 		ed.ed_magic = ERPT_MAGIC;
465 		ed.ed_chksum = checksum32(buf, len);
466 		ed.ed_size = (uint32_t)len;
467 		ed.ed_pad = 0;
468 		ed.ed_hrt_nsec = SE_TIME(sep);
469 		ed.ed_hrt_base = now;
470 		ed.ed_tod_base.sec = tod.tv_sec;
471 		ed.ed_tod_base.nsec = tod.tv_nsec;
472 
473 		dumpvp_write(&ed, sizeof (ed));
474 		dumpvp_write(buf, len);
475 	}
476 
477 	sysevent_evc_walk_fini(chq);
478 }
479 
480 /*
481  * Post an error report (ereport) to the sysevent error channel.  The error
482  * channel must be established with a prior call to sysevent_evc_create()
483  * before publication may occur.
484  */
485 void
486 fm_ereport_post(nvlist_t *ereport, int evc_flag)
487 {
488 	size_t nvl_size = 0;
489 	evchan_t *error_chan;
490 
491 	(void) nvlist_size(ereport, &nvl_size, NV_ENCODE_NATIVE);
492 	if (nvl_size > ERPT_DATA_SZ || nvl_size == 0) {
493 		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
494 		return;
495 	}
496 
497 	if (sysevent_evc_bind(FM_ERROR_CHAN, &error_chan,
498 	    EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
499 		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
500 		return;
501 	}
502 
503 	if (sysevent_evc_publish(error_chan, EC_FM, ESC_FM_ERROR,
504 	    SUNW_VENDOR, FM_PUB, ereport, evc_flag) != 0) {
505 		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
506 		sysevent_evc_unbind(error_chan);
507 		return;
508 	}
509 	sysevent_evc_unbind(error_chan);
510 }
511 
512 /*
513  * Wrapppers for FM nvlist allocators
514  */
515 /* ARGSUSED */
516 static void *
517 i_fm_alloc(nv_alloc_t *nva, size_t size)
518 {
519 	return (kmem_zalloc(size, KM_SLEEP));
520 }
521 
522 /* ARGSUSED */
523 static void
524 i_fm_free(nv_alloc_t *nva, void *buf, size_t size)
525 {
526 	kmem_free(buf, size);
527 }
528 
529 const nv_alloc_ops_t fm_mem_alloc_ops = {
530 	NULL,
531 	NULL,
532 	i_fm_alloc,
533 	i_fm_free,
534 	NULL
535 };
536 
537 /*
538  * Create and initialize a new nv_alloc_t for a fixed buffer, buf.  A pointer
539  * to the newly allocated nv_alloc_t structure is returned upon success or NULL
540  * is returned to indicate that the nv_alloc structure could not be created.
541  */
542 nv_alloc_t *
543 fm_nva_xcreate(char *buf, size_t bufsz)
544 {
545 	nv_alloc_t *nvhdl = kmem_zalloc(sizeof (nv_alloc_t), KM_SLEEP);
546 
547 	if (bufsz == 0 || nv_alloc_init(nvhdl, nv_fixed_ops, buf, bufsz) != 0) {
548 		kmem_free(nvhdl, sizeof (nv_alloc_t));
549 		return (NULL);
550 	}
551 
552 	return (nvhdl);
553 }
554 
555 /*
556  * Destroy a previously allocated nv_alloc structure.  The fixed buffer
557  * associated with nva must be freed by the caller.
558  */
559 void
560 fm_nva_xdestroy(nv_alloc_t *nva)
561 {
562 	nv_alloc_fini(nva);
563 	kmem_free(nva, sizeof (nv_alloc_t));
564 }
565 
566 /*
567  * Create a new nv list.  A pointer to a new nv list structure is returned
568  * upon success or NULL is returned to indicate that the structure could
569  * not be created.  The newly created nv list is created and managed by the
570  * operations installed in nva.   If nva is NULL, the default FMA nva
571  * operations are installed and used.
572  *
573  * When called from the kernel and nva == NULL, this function must be called
574  * from passive kernel context with no locks held that can prevent a
575  * sleeping memory allocation from occurring.  Otherwise, this function may
576  * be called from other kernel contexts as long a valid nva created via
577  * fm_nva_create() is supplied.
578  */
579 nvlist_t *
580 fm_nvlist_create(nv_alloc_t *nva)
581 {
582 	int hdl_alloced = 0;
583 	nvlist_t *nvl;
584 	nv_alloc_t *nvhdl;
585 
586 	if (nva == NULL) {
587 		nvhdl = kmem_zalloc(sizeof (nv_alloc_t), KM_SLEEP);
588 
589 		if (nv_alloc_init(nvhdl, &fm_mem_alloc_ops, NULL, 0) != 0) {
590 			kmem_free(nvhdl, sizeof (nv_alloc_t));
591 			return (NULL);
592 		}
593 		hdl_alloced = 1;
594 	} else {
595 		nvhdl = nva;
596 	}
597 
598 	if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, nvhdl) != 0) {
599 		if (hdl_alloced) {
600 			kmem_free(nvhdl, sizeof (nv_alloc_t));
601 			nv_alloc_fini(nvhdl);
602 		}
603 		return (NULL);
604 	}
605 
606 	return (nvl);
607 }
608 
609 /*
610  * Destroy a previously allocated nvlist structure.  flag indicates whether
611  * or not the associated nva structure should be freed (FM_NVA_FREE) or
612  * retained (FM_NVA_RETAIN).  Retaining the nv alloc structure allows
613  * it to be re-used for future nvlist creation operations.
614  */
615 void
616 fm_nvlist_destroy(nvlist_t *nvl, int flag)
617 {
618 	nv_alloc_t *nva = nvlist_lookup_nv_alloc(nvl);
619 
620 	nvlist_free(nvl);
621 
622 	if (nva != NULL) {
623 		if (flag == FM_NVA_FREE)
624 			fm_nva_xdestroy(nva);
625 	}
626 }
627 
628 int
629 i_fm_payload_set(nvlist_t *payload, const char *name, va_list ap)
630 {
631 	int nelem, ret = 0;
632 	data_type_t type;
633 
634 	while (ret == 0 && name != NULL) {
635 		type = va_arg(ap, data_type_t);
636 		switch (type) {
637 		case DATA_TYPE_BYTE:
638 			ret = nvlist_add_byte(payload, name,
639 			    va_arg(ap, uint_t));
640 			break;
641 		case DATA_TYPE_BYTE_ARRAY:
642 			nelem = va_arg(ap, int);
643 			ret = nvlist_add_byte_array(payload, name,
644 			    va_arg(ap, uchar_t *), nelem);
645 			break;
646 		case DATA_TYPE_BOOLEAN_VALUE:
647 			ret = nvlist_add_boolean_value(payload, name,
648 			    va_arg(ap, boolean_t));
649 			break;
650 		case DATA_TYPE_BOOLEAN_ARRAY:
651 			nelem = va_arg(ap, int);
652 			ret = nvlist_add_boolean_array(payload, name,
653 			    va_arg(ap, boolean_t *), nelem);
654 			break;
655 		case DATA_TYPE_INT8:
656 			ret = nvlist_add_int8(payload, name,
657 			    va_arg(ap, int));
658 			break;
659 		case DATA_TYPE_INT8_ARRAY:
660 			nelem = va_arg(ap, int);
661 			ret = nvlist_add_int8_array(payload, name,
662 			    va_arg(ap, int8_t *), nelem);
663 			break;
664 		case DATA_TYPE_UINT8:
665 			ret = nvlist_add_uint8(payload, name,
666 			    va_arg(ap, uint_t));
667 			break;
668 		case DATA_TYPE_UINT8_ARRAY:
669 			nelem = va_arg(ap, int);
670 			ret = nvlist_add_uint8_array(payload, name,
671 			    va_arg(ap, uint8_t *), nelem);
672 			break;
673 		case DATA_TYPE_INT16:
674 			ret = nvlist_add_int16(payload, name,
675 			    va_arg(ap, int));
676 			break;
677 		case DATA_TYPE_INT16_ARRAY:
678 			nelem = va_arg(ap, int);
679 			ret = nvlist_add_int16_array(payload, name,
680 			    va_arg(ap, int16_t *), nelem);
681 			break;
682 		case DATA_TYPE_UINT16:
683 			ret = nvlist_add_uint16(payload, name,
684 			    va_arg(ap, uint_t));
685 			break;
686 		case DATA_TYPE_UINT16_ARRAY:
687 			nelem = va_arg(ap, int);
688 			ret = nvlist_add_uint16_array(payload, name,
689 			    va_arg(ap, uint16_t *), nelem);
690 			break;
691 		case DATA_TYPE_INT32:
692 			ret = nvlist_add_int32(payload, name,
693 			    va_arg(ap, int32_t));
694 			break;
695 		case DATA_TYPE_INT32_ARRAY:
696 			nelem = va_arg(ap, int);
697 			ret = nvlist_add_int32_array(payload, name,
698 			    va_arg(ap, int32_t *), nelem);
699 			break;
700 		case DATA_TYPE_UINT32:
701 			ret = nvlist_add_uint32(payload, name,
702 			    va_arg(ap, uint32_t));
703 			break;
704 		case DATA_TYPE_UINT32_ARRAY:
705 			nelem = va_arg(ap, int);
706 			ret = nvlist_add_uint32_array(payload, name,
707 			    va_arg(ap, uint32_t *), nelem);
708 			break;
709 		case DATA_TYPE_INT64:
710 			ret = nvlist_add_int64(payload, name,
711 			    va_arg(ap, int64_t));
712 			break;
713 		case DATA_TYPE_INT64_ARRAY:
714 			nelem = va_arg(ap, int);
715 			ret = nvlist_add_int64_array(payload, name,
716 			    va_arg(ap, int64_t *), nelem);
717 			break;
718 		case DATA_TYPE_UINT64:
719 			ret = nvlist_add_uint64(payload, name,
720 			    va_arg(ap, uint64_t));
721 			break;
722 		case DATA_TYPE_UINT64_ARRAY:
723 			nelem = va_arg(ap, int);
724 			ret = nvlist_add_uint64_array(payload, name,
725 			    va_arg(ap, uint64_t *), nelem);
726 			break;
727 		case DATA_TYPE_STRING:
728 			ret = nvlist_add_string(payload, name,
729 			    va_arg(ap, char *));
730 			break;
731 		case DATA_TYPE_STRING_ARRAY:
732 			nelem = va_arg(ap, int);
733 			ret = nvlist_add_string_array(payload, name,
734 			    va_arg(ap, char **), nelem);
735 			break;
736 		case DATA_TYPE_NVLIST:
737 			ret = nvlist_add_nvlist(payload, name,
738 			    va_arg(ap, nvlist_t *));
739 			break;
740 		case DATA_TYPE_NVLIST_ARRAY:
741 			nelem = va_arg(ap, int);
742 			ret = nvlist_add_nvlist_array(payload, name,
743 			    va_arg(ap, nvlist_t **), nelem);
744 			break;
745 		default:
746 			ret = EINVAL;
747 		}
748 
749 		name = va_arg(ap, char *);
750 	}
751 	return (ret);
752 }
753 
754 void
755 fm_payload_set(nvlist_t *payload, ...)
756 {
757 	int ret;
758 	const char *name;
759 	va_list ap;
760 
761 	va_start(ap, payload);
762 	name = va_arg(ap, char *);
763 	ret = i_fm_payload_set(payload, name, ap);
764 	va_end(ap);
765 
766 	if (ret)
767 		atomic_add_64(
768 		    &erpt_kstat_data.payload_set_failed.value.ui64, 1);
769 }
770 
771 /*
772  * Set-up and validate the members of an ereport event according to:
773  *
774  *	Member name		Type		Value
775  *	====================================================
776  *	class			string		ereport
777  *	version			uint8_t		0
778  *	ena			uint64_t	<ena>
779  *	detector		nvlist_t	<detector>
780  *	ereport-payload		nvlist_t	<var args>
781  *
782  */
783 void
784 fm_ereport_set(nvlist_t *ereport, int version, const char *erpt_class,
785     uint64_t ena, const nvlist_t *detector, ...)
786 {
787 	char ereport_class[FM_MAX_CLASS];
788 	const char *name;
789 	va_list ap;
790 	int ret;
791 
792 	if (version != FM_EREPORT_VERS0) {
793 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
794 		return;
795 	}
796 
797 	(void) snprintf(ereport_class, FM_MAX_CLASS, "%s.%s",
798 	    FM_EREPORT_CLASS, erpt_class);
799 	if (nvlist_add_string(ereport, FM_CLASS, ereport_class) != 0) {
800 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
801 		return;
802 	}
803 
804 	if (nvlist_add_uint64(ereport, FM_EREPORT_ENA, ena)) {
805 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
806 	}
807 
808 	if (nvlist_add_nvlist(ereport, FM_EREPORT_DETECTOR,
809 	    (nvlist_t *)detector) != 0) {
810 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
811 	}
812 
813 	va_start(ap, detector);
814 	name = va_arg(ap, const char *);
815 	ret = i_fm_payload_set(ereport, name, ap);
816 	va_end(ap);
817 
818 	if (ret)
819 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
820 }
821 
822 /*
823  * Set-up and validate the members of an hc fmri according to;
824  *
825  *	Member name		Type		Value
826  *	===================================================
827  *	version			uint8_t		0
828  *	auth			nvlist_t	<auth>
829  *	hc-name			string		<name>
830  *	hc-id			string		<id>
831  *
832  * Note that auth and hc-id are optional members.
833  */
834 
835 #define	HC_MAXPAIRS	20
836 #define	HC_MAXNAMELEN	50
837 
838 static int
839 fm_fmri_hc_set_common(nvlist_t *fmri, int version, const nvlist_t *auth)
840 {
841 	if (version != FM_HC_SCHEME_VERSION) {
842 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
843 		return (0);
844 	}
845 
846 	if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0 ||
847 	    nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0) {
848 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
849 		return (0);
850 	}
851 
852 	if (auth != NULL && nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
853 	    (nvlist_t *)auth) != 0) {
854 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
855 		return (0);
856 	}
857 
858 	return (1);
859 }
860 
861 void
862 fm_fmri_hc_set(nvlist_t *fmri, int version, const nvlist_t *auth,
863     nvlist_t *snvl, int npairs, ...)
864 {
865 	nv_alloc_t *nva = nvlist_lookup_nv_alloc(fmri);
866 	nvlist_t *pairs[HC_MAXPAIRS];
867 	va_list ap;
868 	int i;
869 
870 	if (!fm_fmri_hc_set_common(fmri, version, auth))
871 		return;
872 
873 	npairs = MIN(npairs, HC_MAXPAIRS);
874 
875 	va_start(ap, npairs);
876 	for (i = 0; i < npairs; i++) {
877 		const char *name = va_arg(ap, const char *);
878 		uint32_t id = va_arg(ap, uint32_t);
879 		char idstr[11];
880 
881 		(void) snprintf(idstr, sizeof (idstr), "%u", id);
882 
883 		pairs[i] = fm_nvlist_create(nva);
884 		if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, name) != 0 ||
885 		    nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr) != 0) {
886 			atomic_add_64(
887 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
888 		}
889 	}
890 	va_end(ap);
891 
892 	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, pairs, npairs) != 0)
893 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
894 
895 	for (i = 0; i < npairs; i++)
896 		fm_nvlist_destroy(pairs[i], FM_NVA_RETAIN);
897 
898 	if (snvl != NULL) {
899 		if (nvlist_add_nvlist(fmri, FM_FMRI_HC_SPECIFIC, snvl) != 0) {
900 			atomic_add_64(
901 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
902 		}
903 	}
904 }
905 
906 /*
907  * Set-up and validate the members of an dev fmri according to:
908  *
909  *	Member name		Type		Value
910  *	====================================================
911  *	version			uint8_t		0
912  *	auth			nvlist_t	<auth>
913  *	devpath			string		<devpath>
914  *	devid			string		<devid>
915  *
916  * Note that auth and devid are optional members.
917  */
918 void
919 fm_fmri_dev_set(nvlist_t *fmri_dev, int version, const nvlist_t *auth,
920     const char *devpath, const char *devid)
921 {
922 	if (version != DEV_SCHEME_VERSION0) {
923 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
924 		return;
925 	}
926 
927 	if (nvlist_add_uint8(fmri_dev, FM_VERSION, version) != 0) {
928 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
929 		return;
930 	}
931 
932 	if (nvlist_add_string(fmri_dev, FM_FMRI_SCHEME,
933 	    FM_FMRI_SCHEME_DEV) != 0) {
934 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
935 		return;
936 	}
937 
938 	if (auth != NULL) {
939 		if (nvlist_add_nvlist(fmri_dev, FM_FMRI_AUTHORITY,
940 		    (nvlist_t *)auth) != 0) {
941 			atomic_add_64(
942 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
943 		}
944 	}
945 
946 	if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_PATH, devpath) != 0) {
947 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
948 	}
949 
950 	if (devid != NULL)
951 		if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_ID, devid) != 0)
952 			atomic_add_64(
953 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
954 }
955 
956 /*
957  * Set-up and validate the members of an cpu fmri according to:
958  *
959  *	Member name		Type		Value
960  *	====================================================
961  *	version			uint8_t		0
962  *	auth			nvlist_t	<auth>
963  *	cpuid			uint32_t	<cpu_id>
964  *	cpumask			uint8_t		<cpu_mask>
965  *	serial			uint64_t	<serial_id>
966  *
967  * Note that auth, cpumask, serial are optional members.
968  *
969  */
970 void
971 fm_fmri_cpu_set(nvlist_t *fmri_cpu, int version, const nvlist_t *auth,
972     uint32_t cpu_id, uint8_t *cpu_maskp, const char *serial_idp)
973 {
974 	uint64_t *failedp = &erpt_kstat_data.fmri_set_failed.value.ui64;
975 
976 	if (version < CPU_SCHEME_VERSION1) {
977 		atomic_add_64(failedp, 1);
978 		return;
979 	}
980 
981 	if (nvlist_add_uint8(fmri_cpu, FM_VERSION, version) != 0) {
982 		atomic_add_64(failedp, 1);
983 		return;
984 	}
985 
986 	if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME,
987 	    FM_FMRI_SCHEME_CPU) != 0) {
988 		atomic_add_64(failedp, 1);
989 		return;
990 	}
991 
992 	if (auth != NULL && nvlist_add_nvlist(fmri_cpu, FM_FMRI_AUTHORITY,
993 	    (nvlist_t *)auth) != 0)
994 		atomic_add_64(failedp, 1);
995 
996 	if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0)
997 		atomic_add_64(failedp, 1);
998 
999 	if (cpu_maskp != NULL && nvlist_add_uint8(fmri_cpu, FM_FMRI_CPU_MASK,
1000 	    *cpu_maskp) != 0)
1001 		atomic_add_64(failedp, 1);
1002 
1003 	if (serial_idp == NULL || nvlist_add_string(fmri_cpu,
1004 	    FM_FMRI_CPU_SERIAL_ID, (char *)serial_idp) != 0)
1005 			atomic_add_64(failedp, 1);
1006 }
1007 
1008 /*
1009  * Set-up and validate the members of a mem according to:
1010  *
1011  *	Member name		Type		Value
1012  *	====================================================
1013  *	version			uint8_t		0
1014  *	auth			nvlist_t	<auth>		[optional]
1015  *	unum			string		<unum>
1016  *	serial			string		<serial>	[optional*]
1017  *	offset			uint64_t	<offset>	[optional]
1018  *
1019  *	* serial is required if offset is present
1020  */
1021 void
1022 fm_fmri_mem_set(nvlist_t *fmri, int version, const nvlist_t *auth,
1023     const char *unum, const char *serial, uint64_t offset)
1024 {
1025 	if (version != MEM_SCHEME_VERSION0) {
1026 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1027 		return;
1028 	}
1029 
1030 	if (!serial && (offset != (uint64_t)-1)) {
1031 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1032 		return;
1033 	}
1034 
1035 	if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0) {
1036 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1037 		return;
1038 	}
1039 
1040 	if (nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0) {
1041 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1042 		return;
1043 	}
1044 
1045 	if (auth != NULL) {
1046 		if (nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
1047 		    (nvlist_t *)auth) != 0) {
1048 			atomic_add_64(
1049 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1050 		}
1051 	}
1052 
1053 	if (nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum) != 0) {
1054 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1055 	}
1056 
1057 	if (serial != NULL) {
1058 		if (nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID,
1059 		    (char **)&serial, 1) != 0) {
1060 			atomic_add_64(
1061 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1062 		}
1063 		if (offset != (uint64_t)-1) {
1064 			if (nvlist_add_uint64(fmri, FM_FMRI_MEM_OFFSET,
1065 			    offset) != 0) {
1066 				atomic_add_64(&erpt_kstat_data.
1067 				    fmri_set_failed.value.ui64, 1);
1068 			}
1069 		}
1070 	}
1071 }
1072 
1073 uint64_t
1074 fm_ena_increment(uint64_t ena)
1075 {
1076 	uint64_t new_ena;
1077 
1078 	switch (ENA_FORMAT(ena)) {
1079 	case FM_ENA_FMT1:
1080 		new_ena = ena + (1 << ENA_FMT1_GEN_SHFT);
1081 		break;
1082 	case FM_ENA_FMT2:
1083 		new_ena = ena + (1 << ENA_FMT2_GEN_SHFT);
1084 		break;
1085 	default:
1086 		new_ena = 0;
1087 	}
1088 
1089 	return (new_ena);
1090 }
1091 
1092 uint64_t
1093 fm_ena_generate_cpu(uint64_t timestamp, processorid_t cpuid, uchar_t format)
1094 {
1095 	uint64_t ena = 0;
1096 
1097 	switch (format) {
1098 	case FM_ENA_FMT1:
1099 		if (timestamp) {
1100 			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1101 			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
1102 			    ENA_FMT1_CPUID_MASK) |
1103 			    ((timestamp << ENA_FMT1_TIME_SHFT) &
1104 			    ENA_FMT1_TIME_MASK));
1105 		} else {
1106 			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1107 			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
1108 			    ENA_FMT1_CPUID_MASK) |
1109 			    ((gethrtime_waitfree() << ENA_FMT1_TIME_SHFT) &
1110 			    ENA_FMT1_TIME_MASK));
1111 		}
1112 		break;
1113 	case FM_ENA_FMT2:
1114 		ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1115 		    ((timestamp << ENA_FMT2_TIME_SHFT) & ENA_FMT2_TIME_MASK));
1116 		break;
1117 	default:
1118 		break;
1119 	}
1120 
1121 	return (ena);
1122 }
1123 
1124 uint64_t
1125 fm_ena_generate(uint64_t timestamp, uchar_t format)
1126 {
1127 	return (fm_ena_generate_cpu(timestamp, CPU->cpu_id, format));
1128 }
1129 
1130 uint64_t
1131 fm_ena_generation_get(uint64_t ena)
1132 {
1133 	uint64_t gen;
1134 
1135 	switch (ENA_FORMAT(ena)) {
1136 	case FM_ENA_FMT1:
1137 		gen = (ena & ENA_FMT1_GEN_MASK) >> ENA_FMT1_GEN_SHFT;
1138 		break;
1139 	case FM_ENA_FMT2:
1140 		gen = (ena & ENA_FMT2_GEN_MASK) >> ENA_FMT2_GEN_SHFT;
1141 		break;
1142 	default:
1143 		gen = 0;
1144 		break;
1145 	}
1146 
1147 	return (gen);
1148 }
1149 
1150 uchar_t
1151 fm_ena_format_get(uint64_t ena)
1152 {
1153 
1154 	return (ENA_FORMAT(ena));
1155 }
1156 
1157 uint64_t
1158 fm_ena_id_get(uint64_t ena)
1159 {
1160 	uint64_t id;
1161 
1162 	switch (ENA_FORMAT(ena)) {
1163 	case FM_ENA_FMT1:
1164 		id = (ena & ENA_FMT1_ID_MASK) >> ENA_FMT1_ID_SHFT;
1165 		break;
1166 	case FM_ENA_FMT2:
1167 		id = (ena & ENA_FMT2_ID_MASK) >> ENA_FMT2_ID_SHFT;
1168 		break;
1169 	default:
1170 		id = 0;
1171 	}
1172 
1173 	return (id);
1174 }
1175 
1176 uint64_t
1177 fm_ena_time_get(uint64_t ena)
1178 {
1179 	uint64_t time;
1180 
1181 	switch (ENA_FORMAT(ena)) {
1182 	case FM_ENA_FMT1:
1183 		time = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT;
1184 		break;
1185 	case FM_ENA_FMT2:
1186 		time = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT;
1187 		break;
1188 	default:
1189 		time = 0;
1190 	}
1191 
1192 	return (time);
1193 }
1194 
1195 /*
1196  * Convert a getpcstack() trace to symbolic name+offset, and add the resulting
1197  * string array to a Fault Management ereport as FM_EREPORT_PAYLOAD_NAME_STACK.
1198  */
1199 void
1200 fm_payload_stack_add(nvlist_t *payload, const pc_t *stack, int depth)
1201 {
1202 	int i;
1203 	char *sym;
1204 	ulong_t off;
1205 	char *stkpp[FM_STK_DEPTH];
1206 	char buf[FM_STK_DEPTH * FM_SYM_SZ];
1207 	char *stkp = buf;
1208 
1209 	for (i = 0; i < depth && i != FM_STK_DEPTH; i++, stkp += FM_SYM_SZ) {
1210 		if ((sym = kobj_getsymname(stack[i], &off)) != NULL)
1211 			(void) snprintf(stkp, FM_SYM_SZ, "%s+%lx", sym, off);
1212 		else
1213 			(void) snprintf(stkp, FM_SYM_SZ, "%lx", (long)stack[i]);
1214 		stkpp[i] = stkp;
1215 	}
1216 
1217 	fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_STACK,
1218 	    DATA_TYPE_STRING_ARRAY, depth, stkpp, NULL);
1219 }
1220