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