xref: /titanic_44/usr/src/uts/common/os/ddifm.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * Fault Management for Device Drivers
31*7c478bd9Sstevel@tonic-gate  *
32*7c478bd9Sstevel@tonic-gate  * Device drivers wishing to participate in fault management may do so by
33*7c478bd9Sstevel@tonic-gate  * first initializing their fault management state and capabilties via
34*7c478bd9Sstevel@tonic-gate  * ddi_fm_init(). If the system supports the requested FM capabilities,
35*7c478bd9Sstevel@tonic-gate  * the IO framework will intialize FM state and return a bit mask of the
36*7c478bd9Sstevel@tonic-gate  * requested capabilities.
37*7c478bd9Sstevel@tonic-gate  *
38*7c478bd9Sstevel@tonic-gate  * If the system does not support the requested FM capabilities,
39*7c478bd9Sstevel@tonic-gate  * the device driver must behave in accordance with the programming semantics
40*7c478bd9Sstevel@tonic-gate  * defined below for the capabilities returned from ddi_fm_init().
41*7c478bd9Sstevel@tonic-gate  * ddi_fm_init() must be called at attach(9E) time and ddi_fm_fini() must be
42*7c478bd9Sstevel@tonic-gate  * called from detach(9E) to perform FM clean-up.
43*7c478bd9Sstevel@tonic-gate  *
44*7c478bd9Sstevel@tonic-gate  * Driver Fault Management Capabilities
45*7c478bd9Sstevel@tonic-gate  *
46*7c478bd9Sstevel@tonic-gate  * DDI_FM_NOT_CAPABLE
47*7c478bd9Sstevel@tonic-gate  *
48*7c478bd9Sstevel@tonic-gate  *	This is the default fault management capability for drivers.  Drivers
49*7c478bd9Sstevel@tonic-gate  *	that implement no fault management capabilites or do not participate
50*7c478bd9Sstevel@tonic-gate  *	in fault management activities have their FM capability bitmask set
51*7c478bd9Sstevel@tonic-gate  *	to 0.
52*7c478bd9Sstevel@tonic-gate  *
53*7c478bd9Sstevel@tonic-gate  * DDI_FM_EREPORT_CAPABLE
54*7c478bd9Sstevel@tonic-gate  *
55*7c478bd9Sstevel@tonic-gate  *	When this capability bit is set, drivers are expected to generate error
56*7c478bd9Sstevel@tonic-gate  *	report events via ddi_ereport_post() for the associated faults
57*7c478bd9Sstevel@tonic-gate  *	that are diagnosed by the IO fault manager DE.  ddi_ereport_post()
58*7c478bd9Sstevel@tonic-gate  *	may be called in any context subject to the constraints specified
59*7c478bd9Sstevel@tonic-gate  *	by the interrupt iblock cookie	returned during initialization.
60*7c478bd9Sstevel@tonic-gate  *
61*7c478bd9Sstevel@tonic-gate  *	Error reports resulting from hardware component specific and common IO
62*7c478bd9Sstevel@tonic-gate  *	fault and driver defects must be accompanied by an Eversholt fault
63*7c478bd9Sstevel@tonic-gate  *	tree (.eft) by the Solaris fault manager (fmd(1M)) for
64*7c478bd9Sstevel@tonic-gate  *	diagnosis.
65*7c478bd9Sstevel@tonic-gate  *
66*7c478bd9Sstevel@tonic-gate  * DDI_FM_ERRCB_CAPABLE
67*7c478bd9Sstevel@tonic-gate  *
68*7c478bd9Sstevel@tonic-gate  *	Device drivers are expected to implement and register an error
69*7c478bd9Sstevel@tonic-gate  *	handler callback function.  ddi_fm_handler_register() and
70*7c478bd9Sstevel@tonic-gate  *	ddi_fm_handler_unregister() must be
71*7c478bd9Sstevel@tonic-gate  *	called in passive kernel context, typically during an attach(9E)
72*7c478bd9Sstevel@tonic-gate  *	or detach(9E) operation.  When called by the FM IO framework,
73*7c478bd9Sstevel@tonic-gate  *	the callback function should check for error conditions for the
74*7c478bd9Sstevel@tonic-gate  *	hardware and software under its control.  All detected errors
75*7c478bd9Sstevel@tonic-gate  *	should have ereport events generated for them.
76*7c478bd9Sstevel@tonic-gate  *
77*7c478bd9Sstevel@tonic-gate  *	Upon completion of the error handler callback, the driver should
78*7c478bd9Sstevel@tonic-gate  *	return one of the following values:
79*7c478bd9Sstevel@tonic-gate  *
80*7c478bd9Sstevel@tonic-gate  *	#define DDI_FM_OK - no error was detected
81*7c478bd9Sstevel@tonic-gate  *	#define DDI_FM_FATAL - a fatal error was detected
82*7c478bd9Sstevel@tonic-gate  *	#define DDI_FM_NONFATAL - a non-fatal error was detected
83*7c478bd9Sstevel@tonic-gate  *	#define DDI_FM_UNKNOWN - the error status is unknown
84*7c478bd9Sstevel@tonic-gate  *
85*7c478bd9Sstevel@tonic-gate  *	To insure single threaded access to error handling callbacks,
86*7c478bd9Sstevel@tonic-gate  *	the device driver may use i_ddi_fm_handler_enter() and
87*7c478bd9Sstevel@tonic-gate  *	i_ddi_fm_handler_exit() when entering and exiting the callback.
88*7c478bd9Sstevel@tonic-gate  *
89*7c478bd9Sstevel@tonic-gate  * DDI_FM_ACCCHK_CAPABLE/DDI_FM_DMACHK_CAPABLE
90*7c478bd9Sstevel@tonic-gate  *
91*7c478bd9Sstevel@tonic-gate  *	Device drivers are expected to set-up access and DMA handles
92*7c478bd9Sstevel@tonic-gate  *	with FM-specific attributes designed to allow nexus parent
93*7c478bd9Sstevel@tonic-gate  *	drivers to flag any errors seen during subsequent IO transactions.
94*7c478bd9Sstevel@tonic-gate  *	Drivers must set the devacc_attr_acc_flag member of their
95*7c478bd9Sstevel@tonic-gate  *	ddi_device_acc_attr_t structures to DDI_FLAGERR_ACC or DDI_CAUTIOUS_ACC.
96*7c478bd9Sstevel@tonic-gate  *	For DMA transactions, driver must set the dma_attr_flags of
97*7c478bd9Sstevel@tonic-gate  *	their ddi_dma_attr_t structures to DDI_DMA_FLAGERR.
98*7c478bd9Sstevel@tonic-gate  *
99*7c478bd9Sstevel@tonic-gate  *	Upon completion of an IO transaction, device drivers are expected
100*7c478bd9Sstevel@tonic-gate  *	to check the status of host-side hardware access and device-side
101*7c478bd9Sstevel@tonic-gate  *	dma completions by calling ddi_acc_err_check() or ddi_dma_err_check()
102*7c478bd9Sstevel@tonic-gate  *	respectively. If the handle is associated with an error detected by
103*7c478bd9Sstevel@tonic-gate  *	the nexus parent or FM IO framework, ddi_fm_error_t data (status, ena
104*7c478bd9Sstevel@tonic-gate  *	and error expectation) is returned.  If status of DDI_FM_NONFATAL or
105*7c478bd9Sstevel@tonic-gate  *	DDI_FM_FATAL is returned, the ena is valid and the expectation flag
106*7c478bd9Sstevel@tonic-gate  *	will be set to 1 if the error was unexpected (i.e. not the result
107*7c478bd9Sstevel@tonic-gate  *	of a peek or poke type operation).
108*7c478bd9Sstevel@tonic-gate  *
109*7c478bd9Sstevel@tonic-gate  *	ddi_acc_err_check() and ddi_dma_err_check() may be called in any
110*7c478bd9Sstevel@tonic-gate  *	context	subject to the constraints specified by the interrupt
111*7c478bd9Sstevel@tonic-gate  *	iblock cookie returned during initialization.
112*7c478bd9Sstevel@tonic-gate  *
113*7c478bd9Sstevel@tonic-gate  *	Device drivers should generate an access (DDI_FM_IO_ACC) or dma
114*7c478bd9Sstevel@tonic-gate  *	(DDI_FM_IO_DMA) data path error report if DDI_FM_NONFATAL or
115*7c478bd9Sstevel@tonic-gate  *	DDI_FM_FATAL is returned.
116*7c478bd9Sstevel@tonic-gate  *
117*7c478bd9Sstevel@tonic-gate  */
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
120*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
121*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
122*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
123*7c478bd9Sstevel@tonic-gate #include <sys/nvpair.h>
124*7c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h>
125*7c478bd9Sstevel@tonic-gate #include <sys/ndifm.h>
126*7c478bd9Sstevel@tonic-gate #include <sys/ddifm.h>
127*7c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
128*7c478bd9Sstevel@tonic-gate #include <sys/ddi_isa.h>
129*7c478bd9Sstevel@tonic-gate #include <sys/spl.h>
130*7c478bd9Sstevel@tonic-gate #include <sys/varargs.h>
131*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
132*7c478bd9Sstevel@tonic-gate #include <sys/disp.h>
133*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
134*7c478bd9Sstevel@tonic-gate #include <sys/errorq_impl.h>
135*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
136*7c478bd9Sstevel@tonic-gate #include <sys/fm/util.h>
137*7c478bd9Sstevel@tonic-gate #include <sys/fm/io/ddi.h>
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate #define	ERPT_CLASS_SZ	sizeof (DDI_IO_CLASS) + sizeof (FM_EREPORT_CLASS) + \
140*7c478bd9Sstevel@tonic-gate 			    DDI_MAX_ERPT_CLASS + 2
141*7c478bd9Sstevel@tonic-gate /* Globals */
142*7c478bd9Sstevel@tonic-gate int default_dmacache_sz = DEFAULT_DMACACHE_SZ;
143*7c478bd9Sstevel@tonic-gate int default_acccache_sz = DEFAULT_ACCCACHE_SZ;
144*7c478bd9Sstevel@tonic-gate int ddi_system_fmcap = 0;
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate static struct i_ddi_fmkstat ddifm_kstat_template = {
147*7c478bd9Sstevel@tonic-gate 	{"erpt_dropped", KSTAT_DATA_UINT64 },
148*7c478bd9Sstevel@tonic-gate 	{"fm_cache_full", KSTAT_DATA_UINT64 },
149*7c478bd9Sstevel@tonic-gate 	{"fm_cache_grew", KSTAT_DATA_UINT64 },
150*7c478bd9Sstevel@tonic-gate 	{"acc_err", KSTAT_DATA_UINT64 },
151*7c478bd9Sstevel@tonic-gate 	{"dma_err", KSTAT_DATA_UINT64 }
152*7c478bd9Sstevel@tonic-gate };
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate /*
155*7c478bd9Sstevel@tonic-gate  * Update the service state following the detection of an
156*7c478bd9Sstevel@tonic-gate  * error.
157*7c478bd9Sstevel@tonic-gate  */
158*7c478bd9Sstevel@tonic-gate void
159*7c478bd9Sstevel@tonic-gate ddi_fm_service_impact(dev_info_t *dip, int svc_impact)
160*7c478bd9Sstevel@tonic-gate {
161*7c478bd9Sstevel@tonic-gate 	mutex_enter(&(DEVI(dip)->devi_lock));
162*7c478bd9Sstevel@tonic-gate 	if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
163*7c478bd9Sstevel@tonic-gate 		switch (svc_impact) {
164*7c478bd9Sstevel@tonic-gate 		case DDI_SERVICE_LOST:
165*7c478bd9Sstevel@tonic-gate 			DEVI_SET_DEVICE_DOWN(dip);
166*7c478bd9Sstevel@tonic-gate 			break;
167*7c478bd9Sstevel@tonic-gate 		case DDI_SERVICE_DEGRADED:
168*7c478bd9Sstevel@tonic-gate 			DEVI_SET_DEVICE_DEGRADED(dip);
169*7c478bd9Sstevel@tonic-gate 			break;
170*7c478bd9Sstevel@tonic-gate 		case DDI_SERVICE_RESTORED:
171*7c478bd9Sstevel@tonic-gate 			DEVI_SET_DEVICE_UP(dip);
172*7c478bd9Sstevel@tonic-gate 			break;
173*7c478bd9Sstevel@tonic-gate 		case DDI_SERVICE_UNAFFECTED:
174*7c478bd9Sstevel@tonic-gate 		default:
175*7c478bd9Sstevel@tonic-gate 			break;
176*7c478bd9Sstevel@tonic-gate 		}
177*7c478bd9Sstevel@tonic-gate 	}
178*7c478bd9Sstevel@tonic-gate 	mutex_exit(&(DEVI(dip)->devi_lock));
179*7c478bd9Sstevel@tonic-gate }
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate static int
182*7c478bd9Sstevel@tonic-gate erpt_post_sleep(dev_info_t *dip, const char *error_class, uint64_t ena,
183*7c478bd9Sstevel@tonic-gate     uint8_t version, va_list ap)
184*7c478bd9Sstevel@tonic-gate {
185*7c478bd9Sstevel@tonic-gate 	char *devid, *name;
186*7c478bd9Sstevel@tonic-gate 	char device_path[MAXPATHLEN];
187*7c478bd9Sstevel@tonic-gate 	char ddi_error_class[ERPT_CLASS_SZ];
188*7c478bd9Sstevel@tonic-gate 	nvlist_t *ereport, *detector = NULL;
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	/*
191*7c478bd9Sstevel@tonic-gate 	 * Driver defect - should not call with DDI_SLEEP while
192*7c478bd9Sstevel@tonic-gate 	 * in interrupt context
193*7c478bd9Sstevel@tonic-gate 	 */
194*7c478bd9Sstevel@tonic-gate 	if (servicing_interrupt()) {
195*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
196*7c478bd9Sstevel@tonic-gate 		return (1);
197*7c478bd9Sstevel@tonic-gate 	}
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 	if ((ereport = fm_nvlist_create(NULL)) == NULL)
200*7c478bd9Sstevel@tonic-gate 		return (1);
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate 	/*
203*7c478bd9Sstevel@tonic-gate 	 * Use the dev_path/devid for this device instance.
204*7c478bd9Sstevel@tonic-gate 	 */
205*7c478bd9Sstevel@tonic-gate 	detector = fm_nvlist_create(NULL);
206*7c478bd9Sstevel@tonic-gate 	if (dip == ddi_root_node()) {
207*7c478bd9Sstevel@tonic-gate 		device_path[0] = '/';
208*7c478bd9Sstevel@tonic-gate 		device_path[1] = '\0';
209*7c478bd9Sstevel@tonic-gate 	} else {
210*7c478bd9Sstevel@tonic-gate 		(void) ddi_pathname(dip, device_path);
211*7c478bd9Sstevel@tonic-gate 	}
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
214*7c478bd9Sstevel@tonic-gate 		DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devid) == DDI_SUCCESS) {
215*7c478bd9Sstevel@tonic-gate 		fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
216*7c478bd9Sstevel@tonic-gate 		    device_path, devid);
217*7c478bd9Sstevel@tonic-gate 		ddi_prop_free(devid);
218*7c478bd9Sstevel@tonic-gate 	} else {
219*7c478bd9Sstevel@tonic-gate 		fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
220*7c478bd9Sstevel@tonic-gate 		    device_path, NULL);
221*7c478bd9Sstevel@tonic-gate 	}
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	if (ena == 0)
224*7c478bd9Sstevel@tonic-gate 		ena = fm_ena_generate(0, FM_ENA_FMT1);
225*7c478bd9Sstevel@tonic-gate 
226*7c478bd9Sstevel@tonic-gate 	(void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s",
227*7c478bd9Sstevel@tonic-gate 	    DDI_IO_CLASS, error_class);
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	fm_ereport_set(ereport, version, ddi_error_class,
230*7c478bd9Sstevel@tonic-gate 	    ena, detector, NULL);
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 	name = va_arg(ap, char *);
233*7c478bd9Sstevel@tonic-gate 	(void) i_fm_payload_set(ereport, name, ap);
234*7c478bd9Sstevel@tonic-gate 
235*7c478bd9Sstevel@tonic-gate 	fm_ereport_post(ereport, EVCH_SLEEP);
236*7c478bd9Sstevel@tonic-gate 	fm_nvlist_destroy(ereport, FM_NVA_FREE);
237*7c478bd9Sstevel@tonic-gate 	fm_nvlist_destroy(detector, FM_NVA_FREE);
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 	return (0);
240*7c478bd9Sstevel@tonic-gate }
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate static int
243*7c478bd9Sstevel@tonic-gate erpt_post_nosleep(dev_info_t *dip, struct i_ddi_fmhdl *fmhdl,
244*7c478bd9Sstevel@tonic-gate     const char *error_class, uint64_t ena, uint8_t version, va_list ap)
245*7c478bd9Sstevel@tonic-gate {
246*7c478bd9Sstevel@tonic-gate 	char *name;
247*7c478bd9Sstevel@tonic-gate 	char device_path[MAXPATHLEN];
248*7c478bd9Sstevel@tonic-gate 	char ddi_error_class[ERPT_CLASS_SZ];
249*7c478bd9Sstevel@tonic-gate 	nvlist_t *ereport, *detector;
250*7c478bd9Sstevel@tonic-gate 	nv_alloc_t *nva;
251*7c478bd9Sstevel@tonic-gate 	errorq_elem_t *eqep;
252*7c478bd9Sstevel@tonic-gate 
253*7c478bd9Sstevel@tonic-gate 	eqep = errorq_reserve(fmhdl->fh_errorq);
254*7c478bd9Sstevel@tonic-gate 	if (eqep == NULL)
255*7c478bd9Sstevel@tonic-gate 		return (1);
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 	ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep);
258*7c478bd9Sstevel@tonic-gate 	nva = errorq_elem_nva(fmhdl->fh_errorq, eqep);
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 	ASSERT(ereport);
261*7c478bd9Sstevel@tonic-gate 	ASSERT(nva);
262*7c478bd9Sstevel@tonic-gate 
263*7c478bd9Sstevel@tonic-gate 	/*
264*7c478bd9Sstevel@tonic-gate 	 * Use the dev_path/devid for this device instance.
265*7c478bd9Sstevel@tonic-gate 	 */
266*7c478bd9Sstevel@tonic-gate 	detector = fm_nvlist_create(nva);
267*7c478bd9Sstevel@tonic-gate 	if (dip == ddi_root_node()) {
268*7c478bd9Sstevel@tonic-gate 		device_path[0] = '/';
269*7c478bd9Sstevel@tonic-gate 		device_path[1] = '\0';
270*7c478bd9Sstevel@tonic-gate 	} else {
271*7c478bd9Sstevel@tonic-gate 		(void) ddi_pathname(dip, device_path);
272*7c478bd9Sstevel@tonic-gate 	}
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 	fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
275*7c478bd9Sstevel@tonic-gate 	    device_path, NULL);
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate 	if (ena == 0)
278*7c478bd9Sstevel@tonic-gate 		ena = fm_ena_generate(0, FM_ENA_FMT1);
279*7c478bd9Sstevel@tonic-gate 
280*7c478bd9Sstevel@tonic-gate 	(void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s",
281*7c478bd9Sstevel@tonic-gate 	    DDI_IO_CLASS, error_class);
282*7c478bd9Sstevel@tonic-gate 
283*7c478bd9Sstevel@tonic-gate 	fm_ereport_set(ereport, version, ddi_error_class,
284*7c478bd9Sstevel@tonic-gate 	    ena, detector, NULL);
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate 	name = va_arg(ap, char *);
287*7c478bd9Sstevel@tonic-gate 	(void) i_fm_payload_set(ereport, name, ap);
288*7c478bd9Sstevel@tonic-gate 
289*7c478bd9Sstevel@tonic-gate 	errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC);
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 	return (0);
292*7c478bd9Sstevel@tonic-gate }
293*7c478bd9Sstevel@tonic-gate 
294*7c478bd9Sstevel@tonic-gate void
295*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class,
296*7c478bd9Sstevel@tonic-gate     nvlist_t *errp, int sflag)
297*7c478bd9Sstevel@tonic-gate {
298*7c478bd9Sstevel@tonic-gate 	int i;
299*7c478bd9Sstevel@tonic-gate 	int depth;
300*7c478bd9Sstevel@tonic-gate 	char classp[DDI_DVR_MAX_CLASS];
301*7c478bd9Sstevel@tonic-gate 	caddr_t stkp;
302*7c478bd9Sstevel@tonic-gate 	char *buf;
303*7c478bd9Sstevel@tonic-gate 	char **stkpp;
304*7c478bd9Sstevel@tonic-gate 	char *sym;
305*7c478bd9Sstevel@tonic-gate 	pc_t stack[DDI_FM_STKDEPTH];
306*7c478bd9Sstevel@tonic-gate 	ulong_t off;
307*7c478bd9Sstevel@tonic-gate 	dev_info_t *root_dip = ddi_root_node();
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(root_dip)))
310*7c478bd9Sstevel@tonic-gate 		return;
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	(void) snprintf(classp, DDI_DVR_MAX_CLASS, "%s%s", DVR_ERPT,
313*7c478bd9Sstevel@tonic-gate 	    error_class);
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate 	if (sflag == DDI_SLEEP) {
316*7c478bd9Sstevel@tonic-gate 		depth = getpcstack(stack, DDI_FM_STKDEPTH);
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 		/* Allocate array of char * for nvlist payload */
319*7c478bd9Sstevel@tonic-gate 		stkpp = (char **)kmem_alloc(depth * sizeof (char *), KM_SLEEP);
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 		/*
322*7c478bd9Sstevel@tonic-gate 		 * Allocate temporary 64-bit aligned buffer for stack
323*7c478bd9Sstevel@tonic-gate 		 * symbol strings
324*7c478bd9Sstevel@tonic-gate 		 */
325*7c478bd9Sstevel@tonic-gate 		buf = kmem_alloc(depth * DDI_FM_SYM_SZ, KM_SLEEP);
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 		stkp = buf;
328*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < depth; ++i) {
329*7c478bd9Sstevel@tonic-gate 			sym = kobj_getsymname(stack[i], &off);
330*7c478bd9Sstevel@tonic-gate 			(void) snprintf(stkp, DDI_FM_SYM_SZ,
331*7c478bd9Sstevel@tonic-gate 			    "\t%s+%lx\n", sym ? sym : "?", off);
332*7c478bd9Sstevel@tonic-gate 			stkpp[i] = stkp;
333*7c478bd9Sstevel@tonic-gate 			stkp += DDI_FM_SYM_SZ;
334*7c478bd9Sstevel@tonic-gate 		}
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate 		if (errp)
337*7c478bd9Sstevel@tonic-gate 			ddi_fm_ereport_post(root_dip,
338*7c478bd9Sstevel@tonic-gate 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
339*7c478bd9Sstevel@tonic-gate 			    FM_VERSION, DATA_TYPE_UINT8, 0,
340*7c478bd9Sstevel@tonic-gate 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
341*7c478bd9Sstevel@tonic-gate 			    DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
342*7c478bd9Sstevel@tonic-gate 			    DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
343*7c478bd9Sstevel@tonic-gate 			    DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
344*7c478bd9Sstevel@tonic-gate 		else
345*7c478bd9Sstevel@tonic-gate 			ddi_fm_ereport_post(root_dip,
346*7c478bd9Sstevel@tonic-gate 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
347*7c478bd9Sstevel@tonic-gate 			    FM_VERSION, DATA_TYPE_UINT8, 0,
348*7c478bd9Sstevel@tonic-gate 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
349*7c478bd9Sstevel@tonic-gate 			    DVR_STACK_DEPTH, DATA_TYPE_UINT32, depth,
350*7c478bd9Sstevel@tonic-gate 			    DVR_STACK, DATA_TYPE_STRING_ARRAY, depth, stkpp,
351*7c478bd9Sstevel@tonic-gate 			    NULL);
352*7c478bd9Sstevel@tonic-gate 
353*7c478bd9Sstevel@tonic-gate 		kmem_free(stkpp, depth * sizeof (char *));
354*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, depth * DDI_FM_SYM_SZ);
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate 	} else {
357*7c478bd9Sstevel@tonic-gate 		if (errp)
358*7c478bd9Sstevel@tonic-gate 			ddi_fm_ereport_post(root_dip,
359*7c478bd9Sstevel@tonic-gate 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
360*7c478bd9Sstevel@tonic-gate 			    FM_VERSION, DATA_TYPE_UINT8, 0,
361*7c478bd9Sstevel@tonic-gate 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
362*7c478bd9Sstevel@tonic-gate 			    DVR_ERR_SPECIFIC, DATA_TYPE_NVLIST, errp, NULL);
363*7c478bd9Sstevel@tonic-gate 		else
364*7c478bd9Sstevel@tonic-gate 			ddi_fm_ereport_post(root_dip,
365*7c478bd9Sstevel@tonic-gate 			    classp, fm_ena_generate(0, FM_ENA_FMT1), sflag,
366*7c478bd9Sstevel@tonic-gate 			    FM_VERSION, DATA_TYPE_UINT8, 0,
367*7c478bd9Sstevel@tonic-gate 			    DVR_NAME, DATA_TYPE_STRING, ddi_driver_name(dip),
368*7c478bd9Sstevel@tonic-gate 			    NULL);
369*7c478bd9Sstevel@tonic-gate 	}
370*7c478bd9Sstevel@tonic-gate }
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate /*
373*7c478bd9Sstevel@tonic-gate  * Generate an error report for consumption by the Solaris Fault Manager,
374*7c478bd9Sstevel@tonic-gate  * fmd(1M).  Valid ereport classes are defined in /usr/include/sys/fm/io.  The
375*7c478bd9Sstevel@tonic-gate  * ENA should be set if this error is a result of an error status returned
376*7c478bd9Sstevel@tonic-gate  * from ddi_dma_err_check() or ddi_acc_err_check().  Otherwise, an ENA
377*7c478bd9Sstevel@tonic-gate  * value of 0 is appropriate.
378*7c478bd9Sstevel@tonic-gate  *
379*7c478bd9Sstevel@tonic-gate  * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called
380*7c478bd9Sstevel@tonic-gate  * from user, kernel, interrupt or high-interrupt context.  Otherwise,
381*7c478bd9Sstevel@tonic-gate  * ddi_fm_ereport_post() must be called from user or kernel context.
382*7c478bd9Sstevel@tonic-gate  */
383*7c478bd9Sstevel@tonic-gate void
384*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena,
385*7c478bd9Sstevel@tonic-gate     int sflag, ...)
386*7c478bd9Sstevel@tonic-gate {
387*7c478bd9Sstevel@tonic-gate 	int ret;
388*7c478bd9Sstevel@tonic-gate 	char *name;
389*7c478bd9Sstevel@tonic-gate 	data_type_t type;
390*7c478bd9Sstevel@tonic-gate 	uint8_t version;
391*7c478bd9Sstevel@tonic-gate 	va_list ap;
392*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) {
395*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, sflag);
396*7c478bd9Sstevel@tonic-gate 		return;
397*7c478bd9Sstevel@tonic-gate 	}
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 	ASSERT(fmhdl);
400*7c478bd9Sstevel@tonic-gate 
401*7c478bd9Sstevel@tonic-gate 	va_start(ap, sflag);
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate 	/* First payload tuple should be the version */
404*7c478bd9Sstevel@tonic-gate 	name = va_arg(ap, char *);
405*7c478bd9Sstevel@tonic-gate 	type = va_arg(ap, data_type_t);
406*7c478bd9Sstevel@tonic-gate 	version = va_arg(ap, uint_t);
407*7c478bd9Sstevel@tonic-gate 	if (strcmp(name, FM_VERSION) != 0 && type != DATA_TYPE_UINT8) {
408*7c478bd9Sstevel@tonic-gate 		va_end(ap);
409*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag);
410*7c478bd9Sstevel@tonic-gate 		return;
411*7c478bd9Sstevel@tonic-gate 	}
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate 	if (sflag == DDI_SLEEP)
414*7c478bd9Sstevel@tonic-gate 		ret = erpt_post_sleep(dip, error_class, ena, version, ap);
415*7c478bd9Sstevel@tonic-gate 	else
416*7c478bd9Sstevel@tonic-gate 		ret = erpt_post_nosleep(dip, fmhdl, error_class, ena, version,
417*7c478bd9Sstevel@tonic-gate 		    ap);
418*7c478bd9Sstevel@tonic-gate 	va_end(ap);
419*7c478bd9Sstevel@tonic-gate 
420*7c478bd9Sstevel@tonic-gate 	if (ret != 0)
421*7c478bd9Sstevel@tonic-gate 		atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1);
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate }
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate /*
426*7c478bd9Sstevel@tonic-gate  * Driver error handling entry.  Prevents multiple simultaneous calls into
427*7c478bd9Sstevel@tonic-gate  * driver error handling callback.
428*7c478bd9Sstevel@tonic-gate  *
429*7c478bd9Sstevel@tonic-gate  * May be called from a context consistent with the iblock_cookie returned
430*7c478bd9Sstevel@tonic-gate  * in ddi_fm_init().
431*7c478bd9Sstevel@tonic-gate  */
432*7c478bd9Sstevel@tonic-gate void
433*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_enter(dev_info_t *dip)
434*7c478bd9Sstevel@tonic-gate {
435*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 	mutex_enter(&hdl->fh_lock);
438*7c478bd9Sstevel@tonic-gate }
439*7c478bd9Sstevel@tonic-gate 
440*7c478bd9Sstevel@tonic-gate /*
441*7c478bd9Sstevel@tonic-gate  * Driver error handling exit.
442*7c478bd9Sstevel@tonic-gate  *
443*7c478bd9Sstevel@tonic-gate  * May be called from a context consistent with the iblock_cookie returned
444*7c478bd9Sstevel@tonic-gate  * in ddi_fm_init().
445*7c478bd9Sstevel@tonic-gate  */
446*7c478bd9Sstevel@tonic-gate void
447*7c478bd9Sstevel@tonic-gate i_ddi_fm_handler_exit(dev_info_t *dip)
448*7c478bd9Sstevel@tonic-gate {
449*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *hdl = DEVI(dip)->devi_fmhdl;
450*7c478bd9Sstevel@tonic-gate 
451*7c478bd9Sstevel@tonic-gate 	mutex_exit(&hdl->fh_lock);
452*7c478bd9Sstevel@tonic-gate }
453*7c478bd9Sstevel@tonic-gate 
454*7c478bd9Sstevel@tonic-gate /*
455*7c478bd9Sstevel@tonic-gate  * Register a fault manager error handler for this device instance
456*7c478bd9Sstevel@tonic-gate  *
457*7c478bd9Sstevel@tonic-gate  * This function must be called from a driver's attach(9E) routine.
458*7c478bd9Sstevel@tonic-gate  */
459*7c478bd9Sstevel@tonic-gate void
460*7c478bd9Sstevel@tonic-gate ddi_fm_handler_register(dev_info_t *dip, ddi_err_func_t handler,
461*7c478bd9Sstevel@tonic-gate     void *impl_data)
462*7c478bd9Sstevel@tonic-gate {
463*7c478bd9Sstevel@tonic-gate 	dev_info_t *pdip;
464*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *pfmhdl;
465*7c478bd9Sstevel@tonic-gate 	struct i_ddi_errhdl *new_eh;
466*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
467*7c478bd9Sstevel@tonic-gate 
468*7c478bd9Sstevel@tonic-gate 	/*
469*7c478bd9Sstevel@tonic-gate 	 * Check for proper calling context.
470*7c478bd9Sstevel@tonic-gate 	 * The DDI configuration framework does not support
471*7c478bd9Sstevel@tonic-gate 	 * DR states to allow checking for proper invocation
472*7c478bd9Sstevel@tonic-gate 	 * from a DDI_ATTACH or DDI_RESUME.  This limits context checking
473*7c478bd9Sstevel@tonic-gate 	 * to interrupt only.
474*7c478bd9Sstevel@tonic-gate 	 */
475*7c478bd9Sstevel@tonic-gate 	if (servicing_interrupt()) {
476*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
477*7c478bd9Sstevel@tonic-gate 		return;
478*7c478bd9Sstevel@tonic-gate 	}
479*7c478bd9Sstevel@tonic-gate 
480*7c478bd9Sstevel@tonic-gate 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
481*7c478bd9Sstevel@tonic-gate 
482*7c478bd9Sstevel@tonic-gate 	ASSERT(pdip);
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 	if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
485*7c478bd9Sstevel@tonic-gate 	    DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
486*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
487*7c478bd9Sstevel@tonic-gate 		return;
488*7c478bd9Sstevel@tonic-gate 	}
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 	new_eh = kmem_zalloc(sizeof (struct i_ddi_errhdl), KM_SLEEP);
491*7c478bd9Sstevel@tonic-gate 	new_eh->eh_func = handler;
492*7c478bd9Sstevel@tonic-gate 	new_eh->eh_impl = impl_data;
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 	/* Add dip to parent's target list of registered error handlers */
495*7c478bd9Sstevel@tonic-gate 	tgt = kmem_alloc(sizeof (struct i_ddi_fmtgt), KM_SLEEP);
496*7c478bd9Sstevel@tonic-gate 	tgt->ft_dip = dip;
497*7c478bd9Sstevel@tonic-gate 	tgt->ft_errhdl = new_eh;
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_enter(pdip);
500*7c478bd9Sstevel@tonic-gate 	pfmhdl = DEVI(pdip)->devi_fmhdl;
501*7c478bd9Sstevel@tonic-gate 	ASSERT(pfmhdl);
502*7c478bd9Sstevel@tonic-gate 	tgt->ft_next = pfmhdl->fh_tgts;
503*7c478bd9Sstevel@tonic-gate 	pfmhdl->fh_tgts = tgt;
504*7c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_exit(pdip);
505*7c478bd9Sstevel@tonic-gate }
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate /*
508*7c478bd9Sstevel@tonic-gate  * Unregister a fault manager error handler for this device instance
509*7c478bd9Sstevel@tonic-gate  *
510*7c478bd9Sstevel@tonic-gate  * This function must be called from a drivers attach(9E) or detach(9E)
511*7c478bd9Sstevel@tonic-gate  * routine.
512*7c478bd9Sstevel@tonic-gate  */
513*7c478bd9Sstevel@tonic-gate void
514*7c478bd9Sstevel@tonic-gate ddi_fm_handler_unregister(dev_info_t *dip)
515*7c478bd9Sstevel@tonic-gate {
516*7c478bd9Sstevel@tonic-gate 	dev_info_t *pdip;
517*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *pfmhdl;
518*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt, **ptgt;
519*7c478bd9Sstevel@tonic-gate 
520*7c478bd9Sstevel@tonic-gate 	/*
521*7c478bd9Sstevel@tonic-gate 	 * Check for proper calling context.
522*7c478bd9Sstevel@tonic-gate 	 * The DDI configuration framework does not support
523*7c478bd9Sstevel@tonic-gate 	 * DR states to allow checking for proper invocation
524*7c478bd9Sstevel@tonic-gate 	 * from a DDI_DETACH or DDI_SUSPEND.  This limits context checking
525*7c478bd9Sstevel@tonic-gate 	 * to interrupt only.
526*7c478bd9Sstevel@tonic-gate 	 */
527*7c478bd9Sstevel@tonic-gate 	if (servicing_interrupt()) {
528*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
529*7c478bd9Sstevel@tonic-gate 		return;
530*7c478bd9Sstevel@tonic-gate 	}
531*7c478bd9Sstevel@tonic-gate 
532*7c478bd9Sstevel@tonic-gate 	pdip = (dev_info_t *)DEVI(dip)->devi_parent;
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate 	ASSERT(pdip);
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 	if (!(DDI_FM_ERRCB_CAP(ddi_fm_capable(dip)) &&
537*7c478bd9Sstevel@tonic-gate 	    DDI_FM_ERRCB_CAP(ddi_fm_capable(pdip)))) {
538*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
539*7c478bd9Sstevel@tonic-gate 		return;
540*7c478bd9Sstevel@tonic-gate 	}
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_enter(pdip);
543*7c478bd9Sstevel@tonic-gate 	pfmhdl = DEVI(pdip)->devi_fmhdl;
544*7c478bd9Sstevel@tonic-gate 	ASSERT(pfmhdl);
545*7c478bd9Sstevel@tonic-gate 	ptgt = &pfmhdl->fh_tgts;
546*7c478bd9Sstevel@tonic-gate 	for (tgt = pfmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
547*7c478bd9Sstevel@tonic-gate 		if (dip == tgt->ft_dip) {
548*7c478bd9Sstevel@tonic-gate 			*ptgt = tgt->ft_next;
549*7c478bd9Sstevel@tonic-gate 			kmem_free(tgt->ft_errhdl, sizeof (struct i_ddi_errhdl));
550*7c478bd9Sstevel@tonic-gate 			kmem_free(tgt, sizeof (struct i_ddi_fmtgt));
551*7c478bd9Sstevel@tonic-gate 			break;
552*7c478bd9Sstevel@tonic-gate 		}
553*7c478bd9Sstevel@tonic-gate 		ptgt = &tgt->ft_next;
554*7c478bd9Sstevel@tonic-gate 	}
555*7c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_exit(pdip);
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate }
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate /*
561*7c478bd9Sstevel@tonic-gate  * Initialize Fault Management capabilities for this device instance (dip).
562*7c478bd9Sstevel@tonic-gate  * When called with the following capabilities, data structures neccessary
563*7c478bd9Sstevel@tonic-gate  * for fault management activities are allocated and initialized.
564*7c478bd9Sstevel@tonic-gate  *
565*7c478bd9Sstevel@tonic-gate  *	DDI_FM_EREPORT_CAPABLE - initialize ereport errorq and ereport
566*7c478bd9Sstevel@tonic-gate  *				capable driver property.
567*7c478bd9Sstevel@tonic-gate  *
568*7c478bd9Sstevel@tonic-gate  *	DDI_FM_ERRCB_CAPABLE - check with parent for ability to register
569*7c478bd9Sstevel@tonic-gate  *				an error handler.
570*7c478bd9Sstevel@tonic-gate  *
571*7c478bd9Sstevel@tonic-gate  *	DDI_FM_ACCCHK_CAPABLE - initialize access handle cache and acc-chk
572*7c478bd9Sstevel@tonic-gate  *				driver property
573*7c478bd9Sstevel@tonic-gate  *
574*7c478bd9Sstevel@tonic-gate  *	DDI_FM_DMACHK_CAPABLE - initialize dma handle cache and dma-chk
575*7c478bd9Sstevel@tonic-gate  *				driver property
576*7c478bd9Sstevel@tonic-gate  *
577*7c478bd9Sstevel@tonic-gate  * A driver's FM capability level may not exceed that of its parent or
578*7c478bd9Sstevel@tonic-gate  * system-wide FM capability.  The available capability level for this
579*7c478bd9Sstevel@tonic-gate  * device instance is returned in *fmcap.
580*7c478bd9Sstevel@tonic-gate  *
581*7c478bd9Sstevel@tonic-gate  * This function must be called from a driver's attach(9E) entry point.
582*7c478bd9Sstevel@tonic-gate  */
583*7c478bd9Sstevel@tonic-gate void
584*7c478bd9Sstevel@tonic-gate ddi_fm_init(dev_info_t *dip, int *fmcap, ddi_iblock_cookie_t *ibc)
585*7c478bd9Sstevel@tonic-gate {
586*7c478bd9Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
587*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
588*7c478bd9Sstevel@tonic-gate 	int pcap, newcap = DDI_FM_NOT_CAPABLE;
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate 	if (!DEVI_IS_ATTACHING(dip)) {
591*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
592*7c478bd9Sstevel@tonic-gate 		*fmcap = DDI_FM_NOT_CAPABLE;
593*7c478bd9Sstevel@tonic-gate 		return;
594*7c478bd9Sstevel@tonic-gate 	}
595*7c478bd9Sstevel@tonic-gate 
596*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_DEFAULT_CAP(*fmcap))
597*7c478bd9Sstevel@tonic-gate 		return;
598*7c478bd9Sstevel@tonic-gate 
599*7c478bd9Sstevel@tonic-gate 	/*
600*7c478bd9Sstevel@tonic-gate 	 * Check parent for supported FM level
601*7c478bd9Sstevel@tonic-gate 	 * and correct error handling PIL
602*7c478bd9Sstevel@tonic-gate 	 */
603*7c478bd9Sstevel@tonic-gate 	if (dip != ddi_root_node()) {
604*7c478bd9Sstevel@tonic-gate 
605*7c478bd9Sstevel@tonic-gate 		/*
606*7c478bd9Sstevel@tonic-gate 		 * Initialize the default ibc.  The parent may change it
607*7c478bd9Sstevel@tonic-gate 		 * depending upon its capabilities.
608*7c478bd9Sstevel@tonic-gate 		 */
609*7c478bd9Sstevel@tonic-gate 		*ibc = (ddi_iblock_cookie_t)ipltospl(FM_ERR_PIL);
610*7c478bd9Sstevel@tonic-gate 
611*7c478bd9Sstevel@tonic-gate 		pcap = i_ndi_busop_fm_init(dip, *fmcap, ibc);
612*7c478bd9Sstevel@tonic-gate 	} else {
613*7c478bd9Sstevel@tonic-gate 		pcap = *fmcap;
614*7c478bd9Sstevel@tonic-gate 	}
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate 	/* Initialize the per-device instance FM handle */
617*7c478bd9Sstevel@tonic-gate 	fmhdl = kmem_zalloc(sizeof (struct i_ddi_fmhdl), KM_SLEEP);
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate 	if ((fmhdl->fh_ksp = kstat_create((char *)ddi_driver_name(dip),
620*7c478bd9Sstevel@tonic-gate 	    ddi_get_instance(dip), "fm", "misc",
621*7c478bd9Sstevel@tonic-gate 	    KSTAT_TYPE_NAMED, sizeof (struct i_ddi_fmkstat) /
622*7c478bd9Sstevel@tonic-gate 	    sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) == NULL) {
623*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&fmhdl->fh_lock);
624*7c478bd9Sstevel@tonic-gate 		kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
625*7c478bd9Sstevel@tonic-gate 		*fmcap = DDI_FM_NOT_CAPABLE;
626*7c478bd9Sstevel@tonic-gate 		return;
627*7c478bd9Sstevel@tonic-gate 	}
628*7c478bd9Sstevel@tonic-gate 
629*7c478bd9Sstevel@tonic-gate 	bcopy(&ddifm_kstat_template, &fmhdl->fh_kstat,
630*7c478bd9Sstevel@tonic-gate 	    sizeof (struct i_ddi_fmkstat));
631*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_ksp->ks_data = &fmhdl->fh_kstat;
632*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_ksp->ks_private = fmhdl;
633*7c478bd9Sstevel@tonic-gate 	kstat_install(fmhdl->fh_ksp);
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_dma_cache = NULL;
636*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_acc_cache = NULL;
637*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_tgts = NULL;
638*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_dip = dip;
639*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_ibc = *ibc;
640*7c478bd9Sstevel@tonic-gate 	mutex_init(&fmhdl->fh_lock, NULL, MUTEX_DRIVER, fmhdl->fh_ibc);
641*7c478bd9Sstevel@tonic-gate 	devi->devi_fmhdl = fmhdl;
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate 	/*
644*7c478bd9Sstevel@tonic-gate 	 * Initialize support for ereport generation
645*7c478bd9Sstevel@tonic-gate 	 */
646*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_EREPORT_CAP(*fmcap) &&
647*7c478bd9Sstevel@tonic-gate 	    DDI_FM_EREPORT_CAP(ddi_system_fmcap)) {
648*7c478bd9Sstevel@tonic-gate 		fmhdl->fh_errorq = ereport_errorq;
649*7c478bd9Sstevel@tonic-gate 		if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
650*7c478bd9Sstevel@tonic-gate 		    "fm-ereport-capable", 0) == 0)
651*7c478bd9Sstevel@tonic-gate 			(void) ddi_prop_create(DDI_DEV_T_ANY, dip,
652*7c478bd9Sstevel@tonic-gate 			    DDI_PROP_CANSLEEP, "fm-ereport-capable", NULL, 0);
653*7c478bd9Sstevel@tonic-gate 
654*7c478bd9Sstevel@tonic-gate 		newcap |= DDI_FM_EREPORT_CAPABLE;
655*7c478bd9Sstevel@tonic-gate 	}
656*7c478bd9Sstevel@tonic-gate 
657*7c478bd9Sstevel@tonic-gate 	/*
658*7c478bd9Sstevel@tonic-gate 	 * Need cooperation of the parent for error handling
659*7c478bd9Sstevel@tonic-gate 	 */
660*7c478bd9Sstevel@tonic-gate 
661*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_ERRCB_CAP(*fmcap) && DDI_FM_ERRCB_CAP(pcap)) {
662*7c478bd9Sstevel@tonic-gate 		if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
663*7c478bd9Sstevel@tonic-gate 		    "fm-errcb-capable", 0) == 0)
664*7c478bd9Sstevel@tonic-gate 			(void) ddi_prop_create(DDI_DEV_T_ANY, dip,
665*7c478bd9Sstevel@tonic-gate 			    DDI_PROP_CANSLEEP, "fm-errcb-capable", NULL, 0);
666*7c478bd9Sstevel@tonic-gate 
667*7c478bd9Sstevel@tonic-gate 		newcap |= DDI_FM_ERRCB_CAPABLE;
668*7c478bd9Sstevel@tonic-gate 	}
669*7c478bd9Sstevel@tonic-gate 
670*7c478bd9Sstevel@tonic-gate 	/*
671*7c478bd9Sstevel@tonic-gate 	 * Support for DMA and Access error handling
672*7c478bd9Sstevel@tonic-gate 	 */
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_DMA_ERR_CAP(*fmcap) && DDI_FM_DMA_ERR_CAP(pcap)) {
675*7c478bd9Sstevel@tonic-gate 		i_ndi_fmc_create(&fmhdl->fh_dma_cache, 2, *ibc);
676*7c478bd9Sstevel@tonic-gate 
677*7c478bd9Sstevel@tonic-gate 		/* Set-up dma chk capability prop */
678*7c478bd9Sstevel@tonic-gate 		if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
679*7c478bd9Sstevel@tonic-gate 		    "fm-dmachk-capable", 0) == 0)
680*7c478bd9Sstevel@tonic-gate 			(void) ddi_prop_create(DDI_DEV_T_ANY, dip,
681*7c478bd9Sstevel@tonic-gate 			    DDI_PROP_CANSLEEP, "fm-dmachk-capable", NULL, 0);
682*7c478bd9Sstevel@tonic-gate 
683*7c478bd9Sstevel@tonic-gate 		newcap |= DDI_FM_DMACHK_CAPABLE;
684*7c478bd9Sstevel@tonic-gate 	}
685*7c478bd9Sstevel@tonic-gate 
686*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_ACC_ERR_CAP(*fmcap) && DDI_FM_ACC_ERR_CAP(pcap)) {
687*7c478bd9Sstevel@tonic-gate 		i_ndi_fmc_create(&fmhdl->fh_acc_cache, 2, *ibc);
688*7c478bd9Sstevel@tonic-gate 		/* Set-up dma chk capability prop */
689*7c478bd9Sstevel@tonic-gate 		if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
690*7c478bd9Sstevel@tonic-gate 		    "fm-accchk-capable", 0) == 0)
691*7c478bd9Sstevel@tonic-gate 			(void) ddi_prop_create(DDI_DEV_T_ANY, dip,
692*7c478bd9Sstevel@tonic-gate 			    DDI_PROP_CANSLEEP, "fm-accchk-capable", NULL, 0);
693*7c478bd9Sstevel@tonic-gate 
694*7c478bd9Sstevel@tonic-gate 		newcap |= DDI_FM_ACCCHK_CAPABLE;
695*7c478bd9Sstevel@tonic-gate 	}
696*7c478bd9Sstevel@tonic-gate 
697*7c478bd9Sstevel@tonic-gate 	/*
698*7c478bd9Sstevel@tonic-gate 	 * Return the capability support available
699*7c478bd9Sstevel@tonic-gate 	 * to this driver instance
700*7c478bd9Sstevel@tonic-gate 	 */
701*7c478bd9Sstevel@tonic-gate 	fmhdl->fh_cap = newcap;
702*7c478bd9Sstevel@tonic-gate 	*fmcap = newcap;
703*7c478bd9Sstevel@tonic-gate }
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate /*
706*7c478bd9Sstevel@tonic-gate  * Finalize Fault Management activities for this device instance.
707*7c478bd9Sstevel@tonic-gate  * Outstanding IO transaction must be completed prior to calling
708*7c478bd9Sstevel@tonic-gate  * this routine.  All previously allocated resources and error handler
709*7c478bd9Sstevel@tonic-gate  * registration are cleared and deallocated.
710*7c478bd9Sstevel@tonic-gate  *
711*7c478bd9Sstevel@tonic-gate  * This function must be called from a driver's detach(9E) entry point.
712*7c478bd9Sstevel@tonic-gate  */
713*7c478bd9Sstevel@tonic-gate void
714*7c478bd9Sstevel@tonic-gate ddi_fm_fini(dev_info_t *dip)
715*7c478bd9Sstevel@tonic-gate {
716*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
717*7c478bd9Sstevel@tonic-gate 
718*7c478bd9Sstevel@tonic-gate 	if (!(DEVI_IS_DETACHING(dip) || DEVI_IS_ATTACHING(dip))) {
719*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP);
720*7c478bd9Sstevel@tonic-gate 		return;
721*7c478bd9Sstevel@tonic-gate 	}
722*7c478bd9Sstevel@tonic-gate 
723*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_DEFAULT_CAP(fmhdl->fh_cap))
724*7c478bd9Sstevel@tonic-gate 		return;
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate 	ASSERT(fmhdl);
727*7c478bd9Sstevel@tonic-gate 
728*7c478bd9Sstevel@tonic-gate 	kstat_delete(fmhdl->fh_ksp);
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate 	if (DDI_FM_EREPORT_CAP(fmhdl->fh_cap)) {
731*7c478bd9Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_ANY, dip,
732*7c478bd9Sstevel@tonic-gate 		    "fm-ereport-capable");
733*7c478bd9Sstevel@tonic-gate 	}
734*7c478bd9Sstevel@tonic-gate 
735*7c478bd9Sstevel@tonic-gate 	if (dip != ddi_root_node()) {
736*7c478bd9Sstevel@tonic-gate 		ddi_fm_handler_unregister(dip);
737*7c478bd9Sstevel@tonic-gate 
738*7c478bd9Sstevel@tonic-gate 		if (DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap) ||
739*7c478bd9Sstevel@tonic-gate 		    DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
740*7c478bd9Sstevel@tonic-gate 			if (fmhdl->fh_dma_cache != NULL) {
741*7c478bd9Sstevel@tonic-gate 				i_ndi_fmc_destroy(fmhdl->fh_dma_cache);
742*7c478bd9Sstevel@tonic-gate 				(void) ddi_prop_remove(DDI_DEV_T_ANY, dip,
743*7c478bd9Sstevel@tonic-gate 				    "fm-dmachk-capable");
744*7c478bd9Sstevel@tonic-gate 			}
745*7c478bd9Sstevel@tonic-gate 			if (fmhdl->fh_acc_cache != NULL) {
746*7c478bd9Sstevel@tonic-gate 				i_ndi_fmc_destroy(fmhdl->fh_acc_cache);
747*7c478bd9Sstevel@tonic-gate 				(void) ddi_prop_remove(DDI_DEV_T_ANY, dip,
748*7c478bd9Sstevel@tonic-gate 				    "fm-accachk-capable");
749*7c478bd9Sstevel@tonic-gate 			}
750*7c478bd9Sstevel@tonic-gate 		}
751*7c478bd9Sstevel@tonic-gate 
752*7c478bd9Sstevel@tonic-gate 		i_ndi_busop_fm_fini(dip);
753*7c478bd9Sstevel@tonic-gate 	}
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate 	kmem_free(fmhdl, sizeof (struct i_ddi_fmhdl));
756*7c478bd9Sstevel@tonic-gate 	DEVI(dip)->devi_fmhdl = NULL;
757*7c478bd9Sstevel@tonic-gate }
758*7c478bd9Sstevel@tonic-gate 
759*7c478bd9Sstevel@tonic-gate /*
760*7c478bd9Sstevel@tonic-gate  * Return the fault management capability level for this device instance.
761*7c478bd9Sstevel@tonic-gate  *
762*7c478bd9Sstevel@tonic-gate  * This function may be called from user, kernel, or interrupt context.
763*7c478bd9Sstevel@tonic-gate  */
764*7c478bd9Sstevel@tonic-gate int
765*7c478bd9Sstevel@tonic-gate ddi_fm_capable(dev_info_t *dip)
766*7c478bd9Sstevel@tonic-gate {
767*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
768*7c478bd9Sstevel@tonic-gate 
769*7c478bd9Sstevel@tonic-gate 	if (fmhdl == NULL)
770*7c478bd9Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
771*7c478bd9Sstevel@tonic-gate 
772*7c478bd9Sstevel@tonic-gate 	return (fmhdl->fh_cap);
773*7c478bd9Sstevel@tonic-gate }
774*7c478bd9Sstevel@tonic-gate 
775*7c478bd9Sstevel@tonic-gate /*
776*7c478bd9Sstevel@tonic-gate  * Routines to set and get error information for/from an access or dma handle
777*7c478bd9Sstevel@tonic-gate  *
778*7c478bd9Sstevel@tonic-gate  * These routines may be called from user, kernel, and interrupt contexts.
779*7c478bd9Sstevel@tonic-gate  */
780*7c478bd9Sstevel@tonic-gate void
781*7c478bd9Sstevel@tonic-gate ddi_fm_acc_err_get(ddi_acc_handle_t handle, ddi_fm_error_t *de, int version)
782*7c478bd9Sstevel@tonic-gate {
783*7c478bd9Sstevel@tonic-gate 	ndi_err_t *errp = ((ddi_acc_impl_t *)handle)->ahi_err;
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate 	if (version != DDI_FME_VER0) {
786*7c478bd9Sstevel@tonic-gate 		ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
787*7c478bd9Sstevel@tonic-gate 
788*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(hp->ah_dip, DVR_EVER, NULL, DDI_NOSLEEP);
789*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_PANIC, "ddi_fm_dma_err_get: "
790*7c478bd9Sstevel@tonic-gate 		    "Invalid driver version\n");
791*7c478bd9Sstevel@tonic-gate 	}
792*7c478bd9Sstevel@tonic-gate 
793*7c478bd9Sstevel@tonic-gate 	de->fme_status = errp->err_status;
794*7c478bd9Sstevel@tonic-gate 	de->fme_ena = errp->err_ena;
795*7c478bd9Sstevel@tonic-gate 	de->fme_flag = errp->err_expected;
796*7c478bd9Sstevel@tonic-gate 	de->fme_acc_handle = handle;
797*7c478bd9Sstevel@tonic-gate }
798*7c478bd9Sstevel@tonic-gate 
799*7c478bd9Sstevel@tonic-gate void
800*7c478bd9Sstevel@tonic-gate ddi_fm_dma_err_get(ddi_dma_handle_t handle, ddi_fm_error_t *de, int version)
801*7c478bd9Sstevel@tonic-gate {
802*7c478bd9Sstevel@tonic-gate 	ndi_err_t *errp = &((ddi_dma_impl_t *)handle)->dmai_error;
803*7c478bd9Sstevel@tonic-gate 
804*7c478bd9Sstevel@tonic-gate 	if (version != DDI_FME_VER0) {
805*7c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(((ddi_dma_impl_t *)handle)->dmai_rdip,
806*7c478bd9Sstevel@tonic-gate 		    DVR_EVER, NULL, DDI_NOSLEEP);
807*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_PANIC, "ddi_fm_dma_err_get: "
808*7c478bd9Sstevel@tonic-gate 		    "Invalid driver version\n");
809*7c478bd9Sstevel@tonic-gate 	}
810*7c478bd9Sstevel@tonic-gate 
811*7c478bd9Sstevel@tonic-gate 	de->fme_status = errp->err_status;
812*7c478bd9Sstevel@tonic-gate 	de->fme_ena = errp->err_ena;
813*7c478bd9Sstevel@tonic-gate 	de->fme_flag = errp->err_expected;
814*7c478bd9Sstevel@tonic-gate 	de->fme_dma_handle = handle;
815*7c478bd9Sstevel@tonic-gate }
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate void
818*7c478bd9Sstevel@tonic-gate i_ddi_fm_acc_err_set(ddi_acc_handle_t handle, uint64_t ena, int status,
819*7c478bd9Sstevel@tonic-gate     int flag)
820*7c478bd9Sstevel@tonic-gate {
821*7c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t *hdlp = impl_acc_hdl_get(handle);
822*7c478bd9Sstevel@tonic-gate 	ddi_acc_impl_t *i_hdlp = (ddi_acc_impl_t *)handle;
823*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->ah_dip)->devi_fmhdl;
824*7c478bd9Sstevel@tonic-gate 
825*7c478bd9Sstevel@tonic-gate 	i_hdlp->ahi_err->err_ena = ena;
826*7c478bd9Sstevel@tonic-gate 	i_hdlp->ahi_err->err_status = status;
827*7c478bd9Sstevel@tonic-gate 	i_hdlp->ahi_err->err_expected = flag;
828*7c478bd9Sstevel@tonic-gate 	atomic_add_64(&fmhdl->fh_kstat.fek_acc_err.value.ui64, 1);
829*7c478bd9Sstevel@tonic-gate }
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate void
832*7c478bd9Sstevel@tonic-gate i_ddi_fm_dma_err_set(ddi_dma_handle_t handle, uint64_t ena, int status,
833*7c478bd9Sstevel@tonic-gate     int flag)
834*7c478bd9Sstevel@tonic-gate {
835*7c478bd9Sstevel@tonic-gate 	ddi_dma_impl_t *hdlp = (ddi_dma_impl_t *)handle;
836*7c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = DEVI(hdlp->dmai_rdip)->devi_fmhdl;
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 	hdlp->dmai_error.err_ena = ena;
839*7c478bd9Sstevel@tonic-gate 	hdlp->dmai_error.err_status = status;
840*7c478bd9Sstevel@tonic-gate 	hdlp->dmai_error.err_expected = flag;
841*7c478bd9Sstevel@tonic-gate 	atomic_add_64(&fmhdl->fh_kstat.fek_dma_err.value.ui64, 1);
842*7c478bd9Sstevel@tonic-gate }
843