xref: /illumos-gate/usr/src/uts/common/os/fm.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 2005 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/nvpair_impl.h>
64 #include <sys/cmn_err.h>
65 #include <sys/cpuvar.h>
66 #include <sys/sysmacros.h>
67 #include <sys/systm.h>
68 #include <sys/ddifm.h>
69 #include <sys/ddifm_impl.h>
70 #include <sys/spl.h>
71 #include <sys/dumphdr.h>
72 #include <sys/compress.h>
73 #include <sys/cpuvar.h>
74 #include <sys/console.h>
75 #include <sys/panic.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 *nvhdl;
618 
619 	nvhdl = ((nvpriv_t *)(uintptr_t)nvl->nvl_priv)->nvp_nva;
620 
621 	nvlist_free(nvl);
622 
623 	if (nvhdl != NULL) {
624 		if (flag == FM_NVA_FREE)
625 			fm_nva_xdestroy(nvhdl);
626 	}
627 }
628 
629 int
630 i_fm_payload_set(nvlist_t *payload, const char *name, va_list ap)
631 {
632 	int nelem, ret = 0;
633 	data_type_t type;
634 
635 	while (ret == 0 && name != NULL) {
636 		type = va_arg(ap, data_type_t);
637 		switch (type) {
638 		case DATA_TYPE_BYTE:
639 			ret = nvlist_add_byte(payload, name,
640 			    va_arg(ap, uint_t));
641 			break;
642 		case DATA_TYPE_BYTE_ARRAY:
643 			nelem = va_arg(ap, int);
644 			ret = nvlist_add_byte_array(payload, name,
645 			    va_arg(ap, uchar_t *), nelem);
646 			break;
647 		case DATA_TYPE_BOOLEAN_VALUE:
648 			ret = nvlist_add_boolean_value(payload, name,
649 			    va_arg(ap, boolean_t));
650 			break;
651 		case DATA_TYPE_BOOLEAN_ARRAY:
652 			nelem = va_arg(ap, int);
653 			ret = nvlist_add_boolean_array(payload, name,
654 			    va_arg(ap, boolean_t *), nelem);
655 			break;
656 		case DATA_TYPE_INT8:
657 			ret = nvlist_add_int8(payload, name,
658 			    va_arg(ap, int));
659 			break;
660 		case DATA_TYPE_INT8_ARRAY:
661 			nelem = va_arg(ap, int);
662 			ret = nvlist_add_int8_array(payload, name,
663 			    va_arg(ap, int8_t *), nelem);
664 			break;
665 		case DATA_TYPE_UINT8:
666 			ret = nvlist_add_uint8(payload, name,
667 			    va_arg(ap, uint_t));
668 			break;
669 		case DATA_TYPE_UINT8_ARRAY:
670 			nelem = va_arg(ap, int);
671 			ret = nvlist_add_uint8_array(payload, name,
672 			    va_arg(ap, uint8_t *), nelem);
673 			break;
674 		case DATA_TYPE_INT16:
675 			ret = nvlist_add_int16(payload, name,
676 			    va_arg(ap, int));
677 			break;
678 		case DATA_TYPE_INT16_ARRAY:
679 			nelem = va_arg(ap, int);
680 			ret = nvlist_add_int16_array(payload, name,
681 			    va_arg(ap, int16_t *), nelem);
682 			break;
683 		case DATA_TYPE_UINT16:
684 			ret = nvlist_add_uint16(payload, name,
685 			    va_arg(ap, uint_t));
686 			break;
687 		case DATA_TYPE_UINT16_ARRAY:
688 			nelem = va_arg(ap, int);
689 			ret = nvlist_add_uint16_array(payload, name,
690 			    va_arg(ap, uint16_t *), nelem);
691 			break;
692 		case DATA_TYPE_INT32:
693 			ret = nvlist_add_int32(payload, name,
694 			    va_arg(ap, int32_t));
695 			break;
696 		case DATA_TYPE_INT32_ARRAY:
697 			nelem = va_arg(ap, int);
698 			ret = nvlist_add_int32_array(payload, name,
699 			    va_arg(ap, int32_t *), nelem);
700 			break;
701 		case DATA_TYPE_UINT32:
702 			ret = nvlist_add_uint32(payload, name,
703 			    va_arg(ap, uint32_t));
704 			break;
705 		case DATA_TYPE_UINT32_ARRAY:
706 			nelem = va_arg(ap, int);
707 			ret = nvlist_add_uint32_array(payload, name,
708 			    va_arg(ap, uint32_t *), nelem);
709 			break;
710 		case DATA_TYPE_INT64:
711 			ret = nvlist_add_int64(payload, name,
712 			    va_arg(ap, int64_t));
713 			break;
714 		case DATA_TYPE_INT64_ARRAY:
715 			nelem = va_arg(ap, int);
716 			ret = nvlist_add_int64_array(payload, name,
717 			    va_arg(ap, int64_t *), nelem);
718 			break;
719 		case DATA_TYPE_UINT64:
720 			ret = nvlist_add_uint64(payload, name,
721 			    va_arg(ap, uint64_t));
722 			break;
723 		case DATA_TYPE_UINT64_ARRAY:
724 			nelem = va_arg(ap, int);
725 			ret = nvlist_add_uint64_array(payload, name,
726 			    va_arg(ap, uint64_t *), nelem);
727 			break;
728 		case DATA_TYPE_STRING:
729 			ret = nvlist_add_string(payload, name,
730 			    va_arg(ap, char *));
731 			break;
732 		case DATA_TYPE_STRING_ARRAY:
733 			nelem = va_arg(ap, int);
734 			ret = nvlist_add_string_array(payload, name,
735 			    va_arg(ap, char **), nelem);
736 			break;
737 		case DATA_TYPE_NVLIST:
738 			ret = nvlist_add_nvlist(payload, name,
739 			    va_arg(ap, nvlist_t *));
740 			break;
741 		case DATA_TYPE_NVLIST_ARRAY:
742 			nelem = va_arg(ap, int);
743 			ret = nvlist_add_nvlist_array(payload, name,
744 			    va_arg(ap, nvlist_t **), nelem);
745 			break;
746 		default:
747 			ret = EINVAL;
748 		}
749 
750 		name = va_arg(ap, char *);
751 	}
752 	return (ret);
753 }
754 
755 void
756 fm_payload_set(nvlist_t *payload, ...)
757 {
758 	int ret;
759 	const char *name;
760 	va_list ap;
761 
762 	va_start(ap, payload);
763 	name = va_arg(ap, char *);
764 	ret = i_fm_payload_set(payload, name, ap);
765 	va_end(ap);
766 
767 	if (ret)
768 		atomic_add_64(
769 		    &erpt_kstat_data.payload_set_failed.value.ui64, 1);
770 }
771 
772 /*
773  * Set-up and validate the members of an ereport event according to:
774  *
775  *	Member name		Type		Value
776  *	====================================================
777  *	class			string		ereport
778  *	version			uint8_t		0
779  *	ena			uint64_t	<ena>
780  *	detector		nvlist_t	<detector>
781  *	ereport-payload		nvlist_t	<var args>
782  *
783  */
784 void
785 fm_ereport_set(nvlist_t *ereport, int version, const char *erpt_class,
786     uint64_t ena, const nvlist_t *detector, ...)
787 {
788 	char ereport_class[FM_MAX_CLASS];
789 	const char *name;
790 	va_list ap;
791 	int ret;
792 
793 	if (version != FM_EREPORT_VERS0) {
794 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
795 		return;
796 	}
797 
798 	(void) snprintf(ereport_class, FM_MAX_CLASS, "%s.%s",
799 	    FM_EREPORT_CLASS, erpt_class);
800 	if (nvlist_add_string(ereport, FM_CLASS, ereport_class) != 0) {
801 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
802 		return;
803 	}
804 
805 	if (nvlist_add_uint64(ereport, FM_EREPORT_ENA, ena)) {
806 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
807 	}
808 
809 	if (nvlist_add_nvlist(ereport, FM_EREPORT_DETECTOR,
810 	    (nvlist_t *)detector) != 0) {
811 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
812 	}
813 
814 	va_start(ap, detector);
815 	name = va_arg(ap, const char *);
816 	ret = i_fm_payload_set(ereport, name, ap);
817 	va_end(ap);
818 
819 	if (ret)
820 		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
821 }
822 
823 static int
824 i_fm_fmri_hc_set_v0(nvlist_t *hc, uint32_t size, va_list ap)
825 {
826 	int i, ret;
827 	const char *name, *id;
828 	nvlist_t **hc_nvl;
829 
830 	if (size <= 0)
831 		return (0);
832 
833 	hc_nvl = kmem_zalloc(size * sizeof (nvlist_t *), KM_SLEEP);
834 
835 	for (i = 0; i < size; ++i) {
836 		name = va_arg(ap, const char *);
837 		if (name == NULL) {
838 			ret = EINVAL;
839 			goto fail;
840 		}
841 		id = va_arg(ap, const char *);
842 		if ((hc_nvl[i] = fm_nvlist_create(
843 		    ((nvpriv_t *)(uintptr_t)hc->nvl_priv)->nvp_nva)) == NULL) {
844 			ret = ENOMEM;
845 			goto fail;
846 		}
847 		if ((ret = nvlist_add_string(hc_nvl[i], FM_FMRI_HC_NAME,
848 		    name)) != 0)
849 			goto fail;
850 		if ((ret = nvlist_add_string(hc_nvl[i], FM_FMRI_HC_ID,
851 		    id)) != 0)
852 			goto fail;
853 	}
854 
855 	if ((ret = nvlist_add_nvlist_array(hc, FM_FMRI_HC_LIST, hc_nvl,
856 	    size)) != 0)
857 		goto fail;
858 
859 	kmem_free(hc_nvl, size * sizeof (nvlist_t *));
860 	return (0);
861 
862 fail:
863 	for (i = 0; i < size; ++i) {
864 		if (hc_nvl[i] != NULL)
865 			fm_nvlist_destroy(hc_nvl[i], FM_NVA_RETAIN);
866 	}
867 
868 	kmem_free(hc_nvl, size * sizeof (nvlist_t *));
869 	return (ret);
870 }
871 
872 /*
873  * Set-up and validate the members of an dev fmri according to:
874  *
875  *	Member name		Type		Value
876  *	====================================================
877  *	version			uint8_t		0
878  *	auth			nvlist_t	<auth>
879  *	devpath			string		<devpath>
880  *	devid			string		<devid>
881  *
882  * Note that auth and devid are optional members.
883  */
884 void
885 fm_fmri_dev_set(nvlist_t *fmri_dev, int version, const nvlist_t *auth,
886     const char *devpath, const char *devid)
887 {
888 	if (version != DEV_SCHEME_VERSION0) {
889 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
890 		return;
891 	}
892 
893 	if (nvlist_add_uint8(fmri_dev, FM_VERSION, version) != 0) {
894 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
895 		return;
896 	}
897 
898 	if (nvlist_add_string(fmri_dev, FM_FMRI_SCHEME,
899 	    FM_FMRI_SCHEME_DEV) != 0) {
900 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
901 		return;
902 	}
903 
904 	if (auth != NULL) {
905 		if (nvlist_add_nvlist(fmri_dev, FM_FMRI_AUTHORITY,
906 		    (nvlist_t *)auth) != 0) {
907 			atomic_add_64(
908 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
909 		}
910 	}
911 
912 	if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_PATH, devpath) != 0) {
913 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
914 	}
915 
916 	if (devid != NULL)
917 		if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_ID, devid) != 0)
918 			atomic_add_64(
919 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
920 }
921 
922 /*
923  * Set-up and validate the members of an cpu fmri according to:
924  *
925  *	Member name		Type		Value
926  *	====================================================
927  *	version			uint8_t		0
928  *	auth			nvlist_t	<auth>
929  *	cpuid			uint32_t	<cpu_id>
930  *	cpumask			uint8_t		<cpu_mask>
931  *	serial			uint64_t	<serial_id>
932  *
933  * Note that auth is an optional member.
934  *
935  */
936 void
937 fm_fmri_cpu_set(nvlist_t *fmri_cpu, int version, const nvlist_t *auth,
938     uint32_t cpu_id, uint8_t cpu_mask, uint64_t serial_id)
939 {
940 	if (version != CPU_SCHEME_VERSION0) {
941 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
942 		return;
943 	}
944 
945 	if (nvlist_add_uint8(fmri_cpu, FM_VERSION, version) != 0) {
946 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
947 		return;
948 	}
949 
950 	if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME,
951 	    FM_FMRI_SCHEME_CPU) != 0) {
952 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
953 		return;
954 	}
955 
956 	if (auth != NULL)
957 		if (nvlist_add_nvlist(fmri_cpu, FM_FMRI_AUTHORITY,
958 		    (nvlist_t *)auth) != 0) {
959 			atomic_add_64(
960 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
961 		}
962 
963 	if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0) {
964 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
965 	}
966 
967 	if (nvlist_add_uint8(fmri_cpu, FM_FMRI_CPU_MASK, cpu_mask) != 0) {
968 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
969 	}
970 
971 	if (nvlist_add_uint64(fmri_cpu, FM_FMRI_CPU_SERIAL_ID, serial_id)
972 	    != 0)
973 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
974 }
975 
976 /*
977  * Set-up and validate the members of a mem according to:
978  *
979  *	Member name		Type		Value
980  *	====================================================
981  *	version			uint8_t		0
982  *	auth			nvlist_t	<auth>		[optional]
983  *	unum			string		<unum>
984  *	serial			string		<serial>	[optional]
985  *
986  */
987 void
988 fm_fmri_mem_set(nvlist_t *fmri, int version, const nvlist_t *auth,
989     const char *unum, const char *serial)
990 {
991 	if (version != MEM_SCHEME_VERSION0) {
992 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
993 		return;
994 	}
995 
996 	if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0) {
997 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
998 		return;
999 	}
1000 
1001 	if (nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0) {
1002 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1003 		return;
1004 	}
1005 
1006 	if (auth != NULL) {
1007 		if (nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
1008 		    (nvlist_t *)auth) != 0) {
1009 			atomic_add_64(
1010 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1011 		}
1012 	}
1013 
1014 	if (nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum) != 0) {
1015 		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1016 	}
1017 
1018 	if (serial != NULL) {
1019 		if (nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID,
1020 		    (char **)&serial, 1) != 0) {
1021 			atomic_add_64(
1022 			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1023 		}
1024 	}
1025 }
1026 
1027 uint64_t
1028 fm_ena_increment(uint64_t ena)
1029 {
1030 	uint64_t new_ena;
1031 
1032 	switch (ENA_FORMAT(ena)) {
1033 	case FM_ENA_FMT1:
1034 		new_ena = ena + (1 << ENA_FMT1_GEN_SHFT);
1035 		break;
1036 	case FM_ENA_FMT2:
1037 		new_ena = ena + (1 << ENA_FMT2_GEN_SHFT);
1038 		break;
1039 	default:
1040 		new_ena = 0;
1041 	}
1042 
1043 	return (new_ena);
1044 }
1045 
1046 uint64_t
1047 fm_ena_generate_cpu(uint64_t timestamp, processorid_t cpuid, uchar_t format)
1048 {
1049 	uint64_t ena = 0;
1050 
1051 	switch (format) {
1052 	case FM_ENA_FMT1:
1053 		if (timestamp) {
1054 			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1055 			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
1056 			    ENA_FMT1_CPUID_MASK) |
1057 			    ((timestamp << ENA_FMT1_TIME_SHFT) &
1058 			    ENA_FMT1_TIME_MASK));
1059 		} else {
1060 			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1061 			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
1062 			    ENA_FMT1_CPUID_MASK) |
1063 			    ((gethrtime_waitfree() << ENA_FMT1_TIME_SHFT) &
1064 			    ENA_FMT1_TIME_MASK));
1065 		}
1066 		break;
1067 	case FM_ENA_FMT2:
1068 		ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1069 		    ((timestamp << ENA_FMT2_TIME_SHFT) & ENA_FMT2_TIME_MASK));
1070 		break;
1071 	default:
1072 		break;
1073 	}
1074 
1075 	return (ena);
1076 }
1077 
1078 uint64_t
1079 fm_ena_generate(uint64_t timestamp, uchar_t format)
1080 {
1081 	return (fm_ena_generate_cpu(timestamp, CPU->cpu_id, format));
1082 }
1083 
1084 uint64_t
1085 fm_ena_generation_get(uint64_t ena)
1086 {
1087 	uint64_t gen;
1088 
1089 	switch (ENA_FORMAT(ena)) {
1090 	case FM_ENA_FMT1:
1091 		gen = (ena & ENA_FMT1_GEN_MASK) >> ENA_FMT1_GEN_SHFT;
1092 		break;
1093 	case FM_ENA_FMT2:
1094 		gen = (ena & ENA_FMT2_GEN_MASK) >> ENA_FMT2_GEN_SHFT;
1095 		break;
1096 	default:
1097 		gen = 0;
1098 		break;
1099 	}
1100 
1101 	return (gen);
1102 }
1103 
1104 uchar_t
1105 fm_ena_format_get(uint64_t ena)
1106 {
1107 
1108 	return (ENA_FORMAT(ena));
1109 }
1110 
1111 uint64_t
1112 fm_ena_id_get(uint64_t ena)
1113 {
1114 	uint64_t id;
1115 
1116 	switch (ENA_FORMAT(ena)) {
1117 	case FM_ENA_FMT1:
1118 		id = (ena & ENA_FMT1_ID_MASK) >> ENA_FMT1_ID_SHFT;
1119 		break;
1120 	case FM_ENA_FMT2:
1121 		id = (ena & ENA_FMT2_ID_MASK) >> ENA_FMT2_ID_SHFT;
1122 		break;
1123 	default:
1124 		id = 0;
1125 	}
1126 
1127 	return (id);
1128 }
1129 
1130 uint64_t
1131 fm_ena_time_get(uint64_t ena)
1132 {
1133 	uint64_t time;
1134 
1135 	switch (ENA_FORMAT(ena)) {
1136 	case FM_ENA_FMT1:
1137 		time = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT;
1138 		break;
1139 	case FM_ENA_FMT2:
1140 		time = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT;
1141 		break;
1142 	default:
1143 		time = 0;
1144 	}
1145 
1146 	return (time);
1147 }
1148