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