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