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