ddifm.c revision 7931:f10ab35b8729
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 */
139int default_dmacache_sz = DEFAULT_DMACACHE_SZ;
140int default_acccache_sz = DEFAULT_ACCCACHE_SZ;
141int ddi_system_fmcap = 0;
142
143static 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 */
155void
156ddi_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
213void
214i_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 */
321void
322fm_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. */
434err:	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 */
438out:	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 */
461void
462ddi_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
474void
475ndi_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 */
494void
495i_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 */
509void
510i_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
518boolean_t
519i_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 */
531void
532ddi_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 */
588void
589ddi_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 */
661void
662ddi_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 */
795void
796ddi_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 */
847int
848ddi_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 */
863void
864ddi_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
886void
887ddi_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
909void
910ddi_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
931void
932ddi_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
953void
954i_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
967void
968i_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
980ddi_fmcompare_t
981i_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
988ddi_fmcompare_t
989i_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