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