1219089Spjd/*
2219089Spjd * CDDL HEADER START
3219089Spjd *
4219089Spjd * The contents of this file are subject to the terms of the
5219089Spjd * Common Development and Distribution License (the "License").
6219089Spjd * You may not use this file except in compliance with the License.
7219089Spjd *
8219089Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9219089Spjd * or http://www.opensolaris.org/os/licensing.
10219089Spjd * See the License for the specific language governing permissions
11219089Spjd * and limitations under the License.
12219089Spjd *
13219089Spjd * When distributing Covered Code, include this CDDL HEADER in each
14219089Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15219089Spjd * If applicable, add the following below this CDDL HEADER, with the
16219089Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17219089Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18219089Spjd *
19219089Spjd * CDDL HEADER END
20219089Spjd */
21219089Spjd/*
22219089Spjd * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23219089Spjd */
24219089Spjd
25219089Spjd/*
26219089Spjd * Fault Management Architecture (FMA) Resource and Protocol Support
27219089Spjd *
28219089Spjd * The routines contained herein provide services to support kernel subsystems
29219089Spjd * in publishing fault management telemetry (see PSARC 2002/412 and 2003/089).
30219089Spjd *
31219089Spjd * Name-Value Pair Lists
32219089Spjd *
33219089Spjd * The embodiment of an FMA protocol element (event, fmri or authority) is a
34219089Spjd * name-value pair list (nvlist_t).  FMA-specific nvlist construtor and
35219089Spjd * destructor functions, fm_nvlist_create() and fm_nvlist_destroy(), are used
36219089Spjd * to create an nvpair list using custom allocators.  Callers may choose to
37219089Spjd * allocate either from the kernel memory allocator, or from a preallocated
38219089Spjd * buffer, useful in constrained contexts like high-level interrupt routines.
39219089Spjd *
40219089Spjd * Protocol Event and FMRI Construction
41219089Spjd *
42219089Spjd * Convenience routines are provided to construct nvlist events according to
43219089Spjd * the FMA Event Protocol and Naming Schema specification for ereports and
44219089Spjd * FMRIs for the dev, cpu, hc, mem, legacy hc and de schemes.
45219089Spjd *
46219089Spjd * ENA Manipulation
47219089Spjd *
48219089Spjd * Routines to generate ENA formats 0, 1 and 2 are available as well as
49219089Spjd * routines to increment formats 1 and 2.  Individual fields within the
50219089Spjd * ENA are extractable via fm_ena_time_get(), fm_ena_id_get(),
51219089Spjd * fm_ena_format_get() and fm_ena_gen_get().
52219089Spjd */
53219089Spjd
54219089Spjd#include <sys/types.h>
55219089Spjd#include <sys/time.h>
56219089Spjd#include <sys/sysevent.h>
57219089Spjd#include <sys/nvpair.h>
58219089Spjd#include <sys/cmn_err.h>
59219089Spjd#include <sys/cpuvar.h>
60219089Spjd#include <sys/sysmacros.h>
61219089Spjd#include <sys/systm.h>
62219089Spjd#include <sys/compress.h>
63219089Spjd#include <sys/cpuvar.h>
64219089Spjd#include <sys/kobj.h>
65219089Spjd#include <sys/kstat.h>
66219089Spjd#include <sys/processor.h>
67219089Spjd#include <sys/pcpu.h>
68219089Spjd#include <sys/sunddi.h>
69219089Spjd#include <sys/systeminfo.h>
70219089Spjd#include <sys/sysevent/eventdefs.h>
71219089Spjd#include <sys/fm/util.h>
72219089Spjd#include <sys/fm/protocol.h>
73219089Spjd
74219089Spjd/*
75219089Spjd * URL and SUNW-MSG-ID value to display for fm_panic(), defined below.  These
76219089Spjd * values must be kept in sync with the FMA source code in usr/src/cmd/fm.
77219089Spjd */
78219089Spjdstatic const char *fm_url = "http://www.sun.com/msg";
79219089Spjdstatic const char *fm_msgid = "SUNOS-8000-0G";
80219089Spjdstatic char *volatile fm_panicstr = NULL;
81219089Spjd
82219089Spjd#ifdef sun
83219089Spjderrorq_t *ereport_errorq;
84219089Spjd#endif
85219089Spjdvoid *ereport_dumpbuf;
86219089Spjdsize_t ereport_dumplen;
87219089Spjd
88219089Spjdstatic uint_t ereport_chanlen = ERPT_EVCH_MAX;
89219089Spjdstatic evchan_t *ereport_chan = NULL;
90219089Spjdstatic ulong_t ereport_qlen = 0;
91219089Spjdstatic size_t ereport_size = 0;
92219089Spjdstatic int ereport_cols = 80;
93219089Spjd
94219089Spjdextern void fastreboot_disable_highpil(void);
95219089Spjd
96219089Spjd/*
97219089Spjd * Common fault management kstats to record ereport generation
98219089Spjd * failures
99219089Spjd */
100219089Spjd
101219089Spjdstruct erpt_kstat {
102219089Spjd	kstat_named_t	erpt_dropped;		/* num erpts dropped on post */
103219089Spjd	kstat_named_t	erpt_set_failed;	/* num erpt set failures */
104219089Spjd	kstat_named_t	fmri_set_failed;	/* num fmri set failures */
105219089Spjd	kstat_named_t	payload_set_failed;	/* num payload set failures */
106219089Spjd};
107219089Spjd
108219089Spjdstatic struct erpt_kstat erpt_kstat_data = {
109219089Spjd	{ "erpt-dropped", KSTAT_DATA_UINT64 },
110219089Spjd	{ "erpt-set-failed", KSTAT_DATA_UINT64 },
111219089Spjd	{ "fmri-set-failed", KSTAT_DATA_UINT64 },
112219089Spjd	{ "payload-set-failed", KSTAT_DATA_UINT64 }
113219089Spjd};
114219089Spjd
115219089Spjd#ifdef sun
116219089Spjd/*ARGSUSED*/
117219089Spjdstatic void
118219089Spjdfm_drain(void *private, void *data, errorq_elem_t *eep)
119219089Spjd{
120219089Spjd	nvlist_t *nvl = errorq_elem_nvl(ereport_errorq, eep);
121219089Spjd
122219089Spjd	if (!panicstr)
123219089Spjd		(void) fm_ereport_post(nvl, EVCH_TRYHARD);
124219089Spjd	else
125219089Spjd		fm_nvprint(nvl);
126219089Spjd}
127219089Spjd#endif
128219089Spjd
129219089Spjdvoid
130219089Spjdfm_init(void)
131219089Spjd{
132219089Spjd	kstat_t *ksp;
133219089Spjd
134219089Spjd#ifdef sun
135219089Spjd	(void) sysevent_evc_bind(FM_ERROR_CHAN,
136219089Spjd	    &ereport_chan, EVCH_CREAT | EVCH_HOLD_PEND);
137219089Spjd
138219089Spjd	(void) sysevent_evc_control(ereport_chan,
139219089Spjd	    EVCH_SET_CHAN_LEN, &ereport_chanlen);
140219089Spjd#endif
141219089Spjd
142219089Spjd	if (ereport_qlen == 0)
143219089Spjd		ereport_qlen = ERPT_MAX_ERRS * MAX(max_ncpus, 4);
144219089Spjd
145219089Spjd	if (ereport_size == 0)
146219089Spjd		ereport_size = ERPT_DATA_SZ;
147219089Spjd
148219089Spjd#ifdef sun
149219089Spjd	ereport_errorq = errorq_nvcreate("fm_ereport_queue",
150219089Spjd	    (errorq_func_t)fm_drain, NULL, ereport_qlen, ereport_size,
151219089Spjd	    FM_ERR_PIL, ERRORQ_VITAL);
152219089Spjd	if (ereport_errorq == NULL)
153219089Spjd		panic("failed to create required ereport error queue");
154219089Spjd#endif
155219089Spjd
156219089Spjd	ereport_dumpbuf = kmem_alloc(ereport_size, KM_SLEEP);
157219089Spjd	ereport_dumplen = ereport_size;
158219089Spjd
159219089Spjd	/* Initialize ereport allocation and generation kstats */
160219089Spjd	ksp = kstat_create("unix", 0, "fm", "misc", KSTAT_TYPE_NAMED,
161219089Spjd	    sizeof (struct erpt_kstat) / sizeof (kstat_named_t),
162219089Spjd	    KSTAT_FLAG_VIRTUAL);
163219089Spjd
164219089Spjd	if (ksp != NULL) {
165219089Spjd		ksp->ks_data = &erpt_kstat_data;
166219089Spjd		kstat_install(ksp);
167219089Spjd	} else {
168219089Spjd		cmn_err(CE_NOTE, "failed to create fm/misc kstat\n");
169219089Spjd
170219089Spjd	}
171219089Spjd}
172219089Spjd
173219089Spjd#ifdef sun
174219089Spjd/*
175219089Spjd * Formatting utility function for fm_nvprintr.  We attempt to wrap chunks of
176219089Spjd * output so they aren't split across console lines, and return the end column.
177219089Spjd */
178219089Spjd/*PRINTFLIKE4*/
179219089Spjdstatic int
180219089Spjdfm_printf(int depth, int c, int cols, const char *format, ...)
181219089Spjd{
182219089Spjd	va_list ap;
183219089Spjd	int width;
184219089Spjd	char c1;
185219089Spjd
186219089Spjd	va_start(ap, format);
187219089Spjd	width = vsnprintf(&c1, sizeof (c1), format, ap);
188219089Spjd	va_end(ap);
189219089Spjd
190219089Spjd	if (c + width >= cols) {
191219089Spjd		console_printf("\n\r");
192219089Spjd		c = 0;
193219089Spjd		if (format[0] != ' ' && depth > 0) {
194219089Spjd			console_printf(" ");
195219089Spjd			c++;
196219089Spjd		}
197219089Spjd	}
198219089Spjd
199219089Spjd	va_start(ap, format);
200219089Spjd	console_vprintf(format, ap);
201219089Spjd	va_end(ap);
202219089Spjd
203219089Spjd	return ((c + width) % cols);
204219089Spjd}
205219089Spjd
206219089Spjd/*
207219089Spjd * Recursively print a nvlist in the specified column width and return the
208219089Spjd * column we end up in.  This function is called recursively by fm_nvprint(),
209219089Spjd * below.  We generically format the entire nvpair using hexadecimal
210219089Spjd * integers and strings, and elide any integer arrays.  Arrays are basically
211219089Spjd * used for cache dumps right now, so we suppress them so as not to overwhelm
212219089Spjd * the amount of console output we produce at panic time.  This can be further
213219089Spjd * enhanced as FMA technology grows based upon the needs of consumers.  All
214219089Spjd * FMA telemetry is logged using the dump device transport, so the console
215219089Spjd * output serves only as a fallback in case this procedure is unsuccessful.
216219089Spjd */
217219089Spjdstatic int
218219089Spjdfm_nvprintr(nvlist_t *nvl, int d, int c, int cols)
219219089Spjd{
220219089Spjd	nvpair_t *nvp;
221219089Spjd
222219089Spjd	for (nvp = nvlist_next_nvpair(nvl, NULL);
223219089Spjd	    nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
224219089Spjd
225219089Spjd		data_type_t type = nvpair_type(nvp);
226219089Spjd		const char *name = nvpair_name(nvp);
227219089Spjd
228219089Spjd		boolean_t b;
229219089Spjd		uint8_t i8;
230219089Spjd		uint16_t i16;
231219089Spjd		uint32_t i32;
232219089Spjd		uint64_t i64;
233219089Spjd		char *str;
234219089Spjd		nvlist_t *cnv;
235219089Spjd
236219089Spjd		if (strcmp(name, FM_CLASS) == 0)
237219089Spjd			continue; /* already printed by caller */
238219089Spjd
239219089Spjd		c = fm_printf(d, c, cols, " %s=", name);
240219089Spjd
241219089Spjd		switch (type) {
242219089Spjd		case DATA_TYPE_BOOLEAN:
243219089Spjd			c = fm_printf(d + 1, c, cols, " 1");
244219089Spjd			break;
245219089Spjd
246219089Spjd		case DATA_TYPE_BOOLEAN_VALUE:
247219089Spjd			(void) nvpair_value_boolean_value(nvp, &b);
248219089Spjd			c = fm_printf(d + 1, c, cols, b ? "1" : "0");
249219089Spjd			break;
250219089Spjd
251219089Spjd		case DATA_TYPE_BYTE:
252219089Spjd			(void) nvpair_value_byte(nvp, &i8);
253219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i8);
254219089Spjd			break;
255219089Spjd
256219089Spjd		case DATA_TYPE_INT8:
257219089Spjd			(void) nvpair_value_int8(nvp, (void *)&i8);
258219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i8);
259219089Spjd			break;
260219089Spjd
261219089Spjd		case DATA_TYPE_UINT8:
262219089Spjd			(void) nvpair_value_uint8(nvp, &i8);
263219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i8);
264219089Spjd			break;
265219089Spjd
266219089Spjd		case DATA_TYPE_INT16:
267219089Spjd			(void) nvpair_value_int16(nvp, (void *)&i16);
268219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i16);
269219089Spjd			break;
270219089Spjd
271219089Spjd		case DATA_TYPE_UINT16:
272219089Spjd			(void) nvpair_value_uint16(nvp, &i16);
273219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i16);
274219089Spjd			break;
275219089Spjd
276219089Spjd		case DATA_TYPE_INT32:
277219089Spjd			(void) nvpair_value_int32(nvp, (void *)&i32);
278219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i32);
279219089Spjd			break;
280219089Spjd
281219089Spjd		case DATA_TYPE_UINT32:
282219089Spjd			(void) nvpair_value_uint32(nvp, &i32);
283219089Spjd			c = fm_printf(d + 1, c, cols, "%x", i32);
284219089Spjd			break;
285219089Spjd
286219089Spjd		case DATA_TYPE_INT64:
287219089Spjd			(void) nvpair_value_int64(nvp, (void *)&i64);
288219089Spjd			c = fm_printf(d + 1, c, cols, "%llx",
289219089Spjd			    (u_longlong_t)i64);
290219089Spjd			break;
291219089Spjd
292219089Spjd		case DATA_TYPE_UINT64:
293219089Spjd			(void) nvpair_value_uint64(nvp, &i64);
294219089Spjd			c = fm_printf(d + 1, c, cols, "%llx",
295219089Spjd			    (u_longlong_t)i64);
296219089Spjd			break;
297219089Spjd
298219089Spjd		case DATA_TYPE_HRTIME:
299219089Spjd			(void) nvpair_value_hrtime(nvp, (void *)&i64);
300219089Spjd			c = fm_printf(d + 1, c, cols, "%llx",
301219089Spjd			    (u_longlong_t)i64);
302219089Spjd			break;
303219089Spjd
304219089Spjd		case DATA_TYPE_STRING:
305219089Spjd			(void) nvpair_value_string(nvp, &str);
306219089Spjd			c = fm_printf(d + 1, c, cols, "\"%s\"",
307219089Spjd			    str ? str : "<NULL>");
308219089Spjd			break;
309219089Spjd
310219089Spjd		case DATA_TYPE_NVLIST:
311219089Spjd			c = fm_printf(d + 1, c, cols, "[");
312219089Spjd			(void) nvpair_value_nvlist(nvp, &cnv);
313219089Spjd			c = fm_nvprintr(cnv, d + 1, c, cols);
314219089Spjd			c = fm_printf(d + 1, c, cols, " ]");
315219089Spjd			break;
316219089Spjd
317219089Spjd		case DATA_TYPE_NVLIST_ARRAY: {
318219089Spjd			nvlist_t **val;
319219089Spjd			uint_t i, nelem;
320219089Spjd
321219089Spjd			c = fm_printf(d + 1, c, cols, "[");
322219089Spjd			(void) nvpair_value_nvlist_array(nvp, &val, &nelem);
323219089Spjd			for (i = 0; i < nelem; i++) {
324219089Spjd				c = fm_nvprintr(val[i], d + 1, c, cols);
325219089Spjd			}
326219089Spjd			c = fm_printf(d + 1, c, cols, " ]");
327219089Spjd			}
328219089Spjd			break;
329219089Spjd
330219089Spjd		case DATA_TYPE_BOOLEAN_ARRAY:
331219089Spjd		case DATA_TYPE_BYTE_ARRAY:
332219089Spjd		case DATA_TYPE_INT8_ARRAY:
333219089Spjd		case DATA_TYPE_UINT8_ARRAY:
334219089Spjd		case DATA_TYPE_INT16_ARRAY:
335219089Spjd		case DATA_TYPE_UINT16_ARRAY:
336219089Spjd		case DATA_TYPE_INT32_ARRAY:
337219089Spjd		case DATA_TYPE_UINT32_ARRAY:
338219089Spjd		case DATA_TYPE_INT64_ARRAY:
339219089Spjd		case DATA_TYPE_UINT64_ARRAY:
340219089Spjd		case DATA_TYPE_STRING_ARRAY:
341219089Spjd			c = fm_printf(d + 1, c, cols, "[...]");
342219089Spjd			break;
343219089Spjd		case DATA_TYPE_UNKNOWN:
344219089Spjd			c = fm_printf(d + 1, c, cols, "<unknown>");
345219089Spjd			break;
346219089Spjd		}
347219089Spjd	}
348219089Spjd
349219089Spjd	return (c);
350219089Spjd}
351219089Spjd
352219089Spjdvoid
353219089Spjdfm_nvprint(nvlist_t *nvl)
354219089Spjd{
355219089Spjd	char *class;
356219089Spjd	int c = 0;
357219089Spjd
358219089Spjd	console_printf("\r");
359219089Spjd
360219089Spjd	if (nvlist_lookup_string(nvl, FM_CLASS, &class) == 0)
361219089Spjd		c = fm_printf(0, c, ereport_cols, "%s", class);
362219089Spjd
363219089Spjd	if (fm_nvprintr(nvl, 0, c, ereport_cols) != 0)
364219089Spjd		console_printf("\n");
365219089Spjd
366219089Spjd	console_printf("\n");
367219089Spjd}
368219089Spjd
369219089Spjd/*
370219089Spjd * Wrapper for panic() that first produces an FMA-style message for admins.
371219089Spjd * Normally such messages are generated by fmd(1M)'s syslog-msgs agent: this
372219089Spjd * is the one exception to that rule and the only error that gets messaged.
373219089Spjd * This function is intended for use by subsystems that have detected a fatal
374219089Spjd * error and enqueued appropriate ereports and wish to then force a panic.
375219089Spjd */
376219089Spjd/*PRINTFLIKE1*/
377219089Spjdvoid
378219089Spjdfm_panic(const char *format, ...)
379219089Spjd{
380219089Spjd	va_list ap;
381219089Spjd
382219089Spjd	(void) casptr((void *)&fm_panicstr, NULL, (void *)format);
383219089Spjd#if defined(__i386) || defined(__amd64)
384219089Spjd	fastreboot_disable_highpil();
385219089Spjd#endif /* __i386 || __amd64 */
386219089Spjd	va_start(ap, format);
387219089Spjd	vpanic(format, ap);
388219089Spjd	va_end(ap);
389219089Spjd}
390219089Spjd
391219089Spjd/*
392219089Spjd * Simply tell the caller if fm_panicstr is set, ie. an fma event has
393219089Spjd * caused the panic. If so, something other than the default panic
394219089Spjd * diagnosis method will diagnose the cause of the panic.
395219089Spjd */
396219089Spjdint
397219089Spjdis_fm_panic()
398219089Spjd{
399219089Spjd	if (fm_panicstr)
400219089Spjd		return (1);
401219089Spjd	else
402219089Spjd		return (0);
403219089Spjd}
404219089Spjd
405219089Spjd/*
406219089Spjd * Print any appropriate FMA banner message before the panic message.  This
407219089Spjd * function is called by panicsys() and prints the message for fm_panic().
408219089Spjd * We print the message here so that it comes after the system is quiesced.
409219089Spjd * A one-line summary is recorded in the log only (cmn_err(9F) with "!" prefix).
410219089Spjd * The rest of the message is for the console only and not needed in the log,
411219089Spjd * so it is printed using console_printf().  We break it up into multiple
412219089Spjd * chunks so as to avoid overflowing any small legacy prom_printf() buffers.
413219089Spjd */
414219089Spjdvoid
415219089Spjdfm_banner(void)
416219089Spjd{
417219089Spjd	timespec_t tod;
418219089Spjd	hrtime_t now;
419219089Spjd
420219089Spjd	if (!fm_panicstr)
421219089Spjd		return; /* panic was not initiated by fm_panic(); do nothing */
422219089Spjd
423219089Spjd	if (panicstr) {
424219089Spjd		tod = panic_hrestime;
425219089Spjd		now = panic_hrtime;
426219089Spjd	} else {
427219089Spjd		gethrestime(&tod);
428219089Spjd		now = gethrtime_waitfree();
429219089Spjd	}
430219089Spjd
431219089Spjd	cmn_err(CE_NOTE, "!SUNW-MSG-ID: %s, "
432219089Spjd	    "TYPE: Error, VER: 1, SEVERITY: Major\n", fm_msgid);
433219089Spjd
434219089Spjd	console_printf(
435219089Spjd"\n\rSUNW-MSG-ID: %s, TYPE: Error, VER: 1, SEVERITY: Major\n"
436219089Spjd"EVENT-TIME: 0x%lx.0x%lx (0x%llx)\n",
437219089Spjd	    fm_msgid, tod.tv_sec, tod.tv_nsec, (u_longlong_t)now);
438219089Spjd
439219089Spjd	console_printf(
440219089Spjd"PLATFORM: %s, CSN: -, HOSTNAME: %s\n"
441219089Spjd"SOURCE: %s, REV: %s %s\n",
442219089Spjd	    platform, utsname.nodename, utsname.sysname,
443219089Spjd	    utsname.release, utsname.version);
444219089Spjd
445219089Spjd	console_printf(
446219089Spjd"DESC: Errors have been detected that require a reboot to ensure system\n"
447219089Spjd"integrity.  See %s/%s for more information.\n",
448219089Spjd	    fm_url, fm_msgid);
449219089Spjd
450219089Spjd	console_printf(
451219089Spjd"AUTO-RESPONSE: Solaris will attempt to save and diagnose the error telemetry\n"
452219089Spjd"IMPACT: The system will sync files, save a crash dump if needed, and reboot\n"
453219089Spjd"REC-ACTION: Save the error summary below in case telemetry cannot be saved\n");
454219089Spjd
455219089Spjd	console_printf("\n");
456219089Spjd}
457219089Spjd
458219089Spjd/*
459219089Spjd * Utility function to write all of the pending ereports to the dump device.
460219089Spjd * This function is called at either normal reboot or panic time, and simply
461219089Spjd * iterates over the in-transit messages in the ereport sysevent channel.
462219089Spjd */
463219089Spjdvoid
464219089Spjdfm_ereport_dump(void)
465219089Spjd{
466219089Spjd	evchanq_t *chq;
467219089Spjd	sysevent_t *sep;
468219089Spjd	erpt_dump_t ed;
469219089Spjd
470219089Spjd	timespec_t tod;
471219089Spjd	hrtime_t now;
472219089Spjd	char *buf;
473219089Spjd	size_t len;
474219089Spjd
475219089Spjd	if (panicstr) {
476219089Spjd		tod = panic_hrestime;
477219089Spjd		now = panic_hrtime;
478219089Spjd	} else {
479219089Spjd		if (ereport_errorq != NULL)
480219089Spjd			errorq_drain(ereport_errorq);
481219089Spjd		gethrestime(&tod);
482219089Spjd		now = gethrtime_waitfree();
483219089Spjd	}
484219089Spjd
485219089Spjd	/*
486219089Spjd	 * In the panic case, sysevent_evc_walk_init() will return NULL.
487219089Spjd	 */
488219089Spjd	if ((chq = sysevent_evc_walk_init(ereport_chan, NULL)) == NULL &&
489219089Spjd	    !panicstr)
490219089Spjd		return; /* event channel isn't initialized yet */
491219089Spjd
492219089Spjd	while ((sep = sysevent_evc_walk_step(chq)) != NULL) {
493219089Spjd		if ((buf = sysevent_evc_event_attr(sep, &len)) == NULL)
494219089Spjd			break;
495219089Spjd
496219089Spjd		ed.ed_magic = ERPT_MAGIC;
497219089Spjd		ed.ed_chksum = checksum32(buf, len);
498219089Spjd		ed.ed_size = (uint32_t)len;
499219089Spjd		ed.ed_pad = 0;
500219089Spjd		ed.ed_hrt_nsec = SE_TIME(sep);
501219089Spjd		ed.ed_hrt_base = now;
502219089Spjd		ed.ed_tod_base.sec = tod.tv_sec;
503219089Spjd		ed.ed_tod_base.nsec = tod.tv_nsec;
504219089Spjd
505219089Spjd		dumpvp_write(&ed, sizeof (ed));
506219089Spjd		dumpvp_write(buf, len);
507219089Spjd	}
508219089Spjd
509219089Spjd	sysevent_evc_walk_fini(chq);
510219089Spjd}
511219089Spjd#endif
512219089Spjd
513219089Spjd/*
514219089Spjd * Post an error report (ereport) to the sysevent error channel.  The error
515219089Spjd * channel must be established with a prior call to sysevent_evc_create()
516219089Spjd * before publication may occur.
517219089Spjd */
518219089Spjdvoid
519219089Spjdfm_ereport_post(nvlist_t *ereport, int evc_flag)
520219089Spjd{
521219089Spjd	size_t nvl_size = 0;
522219089Spjd	evchan_t *error_chan;
523219089Spjd	sysevent_id_t eid;
524219089Spjd
525219089Spjd	(void) nvlist_size(ereport, &nvl_size, NV_ENCODE_NATIVE);
526219089Spjd	if (nvl_size > ERPT_DATA_SZ || nvl_size == 0) {
527219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
528219089Spjd		return;
529219089Spjd	}
530219089Spjd
531219089Spjd#ifdef sun
532219089Spjd	if (sysevent_evc_bind(FM_ERROR_CHAN, &error_chan,
533219089Spjd	    EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
534219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
535219089Spjd		return;
536219089Spjd	}
537219089Spjd
538219089Spjd	if (sysevent_evc_publish(error_chan, EC_FM, ESC_FM_ERROR,
539219089Spjd	    SUNW_VENDOR, FM_PUB, ereport, evc_flag) != 0) {
540219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1);
541219089Spjd		(void) sysevent_evc_unbind(error_chan);
542219089Spjd		return;
543219089Spjd	}
544219089Spjd	(void) sysevent_evc_unbind(error_chan);
545219089Spjd#else
546219089Spjd	(void) ddi_log_sysevent(NULL, SUNW_VENDOR, EC_DEV_STATUS,
547219089Spjd	    ESC_DEV_DLE, ereport, &eid, DDI_SLEEP);
548219089Spjd#endif
549219089Spjd}
550219089Spjd
551219089Spjd/*
552219089Spjd * Wrapppers for FM nvlist allocators
553219089Spjd */
554219089Spjd/* ARGSUSED */
555219089Spjdstatic void *
556219089Spjdi_fm_alloc(nv_alloc_t *nva, size_t size)
557219089Spjd{
558219089Spjd	return (kmem_zalloc(size, KM_SLEEP));
559219089Spjd}
560219089Spjd
561219089Spjd/* ARGSUSED */
562219089Spjdstatic void
563219089Spjdi_fm_free(nv_alloc_t *nva, void *buf, size_t size)
564219089Spjd{
565219089Spjd	kmem_free(buf, size);
566219089Spjd}
567219089Spjd
568219089Spjdconst nv_alloc_ops_t fm_mem_alloc_ops = {
569219089Spjd	NULL,
570219089Spjd	NULL,
571219089Spjd	i_fm_alloc,
572219089Spjd	i_fm_free,
573219089Spjd	NULL
574219089Spjd};
575219089Spjd
576219089Spjd/*
577219089Spjd * Create and initialize a new nv_alloc_t for a fixed buffer, buf.  A pointer
578219089Spjd * to the newly allocated nv_alloc_t structure is returned upon success or NULL
579219089Spjd * is returned to indicate that the nv_alloc structure could not be created.
580219089Spjd */
581219089Spjdnv_alloc_t *
582219089Spjdfm_nva_xcreate(char *buf, size_t bufsz)
583219089Spjd{
584219089Spjd	nv_alloc_t *nvhdl = kmem_zalloc(sizeof (nv_alloc_t), KM_SLEEP);
585219089Spjd
586219089Spjd	if (bufsz == 0 || nv_alloc_init(nvhdl, nv_fixed_ops, buf, bufsz) != 0) {
587219089Spjd		kmem_free(nvhdl, sizeof (nv_alloc_t));
588219089Spjd		return (NULL);
589219089Spjd	}
590219089Spjd
591219089Spjd	return (nvhdl);
592219089Spjd}
593219089Spjd
594219089Spjd/*
595219089Spjd * Destroy a previously allocated nv_alloc structure.  The fixed buffer
596219089Spjd * associated with nva must be freed by the caller.
597219089Spjd */
598219089Spjdvoid
599219089Spjdfm_nva_xdestroy(nv_alloc_t *nva)
600219089Spjd{
601219089Spjd	nv_alloc_fini(nva);
602219089Spjd	kmem_free(nva, sizeof (nv_alloc_t));
603219089Spjd}
604219089Spjd
605219089Spjd/*
606219089Spjd * Create a new nv list.  A pointer to a new nv list structure is returned
607219089Spjd * upon success or NULL is returned to indicate that the structure could
608219089Spjd * not be created.  The newly created nv list is created and managed by the
609219089Spjd * operations installed in nva.   If nva is NULL, the default FMA nva
610219089Spjd * operations are installed and used.
611219089Spjd *
612219089Spjd * When called from the kernel and nva == NULL, this function must be called
613219089Spjd * from passive kernel context with no locks held that can prevent a
614219089Spjd * sleeping memory allocation from occurring.  Otherwise, this function may
615219089Spjd * be called from other kernel contexts as long a valid nva created via
616219089Spjd * fm_nva_create() is supplied.
617219089Spjd */
618219089Spjdnvlist_t *
619219089Spjdfm_nvlist_create(nv_alloc_t *nva)
620219089Spjd{
621219089Spjd	int hdl_alloced = 0;
622219089Spjd	nvlist_t *nvl;
623219089Spjd	nv_alloc_t *nvhdl;
624219089Spjd
625219089Spjd	if (nva == NULL) {
626219089Spjd		nvhdl = kmem_zalloc(sizeof (nv_alloc_t), KM_SLEEP);
627219089Spjd
628219089Spjd		if (nv_alloc_init(nvhdl, &fm_mem_alloc_ops, NULL, 0) != 0) {
629219089Spjd			kmem_free(nvhdl, sizeof (nv_alloc_t));
630219089Spjd			return (NULL);
631219089Spjd		}
632219089Spjd		hdl_alloced = 1;
633219089Spjd	} else {
634219089Spjd		nvhdl = nva;
635219089Spjd	}
636219089Spjd
637219089Spjd	if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, nvhdl) != 0) {
638219089Spjd		if (hdl_alloced) {
639219089Spjd			nv_alloc_fini(nvhdl);
640219089Spjd			kmem_free(nvhdl, sizeof (nv_alloc_t));
641219089Spjd		}
642219089Spjd		return (NULL);
643219089Spjd	}
644219089Spjd
645219089Spjd	return (nvl);
646219089Spjd}
647219089Spjd
648219089Spjd/*
649219089Spjd * Destroy a previously allocated nvlist structure.  flag indicates whether
650219089Spjd * or not the associated nva structure should be freed (FM_NVA_FREE) or
651219089Spjd * retained (FM_NVA_RETAIN).  Retaining the nv alloc structure allows
652219089Spjd * it to be re-used for future nvlist creation operations.
653219089Spjd */
654219089Spjdvoid
655219089Spjdfm_nvlist_destroy(nvlist_t *nvl, int flag)
656219089Spjd{
657219089Spjd	nv_alloc_t *nva = nvlist_lookup_nv_alloc(nvl);
658219089Spjd
659219089Spjd	nvlist_free(nvl);
660219089Spjd
661219089Spjd	if (nva != NULL) {
662219089Spjd		if (flag == FM_NVA_FREE)
663219089Spjd			fm_nva_xdestroy(nva);
664219089Spjd	}
665219089Spjd}
666219089Spjd
667219089Spjdint
668219089Spjdi_fm_payload_set(nvlist_t *payload, const char *name, va_list ap)
669219089Spjd{
670219089Spjd	int nelem, ret = 0;
671219089Spjd	data_type_t type;
672219089Spjd
673219089Spjd	while (ret == 0 && name != NULL) {
674219089Spjd		type = va_arg(ap, data_type_t);
675219089Spjd		switch (type) {
676219089Spjd		case DATA_TYPE_BYTE:
677219089Spjd			ret = nvlist_add_byte(payload, name,
678219089Spjd			    va_arg(ap, uint_t));
679219089Spjd			break;
680219089Spjd		case DATA_TYPE_BYTE_ARRAY:
681219089Spjd			nelem = va_arg(ap, int);
682219089Spjd			ret = nvlist_add_byte_array(payload, name,
683219089Spjd			    va_arg(ap, uchar_t *), nelem);
684219089Spjd			break;
685219089Spjd		case DATA_TYPE_BOOLEAN_VALUE:
686219089Spjd			ret = nvlist_add_boolean_value(payload, name,
687219089Spjd			    va_arg(ap, boolean_t));
688219089Spjd			break;
689219089Spjd		case DATA_TYPE_BOOLEAN_ARRAY:
690219089Spjd			nelem = va_arg(ap, int);
691219089Spjd			ret = nvlist_add_boolean_array(payload, name,
692219089Spjd			    va_arg(ap, boolean_t *), nelem);
693219089Spjd			break;
694219089Spjd		case DATA_TYPE_INT8:
695219089Spjd			ret = nvlist_add_int8(payload, name,
696219089Spjd			    va_arg(ap, int));
697219089Spjd			break;
698219089Spjd		case DATA_TYPE_INT8_ARRAY:
699219089Spjd			nelem = va_arg(ap, int);
700219089Spjd			ret = nvlist_add_int8_array(payload, name,
701219089Spjd			    va_arg(ap, int8_t *), nelem);
702219089Spjd			break;
703219089Spjd		case DATA_TYPE_UINT8:
704219089Spjd			ret = nvlist_add_uint8(payload, name,
705219089Spjd			    va_arg(ap, uint_t));
706219089Spjd			break;
707219089Spjd		case DATA_TYPE_UINT8_ARRAY:
708219089Spjd			nelem = va_arg(ap, int);
709219089Spjd			ret = nvlist_add_uint8_array(payload, name,
710219089Spjd			    va_arg(ap, uint8_t *), nelem);
711219089Spjd			break;
712219089Spjd		case DATA_TYPE_INT16:
713219089Spjd			ret = nvlist_add_int16(payload, name,
714219089Spjd			    va_arg(ap, int));
715219089Spjd			break;
716219089Spjd		case DATA_TYPE_INT16_ARRAY:
717219089Spjd			nelem = va_arg(ap, int);
718219089Spjd			ret = nvlist_add_int16_array(payload, name,
719219089Spjd			    va_arg(ap, int16_t *), nelem);
720219089Spjd			break;
721219089Spjd		case DATA_TYPE_UINT16:
722219089Spjd			ret = nvlist_add_uint16(payload, name,
723219089Spjd			    va_arg(ap, uint_t));
724219089Spjd			break;
725219089Spjd		case DATA_TYPE_UINT16_ARRAY:
726219089Spjd			nelem = va_arg(ap, int);
727219089Spjd			ret = nvlist_add_uint16_array(payload, name,
728219089Spjd			    va_arg(ap, uint16_t *), nelem);
729219089Spjd			break;
730219089Spjd		case DATA_TYPE_INT32:
731219089Spjd			ret = nvlist_add_int32(payload, name,
732219089Spjd			    va_arg(ap, int32_t));
733219089Spjd			break;
734219089Spjd		case DATA_TYPE_INT32_ARRAY:
735219089Spjd			nelem = va_arg(ap, int);
736219089Spjd			ret = nvlist_add_int32_array(payload, name,
737219089Spjd			    va_arg(ap, int32_t *), nelem);
738219089Spjd			break;
739219089Spjd		case DATA_TYPE_UINT32:
740219089Spjd			ret = nvlist_add_uint32(payload, name,
741219089Spjd			    va_arg(ap, uint32_t));
742219089Spjd			break;
743219089Spjd		case DATA_TYPE_UINT32_ARRAY:
744219089Spjd			nelem = va_arg(ap, int);
745219089Spjd			ret = nvlist_add_uint32_array(payload, name,
746219089Spjd			    va_arg(ap, uint32_t *), nelem);
747219089Spjd			break;
748219089Spjd		case DATA_TYPE_INT64:
749219089Spjd			ret = nvlist_add_int64(payload, name,
750219089Spjd			    va_arg(ap, int64_t));
751219089Spjd			break;
752219089Spjd		case DATA_TYPE_INT64_ARRAY:
753219089Spjd			nelem = va_arg(ap, int);
754219089Spjd			ret = nvlist_add_int64_array(payload, name,
755219089Spjd			    va_arg(ap, int64_t *), nelem);
756219089Spjd			break;
757219089Spjd		case DATA_TYPE_UINT64:
758219089Spjd			ret = nvlist_add_uint64(payload, name,
759219089Spjd			    va_arg(ap, uint64_t));
760219089Spjd			break;
761219089Spjd		case DATA_TYPE_UINT64_ARRAY:
762219089Spjd			nelem = va_arg(ap, int);
763219089Spjd			ret = nvlist_add_uint64_array(payload, name,
764219089Spjd			    va_arg(ap, uint64_t *), nelem);
765219089Spjd			break;
766219089Spjd		case DATA_TYPE_STRING:
767219089Spjd			ret = nvlist_add_string(payload, name,
768219089Spjd			    va_arg(ap, char *));
769219089Spjd			break;
770219089Spjd		case DATA_TYPE_STRING_ARRAY:
771219089Spjd			nelem = va_arg(ap, int);
772219089Spjd			ret = nvlist_add_string_array(payload, name,
773219089Spjd			    va_arg(ap, char **), nelem);
774219089Spjd			break;
775219089Spjd		case DATA_TYPE_NVLIST:
776219089Spjd			ret = nvlist_add_nvlist(payload, name,
777219089Spjd			    va_arg(ap, nvlist_t *));
778219089Spjd			break;
779219089Spjd		case DATA_TYPE_NVLIST_ARRAY:
780219089Spjd			nelem = va_arg(ap, int);
781219089Spjd			ret = nvlist_add_nvlist_array(payload, name,
782219089Spjd			    va_arg(ap, nvlist_t **), nelem);
783219089Spjd			break;
784219089Spjd		default:
785219089Spjd			ret = EINVAL;
786219089Spjd		}
787219089Spjd
788219089Spjd		name = va_arg(ap, char *);
789219089Spjd	}
790219089Spjd	return (ret);
791219089Spjd}
792219089Spjd
793219089Spjdvoid
794219089Spjdfm_payload_set(nvlist_t *payload, ...)
795219089Spjd{
796219089Spjd	int ret;
797219089Spjd	const char *name;
798219089Spjd	va_list ap;
799219089Spjd
800219089Spjd	va_start(ap, payload);
801219089Spjd	name = va_arg(ap, char *);
802219089Spjd	ret = i_fm_payload_set(payload, name, ap);
803219089Spjd	va_end(ap);
804219089Spjd
805219089Spjd	if (ret)
806219089Spjd		atomic_add_64(
807219089Spjd		    &erpt_kstat_data.payload_set_failed.value.ui64, 1);
808219089Spjd}
809219089Spjd
810219089Spjd/*
811219089Spjd * Set-up and validate the members of an ereport event according to:
812219089Spjd *
813219089Spjd *	Member name		Type		Value
814219089Spjd *	====================================================
815219089Spjd *	class			string		ereport
816219089Spjd *	version			uint8_t		0
817219089Spjd *	ena			uint64_t	<ena>
818219089Spjd *	detector		nvlist_t	<detector>
819219089Spjd *	ereport-payload		nvlist_t	<var args>
820219089Spjd *
821219089Spjd * We don't actually add a 'version' member to the payload.  Really,
822219089Spjd * the version quoted to us by our caller is that of the category 1
823219089Spjd * "ereport" event class (and we require FM_EREPORT_VERS0) but
824219089Spjd * the payload version of the actual leaf class event under construction
825219089Spjd * may be something else.  Callers should supply a version in the varargs,
826219089Spjd * or (better) we could take two version arguments - one for the
827219089Spjd * ereport category 1 classification (expect FM_EREPORT_VERS0) and one
828219089Spjd * for the leaf class.
829219089Spjd */
830219089Spjdvoid
831219089Spjdfm_ereport_set(nvlist_t *ereport, int version, const char *erpt_class,
832219089Spjd    uint64_t ena, const nvlist_t *detector, ...)
833219089Spjd{
834219089Spjd	char ereport_class[FM_MAX_CLASS];
835219089Spjd	const char *name;
836219089Spjd	va_list ap;
837219089Spjd	int ret;
838219089Spjd
839219089Spjd	if (version != FM_EREPORT_VERS0) {
840219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
841219089Spjd		return;
842219089Spjd	}
843219089Spjd
844219089Spjd	(void) snprintf(ereport_class, FM_MAX_CLASS, "%s.%s",
845219089Spjd	    FM_EREPORT_CLASS, erpt_class);
846219089Spjd	if (nvlist_add_string(ereport, FM_CLASS, ereport_class) != 0) {
847219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
848219089Spjd		return;
849219089Spjd	}
850219089Spjd
851219089Spjd	if (nvlist_add_uint64(ereport, FM_EREPORT_ENA, ena)) {
852219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
853219089Spjd	}
854219089Spjd
855219089Spjd	if (nvlist_add_nvlist(ereport, FM_EREPORT_DETECTOR,
856219089Spjd	    (nvlist_t *)detector) != 0) {
857219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
858219089Spjd	}
859219089Spjd
860219089Spjd	va_start(ap, detector);
861219089Spjd	name = va_arg(ap, const char *);
862219089Spjd	ret = i_fm_payload_set(ereport, name, ap);
863219089Spjd	va_end(ap);
864219089Spjd
865219089Spjd	if (ret)
866219089Spjd		atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1);
867219089Spjd}
868219089Spjd
869219089Spjd/*
870219089Spjd * Set-up and validate the members of an hc fmri according to;
871219089Spjd *
872219089Spjd *	Member name		Type		Value
873219089Spjd *	===================================================
874219089Spjd *	version			uint8_t		0
875219089Spjd *	auth			nvlist_t	<auth>
876219089Spjd *	hc-name			string		<name>
877219089Spjd *	hc-id			string		<id>
878219089Spjd *
879219089Spjd * Note that auth and hc-id are optional members.
880219089Spjd */
881219089Spjd
882219089Spjd#define	HC_MAXPAIRS	20
883219089Spjd#define	HC_MAXNAMELEN	50
884219089Spjd
885219089Spjdstatic int
886219089Spjdfm_fmri_hc_set_common(nvlist_t *fmri, int version, const nvlist_t *auth)
887219089Spjd{
888219089Spjd	if (version != FM_HC_SCHEME_VERSION) {
889219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
890219089Spjd		return (0);
891219089Spjd	}
892219089Spjd
893219089Spjd	if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0 ||
894219089Spjd	    nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0) {
895219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
896219089Spjd		return (0);
897219089Spjd	}
898219089Spjd
899219089Spjd	if (auth != NULL && nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
900219089Spjd	    (nvlist_t *)auth) != 0) {
901219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
902219089Spjd		return (0);
903219089Spjd	}
904219089Spjd
905219089Spjd	return (1);
906219089Spjd}
907219089Spjd
908219089Spjdvoid
909219089Spjdfm_fmri_hc_set(nvlist_t *fmri, int version, const nvlist_t *auth,
910219089Spjd    nvlist_t *snvl, int npairs, ...)
911219089Spjd{
912219089Spjd	nv_alloc_t *nva = nvlist_lookup_nv_alloc(fmri);
913219089Spjd	nvlist_t *pairs[HC_MAXPAIRS];
914219089Spjd	va_list ap;
915219089Spjd	int i;
916219089Spjd
917219089Spjd	if (!fm_fmri_hc_set_common(fmri, version, auth))
918219089Spjd		return;
919219089Spjd
920219089Spjd	npairs = MIN(npairs, HC_MAXPAIRS);
921219089Spjd
922219089Spjd	va_start(ap, npairs);
923219089Spjd	for (i = 0; i < npairs; i++) {
924219089Spjd		const char *name = va_arg(ap, const char *);
925219089Spjd		uint32_t id = va_arg(ap, uint32_t);
926219089Spjd		char idstr[11];
927219089Spjd
928219089Spjd		(void) snprintf(idstr, sizeof (idstr), "%u", id);
929219089Spjd
930219089Spjd		pairs[i] = fm_nvlist_create(nva);
931219089Spjd		if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, name) != 0 ||
932219089Spjd		    nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr) != 0) {
933219089Spjd			atomic_add_64(
934219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
935219089Spjd		}
936219089Spjd	}
937219089Spjd	va_end(ap);
938219089Spjd
939219089Spjd	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, pairs, npairs) != 0)
940219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
941219089Spjd
942219089Spjd	for (i = 0; i < npairs; i++)
943219089Spjd		fm_nvlist_destroy(pairs[i], FM_NVA_RETAIN);
944219089Spjd
945219089Spjd	if (snvl != NULL) {
946219089Spjd		if (nvlist_add_nvlist(fmri, FM_FMRI_HC_SPECIFIC, snvl) != 0) {
947219089Spjd			atomic_add_64(
948219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
949219089Spjd		}
950219089Spjd	}
951219089Spjd}
952219089Spjd
953219089Spjd/*
954219089Spjd * Set-up and validate the members of an dev fmri according to:
955219089Spjd *
956219089Spjd *	Member name		Type		Value
957219089Spjd *	====================================================
958219089Spjd *	version			uint8_t		0
959219089Spjd *	auth			nvlist_t	<auth>
960219089Spjd *	devpath			string		<devpath>
961219089Spjd *	[devid]			string		<devid>
962219089Spjd *	[target-port-l0id]	string		<target-port-lun0-id>
963219089Spjd *
964219089Spjd * Note that auth and devid are optional members.
965219089Spjd */
966219089Spjdvoid
967219089Spjdfm_fmri_dev_set(nvlist_t *fmri_dev, int version, const nvlist_t *auth,
968219089Spjd    const char *devpath, const char *devid, const char *tpl0)
969219089Spjd{
970219089Spjd	int err = 0;
971219089Spjd
972219089Spjd	if (version != DEV_SCHEME_VERSION0) {
973219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
974219089Spjd		return;
975219089Spjd	}
976219089Spjd
977219089Spjd	err |= nvlist_add_uint8(fmri_dev, FM_VERSION, version);
978219089Spjd	err |= nvlist_add_string(fmri_dev, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
979219089Spjd
980219089Spjd	if (auth != NULL) {
981219089Spjd		err |= nvlist_add_nvlist(fmri_dev, FM_FMRI_AUTHORITY,
982219089Spjd		    (nvlist_t *)auth);
983219089Spjd	}
984219089Spjd
985219089Spjd	err |= nvlist_add_string(fmri_dev, FM_FMRI_DEV_PATH, devpath);
986219089Spjd
987219089Spjd	if (devid != NULL)
988219089Spjd		err |= nvlist_add_string(fmri_dev, FM_FMRI_DEV_ID, devid);
989219089Spjd
990219089Spjd	if (tpl0 != NULL)
991219089Spjd		err |= nvlist_add_string(fmri_dev, FM_FMRI_DEV_TGTPTLUN0, tpl0);
992219089Spjd
993219089Spjd	if (err)
994219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
995219089Spjd
996219089Spjd}
997219089Spjd
998219089Spjd/*
999219089Spjd * Set-up and validate the members of an cpu fmri according to:
1000219089Spjd *
1001219089Spjd *	Member name		Type		Value
1002219089Spjd *	====================================================
1003219089Spjd *	version			uint8_t		0
1004219089Spjd *	auth			nvlist_t	<auth>
1005219089Spjd *	cpuid			uint32_t	<cpu_id>
1006219089Spjd *	cpumask			uint8_t		<cpu_mask>
1007219089Spjd *	serial			uint64_t	<serial_id>
1008219089Spjd *
1009219089Spjd * Note that auth, cpumask, serial are optional members.
1010219089Spjd *
1011219089Spjd */
1012219089Spjdvoid
1013219089Spjdfm_fmri_cpu_set(nvlist_t *fmri_cpu, int version, const nvlist_t *auth,
1014219089Spjd    uint32_t cpu_id, uint8_t *cpu_maskp, const char *serial_idp)
1015219089Spjd{
1016219089Spjd	uint64_t *failedp = &erpt_kstat_data.fmri_set_failed.value.ui64;
1017219089Spjd
1018219089Spjd	if (version < CPU_SCHEME_VERSION1) {
1019219089Spjd		atomic_add_64(failedp, 1);
1020219089Spjd		return;
1021219089Spjd	}
1022219089Spjd
1023219089Spjd	if (nvlist_add_uint8(fmri_cpu, FM_VERSION, version) != 0) {
1024219089Spjd		atomic_add_64(failedp, 1);
1025219089Spjd		return;
1026219089Spjd	}
1027219089Spjd
1028219089Spjd	if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME,
1029219089Spjd	    FM_FMRI_SCHEME_CPU) != 0) {
1030219089Spjd		atomic_add_64(failedp, 1);
1031219089Spjd		return;
1032219089Spjd	}
1033219089Spjd
1034219089Spjd	if (auth != NULL && nvlist_add_nvlist(fmri_cpu, FM_FMRI_AUTHORITY,
1035219089Spjd	    (nvlist_t *)auth) != 0)
1036219089Spjd		atomic_add_64(failedp, 1);
1037219089Spjd
1038219089Spjd	if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0)
1039219089Spjd		atomic_add_64(failedp, 1);
1040219089Spjd
1041219089Spjd	if (cpu_maskp != NULL && nvlist_add_uint8(fmri_cpu, FM_FMRI_CPU_MASK,
1042219089Spjd	    *cpu_maskp) != 0)
1043219089Spjd		atomic_add_64(failedp, 1);
1044219089Spjd
1045219089Spjd	if (serial_idp == NULL || nvlist_add_string(fmri_cpu,
1046219089Spjd	    FM_FMRI_CPU_SERIAL_ID, (char *)serial_idp) != 0)
1047219089Spjd			atomic_add_64(failedp, 1);
1048219089Spjd}
1049219089Spjd
1050219089Spjd/*
1051219089Spjd * Set-up and validate the members of a mem according to:
1052219089Spjd *
1053219089Spjd *	Member name		Type		Value
1054219089Spjd *	====================================================
1055219089Spjd *	version			uint8_t		0
1056219089Spjd *	auth			nvlist_t	<auth>		[optional]
1057219089Spjd *	unum			string		<unum>
1058219089Spjd *	serial			string		<serial>	[optional*]
1059219089Spjd *	offset			uint64_t	<offset>	[optional]
1060219089Spjd *
1061219089Spjd *	* serial is required if offset is present
1062219089Spjd */
1063219089Spjdvoid
1064219089Spjdfm_fmri_mem_set(nvlist_t *fmri, int version, const nvlist_t *auth,
1065219089Spjd    const char *unum, const char *serial, uint64_t offset)
1066219089Spjd{
1067219089Spjd	if (version != MEM_SCHEME_VERSION0) {
1068219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1069219089Spjd		return;
1070219089Spjd	}
1071219089Spjd
1072219089Spjd	if (!serial && (offset != (uint64_t)-1)) {
1073219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1074219089Spjd		return;
1075219089Spjd	}
1076219089Spjd
1077219089Spjd	if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0) {
1078219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1079219089Spjd		return;
1080219089Spjd	}
1081219089Spjd
1082219089Spjd	if (nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0) {
1083219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1084219089Spjd		return;
1085219089Spjd	}
1086219089Spjd
1087219089Spjd	if (auth != NULL) {
1088219089Spjd		if (nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY,
1089219089Spjd		    (nvlist_t *)auth) != 0) {
1090219089Spjd			atomic_add_64(
1091219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1092219089Spjd		}
1093219089Spjd	}
1094219089Spjd
1095219089Spjd	if (nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum) != 0) {
1096219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1097219089Spjd	}
1098219089Spjd
1099219089Spjd	if (serial != NULL) {
1100219089Spjd		if (nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID,
1101219089Spjd		    (char **)&serial, 1) != 0) {
1102219089Spjd			atomic_add_64(
1103219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1104219089Spjd		}
1105219089Spjd		if (offset != (uint64_t)-1) {
1106219089Spjd			if (nvlist_add_uint64(fmri, FM_FMRI_MEM_OFFSET,
1107219089Spjd			    offset) != 0) {
1108219089Spjd				atomic_add_64(&erpt_kstat_data.
1109219089Spjd				    fmri_set_failed.value.ui64, 1);
1110219089Spjd			}
1111219089Spjd		}
1112219089Spjd	}
1113219089Spjd}
1114219089Spjd
1115219089Spjdvoid
1116219089Spjdfm_fmri_zfs_set(nvlist_t *fmri, int version, uint64_t pool_guid,
1117219089Spjd    uint64_t vdev_guid)
1118219089Spjd{
1119219089Spjd	if (version != ZFS_SCHEME_VERSION0) {
1120219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1121219089Spjd		return;
1122219089Spjd	}
1123219089Spjd
1124219089Spjd	if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0) {
1125219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1126219089Spjd		return;
1127219089Spjd	}
1128219089Spjd
1129219089Spjd	if (nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_ZFS) != 0) {
1130219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1131219089Spjd		return;
1132219089Spjd	}
1133219089Spjd
1134219089Spjd	if (nvlist_add_uint64(fmri, FM_FMRI_ZFS_POOL, pool_guid) != 0) {
1135219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1136219089Spjd	}
1137219089Spjd
1138219089Spjd	if (vdev_guid != 0) {
1139219089Spjd		if (nvlist_add_uint64(fmri, FM_FMRI_ZFS_VDEV, vdev_guid) != 0) {
1140219089Spjd			atomic_add_64(
1141219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1142219089Spjd		}
1143219089Spjd	}
1144219089Spjd}
1145219089Spjd
1146219089Spjduint64_t
1147219089Spjdfm_ena_increment(uint64_t ena)
1148219089Spjd{
1149219089Spjd	uint64_t new_ena;
1150219089Spjd
1151219089Spjd	switch (ENA_FORMAT(ena)) {
1152219089Spjd	case FM_ENA_FMT1:
1153219089Spjd		new_ena = ena + (1 << ENA_FMT1_GEN_SHFT);
1154219089Spjd		break;
1155219089Spjd	case FM_ENA_FMT2:
1156219089Spjd		new_ena = ena + (1 << ENA_FMT2_GEN_SHFT);
1157219089Spjd		break;
1158219089Spjd	default:
1159219089Spjd		new_ena = 0;
1160219089Spjd	}
1161219089Spjd
1162219089Spjd	return (new_ena);
1163219089Spjd}
1164219089Spjd
1165219089Spjduint64_t
1166219089Spjdfm_ena_generate_cpu(uint64_t timestamp, processorid_t cpuid, uchar_t format)
1167219089Spjd{
1168219089Spjd	uint64_t ena = 0;
1169219089Spjd
1170219089Spjd	switch (format) {
1171219089Spjd	case FM_ENA_FMT1:
1172219089Spjd		if (timestamp) {
1173219089Spjd			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1174219089Spjd			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
1175219089Spjd			    ENA_FMT1_CPUID_MASK) |
1176219089Spjd			    ((timestamp << ENA_FMT1_TIME_SHFT) &
1177219089Spjd			    ENA_FMT1_TIME_MASK));
1178219089Spjd		} else {
1179219089Spjd			ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1180219089Spjd			    ((cpuid << ENA_FMT1_CPUID_SHFT) &
1181219089Spjd			    ENA_FMT1_CPUID_MASK) |
1182219089Spjd			    ((gethrtime_waitfree() << ENA_FMT1_TIME_SHFT) &
1183219089Spjd			    ENA_FMT1_TIME_MASK));
1184219089Spjd		}
1185219089Spjd		break;
1186219089Spjd	case FM_ENA_FMT2:
1187219089Spjd		ena = (uint64_t)((format & ENA_FORMAT_MASK) |
1188219089Spjd		    ((timestamp << ENA_FMT2_TIME_SHFT) & ENA_FMT2_TIME_MASK));
1189219089Spjd		break;
1190219089Spjd	default:
1191219089Spjd		break;
1192219089Spjd	}
1193219089Spjd
1194219089Spjd	return (ena);
1195219089Spjd}
1196219089Spjd
1197219089Spjduint64_t
1198219089Spjdfm_ena_generate(uint64_t timestamp, uchar_t format)
1199219089Spjd{
1200219089Spjd	return (fm_ena_generate_cpu(timestamp, PCPU_GET(cpuid), format));
1201219089Spjd}
1202219089Spjd
1203219089Spjduint64_t
1204219089Spjdfm_ena_generation_get(uint64_t ena)
1205219089Spjd{
1206219089Spjd	uint64_t gen;
1207219089Spjd
1208219089Spjd	switch (ENA_FORMAT(ena)) {
1209219089Spjd	case FM_ENA_FMT1:
1210219089Spjd		gen = (ena & ENA_FMT1_GEN_MASK) >> ENA_FMT1_GEN_SHFT;
1211219089Spjd		break;
1212219089Spjd	case FM_ENA_FMT2:
1213219089Spjd		gen = (ena & ENA_FMT2_GEN_MASK) >> ENA_FMT2_GEN_SHFT;
1214219089Spjd		break;
1215219089Spjd	default:
1216219089Spjd		gen = 0;
1217219089Spjd		break;
1218219089Spjd	}
1219219089Spjd
1220219089Spjd	return (gen);
1221219089Spjd}
1222219089Spjd
1223219089Spjduchar_t
1224219089Spjdfm_ena_format_get(uint64_t ena)
1225219089Spjd{
1226219089Spjd
1227219089Spjd	return (ENA_FORMAT(ena));
1228219089Spjd}
1229219089Spjd
1230219089Spjduint64_t
1231219089Spjdfm_ena_id_get(uint64_t ena)
1232219089Spjd{
1233219089Spjd	uint64_t id;
1234219089Spjd
1235219089Spjd	switch (ENA_FORMAT(ena)) {
1236219089Spjd	case FM_ENA_FMT1:
1237219089Spjd		id = (ena & ENA_FMT1_ID_MASK) >> ENA_FMT1_ID_SHFT;
1238219089Spjd		break;
1239219089Spjd	case FM_ENA_FMT2:
1240219089Spjd		id = (ena & ENA_FMT2_ID_MASK) >> ENA_FMT2_ID_SHFT;
1241219089Spjd		break;
1242219089Spjd	default:
1243219089Spjd		id = 0;
1244219089Spjd	}
1245219089Spjd
1246219089Spjd	return (id);
1247219089Spjd}
1248219089Spjd
1249219089Spjduint64_t
1250219089Spjdfm_ena_time_get(uint64_t ena)
1251219089Spjd{
1252219089Spjd	uint64_t time;
1253219089Spjd
1254219089Spjd	switch (ENA_FORMAT(ena)) {
1255219089Spjd	case FM_ENA_FMT1:
1256219089Spjd		time = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT;
1257219089Spjd		break;
1258219089Spjd	case FM_ENA_FMT2:
1259219089Spjd		time = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT;
1260219089Spjd		break;
1261219089Spjd	default:
1262219089Spjd		time = 0;
1263219089Spjd	}
1264219089Spjd
1265219089Spjd	return (time);
1266219089Spjd}
1267219089Spjd
1268219089Spjd#ifdef sun
1269219089Spjd/*
1270219089Spjd * Convert a getpcstack() trace to symbolic name+offset, and add the resulting
1271219089Spjd * string array to a Fault Management ereport as FM_EREPORT_PAYLOAD_NAME_STACK.
1272219089Spjd */
1273219089Spjdvoid
1274219089Spjdfm_payload_stack_add(nvlist_t *payload, const pc_t *stack, int depth)
1275219089Spjd{
1276219089Spjd	int i;
1277219089Spjd	char *sym;
1278219089Spjd	ulong_t off;
1279219089Spjd	char *stkpp[FM_STK_DEPTH];
1280219089Spjd	char buf[FM_STK_DEPTH * FM_SYM_SZ];
1281219089Spjd	char *stkp = buf;
1282219089Spjd
1283219089Spjd	for (i = 0; i < depth && i != FM_STK_DEPTH; i++, stkp += FM_SYM_SZ) {
1284219089Spjd		if ((sym = kobj_getsymname(stack[i], &off)) != NULL)
1285219089Spjd			(void) snprintf(stkp, FM_SYM_SZ, "%s+%lx", sym, off);
1286219089Spjd		else
1287219089Spjd			(void) snprintf(stkp, FM_SYM_SZ, "%lx", (long)stack[i]);
1288219089Spjd		stkpp[i] = stkp;
1289219089Spjd	}
1290219089Spjd
1291219089Spjd	fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_STACK,
1292219089Spjd	    DATA_TYPE_STRING_ARRAY, depth, stkpp, NULL);
1293219089Spjd}
1294219089Spjd#endif
1295219089Spjd
1296219089Spjd#ifdef sun
1297219089Spjdvoid
1298219089Spjdprint_msg_hwerr(ctid_t ct_id, proc_t *p)
1299219089Spjd{
1300219089Spjd	uprintf("Killed process %d (%s) in contract id %d "
1301219089Spjd	    "due to hardware error\n", p->p_pid, p->p_user.u_comm, ct_id);
1302219089Spjd}
1303219089Spjd#endif
1304219089Spjd
1305219089Spjdvoid
1306219089Spjdfm_fmri_hc_create(nvlist_t *fmri, int version, const nvlist_t *auth,
1307219089Spjd    nvlist_t *snvl, nvlist_t *bboard, int npairs, ...)
1308219089Spjd{
1309219089Spjd	nv_alloc_t *nva = nvlist_lookup_nv_alloc(fmri);
1310219089Spjd	nvlist_t *pairs[HC_MAXPAIRS];
1311219089Spjd	nvlist_t **hcl;
1312219089Spjd	uint_t n;
1313219089Spjd	int i, j;
1314219089Spjd	va_list ap;
1315219089Spjd	char *hcname, *hcid;
1316219089Spjd
1317219089Spjd	if (!fm_fmri_hc_set_common(fmri, version, auth))
1318219089Spjd		return;
1319219089Spjd
1320219089Spjd	/*
1321219089Spjd	 * copy the bboard nvpairs to the pairs array
1322219089Spjd	 */
1323219089Spjd	if (nvlist_lookup_nvlist_array(bboard, FM_FMRI_HC_LIST, &hcl, &n)
1324219089Spjd	    != 0) {
1325219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1326219089Spjd		return;
1327219089Spjd	}
1328219089Spjd
1329219089Spjd	for (i = 0; i < n; i++) {
1330219089Spjd		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME,
1331219089Spjd		    &hcname) != 0) {
1332219089Spjd			atomic_add_64(
1333219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1334219089Spjd			return;
1335219089Spjd		}
1336219089Spjd		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0) {
1337219089Spjd			atomic_add_64(
1338219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1339219089Spjd			return;
1340219089Spjd		}
1341219089Spjd
1342219089Spjd		pairs[i] = fm_nvlist_create(nva);
1343219089Spjd		if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, hcname) != 0 ||
1344219089Spjd		    nvlist_add_string(pairs[i], FM_FMRI_HC_ID, hcid) != 0) {
1345219089Spjd			for (j = 0; j <= i; j++) {
1346219089Spjd				if (pairs[j] != NULL)
1347219089Spjd					fm_nvlist_destroy(pairs[j],
1348219089Spjd					    FM_NVA_RETAIN);
1349219089Spjd			}
1350219089Spjd			atomic_add_64(
1351219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1352219089Spjd			return;
1353219089Spjd		}
1354219089Spjd	}
1355219089Spjd
1356219089Spjd	/*
1357219089Spjd	 * create the pairs from passed in pairs
1358219089Spjd	 */
1359219089Spjd	npairs = MIN(npairs, HC_MAXPAIRS);
1360219089Spjd
1361219089Spjd	va_start(ap, npairs);
1362219089Spjd	for (i = n; i < npairs + n; i++) {
1363219089Spjd		const char *name = va_arg(ap, const char *);
1364219089Spjd		uint32_t id = va_arg(ap, uint32_t);
1365219089Spjd		char idstr[11];
1366219089Spjd		(void) snprintf(idstr, sizeof (idstr), "%u", id);
1367219089Spjd		pairs[i] = fm_nvlist_create(nva);
1368219089Spjd		if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, name) != 0 ||
1369219089Spjd		    nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr) != 0) {
1370219089Spjd			for (j = 0; j <= i; j++) {
1371219089Spjd				if (pairs[j] != NULL)
1372219089Spjd					fm_nvlist_destroy(pairs[j],
1373219089Spjd					    FM_NVA_RETAIN);
1374219089Spjd			}
1375219089Spjd			atomic_add_64(
1376219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1377219089Spjd			return;
1378219089Spjd		}
1379219089Spjd	}
1380219089Spjd	va_end(ap);
1381219089Spjd
1382219089Spjd	/*
1383219089Spjd	 * Create the fmri hc list
1384219089Spjd	 */
1385219089Spjd	if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, pairs,
1386219089Spjd	    npairs + n) != 0) {
1387219089Spjd		atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1388219089Spjd		return;
1389219089Spjd	}
1390219089Spjd
1391219089Spjd	for (i = 0; i < npairs + n; i++) {
1392219089Spjd			fm_nvlist_destroy(pairs[i], FM_NVA_RETAIN);
1393219089Spjd	}
1394219089Spjd
1395219089Spjd	if (snvl != NULL) {
1396219089Spjd		if (nvlist_add_nvlist(fmri, FM_FMRI_HC_SPECIFIC, snvl) != 0) {
1397219089Spjd			atomic_add_64(
1398219089Spjd			    &erpt_kstat_data.fmri_set_failed.value.ui64, 1);
1399219089Spjd			return;
1400219089Spjd		}
1401219089Spjd	}
1402219089Spjd}
1403