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