xref: /onnv-gate/usr/src/uts/common/os/ndifm.c (revision 3752:52d4ba85bf6c)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51865Sdilpreet  * Common Development and Distribution License (the "License").
61865Sdilpreet  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3752Scindi  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Fault Management for Nexus Device Drivers
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * In addition to implementing and supporting Fault Management for Device
320Sstevel@tonic-gate  * Drivers (ddifm.c), nexus drivers must support their children by
330Sstevel@tonic-gate  * reporting FM capabilities, intializing interrupt block cookies
340Sstevel@tonic-gate  * for error handling callbacks and caching mapped resources for lookup
350Sstevel@tonic-gate  * during the detection of an IO transaction error.
360Sstevel@tonic-gate  *
370Sstevel@tonic-gate  * It is typically the nexus driver that receives an error indication
380Sstevel@tonic-gate  * for a fault that may have occurred in the data path of an IO transaction.
390Sstevel@tonic-gate  * Errors may be detected or received via an interrupt, a callback from
400Sstevel@tonic-gate  * another subsystem (e.g. a cpu trap) or examination of control data.
410Sstevel@tonic-gate  *
420Sstevel@tonic-gate  * Upon detection of an error, the nexus has a responsibility to alert
430Sstevel@tonic-gate  * its children of the error and the transaction associated with that
440Sstevel@tonic-gate  * error.  The actual implementation may vary depending upon the capabilities
450Sstevel@tonic-gate  * of the nexus, its underlying hardware and its children.  In this file,
460Sstevel@tonic-gate  * we provide support for typical nexus driver fault management tasks.
470Sstevel@tonic-gate  *
480Sstevel@tonic-gate  * Fault Management Initialization
490Sstevel@tonic-gate  *
500Sstevel@tonic-gate  *      Nexus drivers must implement two new busops, bus_fm_init() and
510Sstevel@tonic-gate  *      bus_fm_fini().  bus_fm_init() is called from a child nexus or device
520Sstevel@tonic-gate  *      driver and is expected to initialize any per-child state and return
530Sstevel@tonic-gate  *      the FM and error interrupt priority levels of the nexus driver.
540Sstevel@tonic-gate  *      Similarly, bus_fm_fini() is called by child drivers and should
550Sstevel@tonic-gate  *      clean-up any resources allocated during bus_fm_init().
560Sstevel@tonic-gate  *      These functions are called from passive kernel context, typically from
570Sstevel@tonic-gate  *      driver attach(9F) and detach(9F) entry points.
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  * Error Handler Dispatching
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  *      Nexus drivers implemented to support error handler capabilities
620Sstevel@tonic-gate  *	should invoke registered error handler callbacks for child drivers
630Sstevel@tonic-gate  *	thought to be involved in the error.
640Sstevel@tonic-gate  *	ndi_fm_handler_dispatch() is used to invoke
650Sstevel@tonic-gate  *      all error handlers and returns one of the following status
660Sstevel@tonic-gate  *      indications:
670Sstevel@tonic-gate  *
680Sstevel@tonic-gate  *      DDI_FM_OK - No errors found by any child
690Sstevel@tonic-gate  *      DDI_FM_FATAL - one or more children have detected a fatal error
700Sstevel@tonic-gate  *      DDI_FM_NONFATAL - no fatal errors, but one or more children have
710Sstevel@tonic-gate  *                            detected a non-fatal error
720Sstevel@tonic-gate  *
730Sstevel@tonic-gate  *      ndi_fm_handler_dispatch() may be called in any context
740Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
750Sstevel@tonic-gate  *      returned during initialization.
760Sstevel@tonic-gate  *
770Sstevel@tonic-gate  * Protected Accesses
780Sstevel@tonic-gate  *
790Sstevel@tonic-gate  *      When an access handle is mapped or a DMA handle is bound via the
800Sstevel@tonic-gate  *      standard busops, bus_map() or bus_dma_bindhdl(), a child driver
810Sstevel@tonic-gate  *      implemented to support DDI_FM_ACCCHK_CAPABLE or
820Sstevel@tonic-gate  *	DDI_FM_DMACHK_CAPABLE capabilites
830Sstevel@tonic-gate  *	expects the nexus to flag any errors detected for transactions
840Sstevel@tonic-gate  *	associated with the mapped or bound handles.
850Sstevel@tonic-gate  *
860Sstevel@tonic-gate  *      Children nexus or device drivers will set the following flags
870Sstevel@tonic-gate  *      in their ddi_device_access or dma_attr_flags when requesting
880Sstevel@tonic-gate  *      the an access or DMA handle mapping:
890Sstevel@tonic-gate  *
900Sstevel@tonic-gate  *      DDI_DMA_FLAGERR - nexus should set error status for any errors
910Sstevel@tonic-gate  *                              detected for a failed DMA transaction.
920Sstevel@tonic-gate  *      DDI_ACC_FLAGERR - nexus should set error status for any errors
930Sstevel@tonic-gate  *                              detected for a failed PIO transaction.
940Sstevel@tonic-gate  *
950Sstevel@tonic-gate  *      A nexus is expected to provide additional error detection and
960Sstevel@tonic-gate  *      handling for handles with these flags set.
970Sstevel@tonic-gate  *
980Sstevel@tonic-gate  * Exclusive Bus Access
990Sstevel@tonic-gate  *
1000Sstevel@tonic-gate  *      In cases where a driver requires a high level of fault tolerance
1010Sstevel@tonic-gate  *      for a programmed IO transaction, it is neccessary to grant exclusive
1020Sstevel@tonic-gate  *      access to the bus resource.  Exclusivity guarantees that a fault
1030Sstevel@tonic-gate  *      resulting from a transaction on the bus can be easily traced and
1040Sstevel@tonic-gate  *      reported to the driver requesting the transaction.
1050Sstevel@tonic-gate  *
1060Sstevel@tonic-gate  *      Nexus drivers must implement two new busops to support exclusive
1070Sstevel@tonic-gate  *      access, bus_fm_access_enter() and bus_fm_access_exit().  The IO
1080Sstevel@tonic-gate  *      framework will use these functions when it must set-up access
1090Sstevel@tonic-gate  *      handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
1100Sstevel@tonic-gate  *      their ddi_device_acc_attr_t request.
1110Sstevel@tonic-gate  *
1120Sstevel@tonic-gate  *      Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
1130Sstevel@tonic-gate  *      all other access requests until it receives bus_fm_access_exit()
1140Sstevel@tonic-gate  *      for the requested bus instance. bus_fm_access_enter() and
1150Sstevel@tonic-gate  *	bus_fm_access_exit() may be called from user, kernel or kernel
1160Sstevel@tonic-gate  *	interrupt context.
1170Sstevel@tonic-gate  *
1180Sstevel@tonic-gate  * Access and DMA Handle Caching
1190Sstevel@tonic-gate  *
1200Sstevel@tonic-gate  *      To aid a nexus driver in associating access or DMA handles with
1210Sstevel@tonic-gate  *      a detected error, the nexus should cache all handles that are
1220Sstevel@tonic-gate  *      associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
1230Sstevel@tonic-gate  *	DDI_DMA_FLAGERR requests from its children.  ndi_fmc_insert() is
1240Sstevel@tonic-gate  *	called by a nexus to cache handles with the above protection flags
1250Sstevel@tonic-gate  *	and ndi_fmc_remove() is called when that handle is unmapped or
1260Sstevel@tonic-gate  *	unbound by the requesting child.  ndi_fmc_insert() and
1270Sstevel@tonic-gate  *	ndi_fmc_remove() may be called from any user or kernel context.
1280Sstevel@tonic-gate  *
1290Sstevel@tonic-gate  *	FM caches are allocated during ddi_fm_init() and maintained
1300Sstevel@tonic-gate  *	as an array of elements that may be on one of two lists:
1310Sstevel@tonic-gate  *	free or active.  The free list is a singly-linked list of
1320Sstevel@tonic-gate  *	elements available for activity.  ndi_fm_insert() moves the
1330Sstevel@tonic-gate  *	element at the head of the free to the active list.  The active
1340Sstevel@tonic-gate  *	list is a doubly-linked searchable list.
1350Sstevel@tonic-gate  *	When a handle is unmapped or unbound, its associated cache
1360Sstevel@tonic-gate  *	entry is removed from the active list back to the free list.
1370Sstevel@tonic-gate  *
1380Sstevel@tonic-gate  *      Upon detection of an error, the nexus may invoke ndi_fmc_error() to
1390Sstevel@tonic-gate  *      iterate over the handle cache of one or more of its FM compliant
1400Sstevel@tonic-gate  *      children.  A comparison callback function is provided upon each
1410Sstevel@tonic-gate  *      invocation of ndi_fmc_error() to tell the IO framework if a
1420Sstevel@tonic-gate  *      handle is associated with an error.  If so, the framework will
1430Sstevel@tonic-gate  *      set the error status for that handle before returning from
1440Sstevel@tonic-gate  *      ndi_fmc_error().
1450Sstevel@tonic-gate  *
1460Sstevel@tonic-gate  *      ndi_fmc_error() may be called in any context
1470Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
1480Sstevel@tonic-gate  *      returned during initialization of the nexus and its children.
1490Sstevel@tonic-gate  *
1500Sstevel@tonic-gate  */
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate #include <sys/types.h>
1530Sstevel@tonic-gate #include <sys/param.h>
1540Sstevel@tonic-gate #include <sys/debug.h>
1550Sstevel@tonic-gate #include <sys/sunddi.h>
1560Sstevel@tonic-gate #include <sys/sunndi.h>
1570Sstevel@tonic-gate #include <sys/ddi.h>
1580Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
1590Sstevel@tonic-gate #include <sys/devctl.h>
1600Sstevel@tonic-gate #include <sys/nvpair.h>
1610Sstevel@tonic-gate #include <sys/ddifm.h>
1620Sstevel@tonic-gate #include <sys/ndifm.h>
1630Sstevel@tonic-gate #include <sys/spl.h>
1640Sstevel@tonic-gate #include <sys/sysmacros.h>
1650Sstevel@tonic-gate #include <sys/devops.h>
1660Sstevel@tonic-gate #include <sys/atomic.h>
1670Sstevel@tonic-gate #include <sys/fm/io/ddi.h>
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate /*
1700Sstevel@tonic-gate  * Allocate and initialize a fault management resource cache
1710Sstevel@tonic-gate  * A fault management cache consists of a set of cache elements that
1720Sstevel@tonic-gate  * may be on one of two lists: free or active.
1730Sstevel@tonic-gate  *
1740Sstevel@tonic-gate  * At creation time, every element but one is placed on the free list
1750Sstevel@tonic-gate  * except for the first element.  This element is reserved as the first
1760Sstevel@tonic-gate  * element of the active list and serves as an anchor for the active
1770Sstevel@tonic-gate  * list in ndi_fmc_insert() and ndi_fmc_remove().  In these functions,
1780Sstevel@tonic-gate  * it is not neccessary to check for the existence or validity of
1790Sstevel@tonic-gate  * the active list.
1800Sstevel@tonic-gate  */
1810Sstevel@tonic-gate void
1820Sstevel@tonic-gate i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc)
1830Sstevel@tonic-gate {
1840Sstevel@tonic-gate 	ndi_fmc_t *fcp;
1850Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	ASSERT(qlen > 1);
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
1900Sstevel@tonic-gate 	mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
1913114Scindi 	mutex_init(&fcp->fc_free_lock, NULL, MUTEX_DRIVER, NULL);
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	/* Preallocate and initialize entries for this fm cache */
1940Sstevel@tonic-gate 	fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP);
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	fcp->fc_len = qlen;
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	/* Intialize the active and free lists */
1990Sstevel@tonic-gate 	fcp->fc_active = fcp->fc_tail = fcp->fc_elems;
2000Sstevel@tonic-gate 	fcp->fc_free = fcp->fc_elems + 1;
2010Sstevel@tonic-gate 	qlen--;
2020Sstevel@tonic-gate 	for (fep = fcp->fc_free; qlen > 1; qlen--) {
2030Sstevel@tonic-gate 		fep->fce_prev = fep + 1;
2040Sstevel@tonic-gate 		fep++;
2050Sstevel@tonic-gate 	}
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	*fcpp = fcp;
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate  * Destroy and resources associated with the given fault management cache.
2120Sstevel@tonic-gate  */
2130Sstevel@tonic-gate void
2140Sstevel@tonic-gate i_ndi_fmc_destroy(ndi_fmc_t *fcp)
2150Sstevel@tonic-gate {
2160Sstevel@tonic-gate 	if (fcp == NULL)
2170Sstevel@tonic-gate 		return;
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t));
2200Sstevel@tonic-gate 	kmem_free(fcp, sizeof (ndi_fmc_t));
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate /*
2240Sstevel@tonic-gate  * Grow an existing fault management cache by grow_sz number of entries
2250Sstevel@tonic-gate  */
2260Sstevel@tonic-gate static int
2270Sstevel@tonic-gate fmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz)
2280Sstevel@tonic-gate {
2290Sstevel@tonic-gate 	int olen, nlen;
2300Sstevel@tonic-gate 	void *resource;
2310Sstevel@tonic-gate 	ndi_fmcentry_t *ncp, *oep, *nep, *nnep;
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	ASSERT(grow_sz);
2343114Scindi 	ASSERT(MUTEX_HELD(&fcp->fc_free_lock));
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	/* Allocate a new cache */
2370Sstevel@tonic-gate 	nlen = grow_sz + fcp->fc_len;
2380Sstevel@tonic-gate 	if ((ncp = kmem_zalloc(nlen * sizeof (ndi_fmcentry_t),
2390Sstevel@tonic-gate 	    KM_NOSLEEP)) == NULL)
2400Sstevel@tonic-gate 		return (1);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	/* Migrate old cache to new cache */
2430Sstevel@tonic-gate 	oep = fcp->fc_elems;
2440Sstevel@tonic-gate 	olen = fcp->fc_len;
2450Sstevel@tonic-gate 	for (nep = ncp; ; olen--) {
2460Sstevel@tonic-gate 		resource = nep->fce_resource = oep->fce_resource;
2470Sstevel@tonic-gate 		nep->fce_bus_specific = oep->fce_bus_specific;
2480Sstevel@tonic-gate 		if (resource) {
2490Sstevel@tonic-gate 			if (flag == DMA_HANDLE) {
2500Sstevel@tonic-gate 				((ddi_dma_impl_t *)resource)->
2510Sstevel@tonic-gate 				    dmai_error.err_fep = nep;
2520Sstevel@tonic-gate 			} else if (flag == ACC_HANDLE) {
2530Sstevel@tonic-gate 				((ddi_acc_impl_t *)resource)->
2540Sstevel@tonic-gate 				    ahi_err->err_fep = nep;
2550Sstevel@tonic-gate 			}
2560Sstevel@tonic-gate 		}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 		/*
2590Sstevel@tonic-gate 		 * This is the last entry.  Set the tail pointer and
2600Sstevel@tonic-gate 		 * terminate processing of the old cache.
2610Sstevel@tonic-gate 		 */
2620Sstevel@tonic-gate 		if (olen == 1) {
2630Sstevel@tonic-gate 			fcp->fc_tail = nep;
2640Sstevel@tonic-gate 			++nep;
2650Sstevel@tonic-gate 			break;
2660Sstevel@tonic-gate 		}
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 		/*
2690Sstevel@tonic-gate 		 * Set the next and previous pointer for the new cache
2700Sstevel@tonic-gate 		 * entry.
2710Sstevel@tonic-gate 		 */
2720Sstevel@tonic-gate 		nnep = nep + 1;
2730Sstevel@tonic-gate 		nep->fce_next = nnep;
2740Sstevel@tonic-gate 		nnep->fce_prev = nep;
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 		/* Advance to the next entry */
2770Sstevel@tonic-gate 		++oep;
2780Sstevel@tonic-gate 		nep = nnep;
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	/* Initialize and add remaining new cache entries to the free list */
282*3752Scindi 	for (fcp->fc_free = nep; nlen > fcp->fc_len + 1; nlen--) {
2830Sstevel@tonic-gate 		nep->fce_prev = nep + 1;
2840Sstevel@tonic-gate 		nep++;
2850Sstevel@tonic-gate 	}
2860Sstevel@tonic-gate 
287*3752Scindi 	oep = fcp->fc_elems;
288*3752Scindi 	olen = fcp->fc_len;
289*3752Scindi 	nlen = grow_sz + olen;
290*3752Scindi 
291*3752Scindi 	/*
292*3752Scindi 	 * Update the FM cache array and active list pointers.
293*3752Scindi 	 * Updates to these pointers require us to acquire the
294*3752Scindi 	 * FMA cache lock to prevent accesses to a stale active
295*3752Scindi 	 * list in ndi_fmc_error().
296*3752Scindi 	 */
297*3752Scindi 
298*3752Scindi 	mutex_enter(&fcp->fc_lock);
2990Sstevel@tonic-gate 	fcp->fc_active = ncp;
3000Sstevel@tonic-gate 	fcp->fc_elems = ncp;
301*3752Scindi 	fcp->fc_len = nlen;
302*3752Scindi 	mutex_exit(&fcp->fc_lock);
303*3752Scindi 
304*3752Scindi 	kmem_free(oep, olen * sizeof (ndi_fmcentry_t));
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	return (0);
3070Sstevel@tonic-gate }
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate /*
3100Sstevel@tonic-gate  * ndi_fmc_insert -
3110Sstevel@tonic-gate  * 	Add a new entry to the specified cache.
3120Sstevel@tonic-gate  *
3130Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL
3140Sstevel@tonic-gate  */
3150Sstevel@tonic-gate void
3160Sstevel@tonic-gate ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
3170Sstevel@tonic-gate {
3180Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
3190Sstevel@tonic-gate 	ndi_fmc_t *fcp;
3200Sstevel@tonic-gate 	ndi_fmcentry_t *fep, **fpp;
3210Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	ASSERT(devi);
3240Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
3270Sstevel@tonic-gate 	if (fmhdl == NULL) {
3280Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
3290Sstevel@tonic-gate 		return;
3300Sstevel@tonic-gate 	}
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
3330Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
3340Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
3350Sstevel@tonic-gate 			    DDI_NOSLEEP);
3360Sstevel@tonic-gate 			return;
3370Sstevel@tonic-gate 		}
3380Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
3390Sstevel@tonic-gate 		fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
3400Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
3410Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
3420Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
3430Sstevel@tonic-gate 			    DDI_NOSLEEP);
3440Sstevel@tonic-gate 			return;
3450Sstevel@tonic-gate 		}
3460Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
3470Sstevel@tonic-gate 		fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
3480Sstevel@tonic-gate 	}
3490Sstevel@tonic-gate 	ASSERT(*fpp == NULL);
3500Sstevel@tonic-gate 
3513114Scindi 	mutex_enter(&fcp->fc_free_lock);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	/* Get an entry from the free list */
3540Sstevel@tonic-gate 	fep = fcp->fc_free;
3550Sstevel@tonic-gate 	if (fep == NULL) {
3560Sstevel@tonic-gate 		if (fmc_grow(fcp, flag,
3570Sstevel@tonic-gate 		    (flag == ACC_HANDLE ? default_acccache_sz :
3580Sstevel@tonic-gate 		    default_dmacache_sz)) != 0) {
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 			/* Unable to get an entry or grow this cache */
3610Sstevel@tonic-gate 			atomic_add_64(
3620Sstevel@tonic-gate 			    &fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1);
3633114Scindi 			mutex_exit(&fcp->fc_free_lock);
3640Sstevel@tonic-gate 			return;
3650Sstevel@tonic-gate 		}
3660Sstevel@tonic-gate 		atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1);
3670Sstevel@tonic-gate 		fep = fcp->fc_free;
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 	fcp->fc_free = fep->fce_prev;
3703114Scindi 	mutex_exit(&fcp->fc_free_lock);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	/*
3730Sstevel@tonic-gate 	 * Set-up the handle resource and bus_specific information.
3740Sstevel@tonic-gate 	 * Also remember the pointer back to the cache for quick removal.
3750Sstevel@tonic-gate 	 */
3760Sstevel@tonic-gate 	fep->fce_bus_specific = bus_specific;
3770Sstevel@tonic-gate 	fep->fce_resource = resource;
3780Sstevel@tonic-gate 	fep->fce_next = NULL;
3790Sstevel@tonic-gate 	*fpp = fep;
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	/* Add entry to the end of the active list */
3823114Scindi 	mutex_enter(&fcp->fc_lock);
3830Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_tail;
3840Sstevel@tonic-gate 	fcp->fc_tail->fce_next = fep;
3850Sstevel@tonic-gate 	fcp->fc_tail = fep;
3860Sstevel@tonic-gate 	mutex_exit(&fcp->fc_lock);
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate /*
3900Sstevel@tonic-gate  * 	Remove an entry from the specified cache of access or dma mappings
3910Sstevel@tonic-gate  *
3920Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL.
3930Sstevel@tonic-gate  */
3940Sstevel@tonic-gate void
3950Sstevel@tonic-gate ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
3960Sstevel@tonic-gate {
3970Sstevel@tonic-gate 	ndi_fmc_t *fcp;
3980Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
3990Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
4000Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	ASSERT(devi);
4030Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
4060Sstevel@tonic-gate 	if (fmhdl == NULL) {
4070Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
4080Sstevel@tonic-gate 		return;
4090Sstevel@tonic-gate 	}
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	/* Find cache entry pointer for this resource */
4120Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
4130Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
4140Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
4150Sstevel@tonic-gate 			    DDI_NOSLEEP);
4160Sstevel@tonic-gate 			return;
4170Sstevel@tonic-gate 		}
4180Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 		ASSERT(fcp);
4210Sstevel@tonic-gate 
422*3752Scindi 		mutex_enter(&fcp->fc_free_lock);
4230Sstevel@tonic-gate 		fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
4240Sstevel@tonic-gate 		((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
4250Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
4260Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
4270Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
4280Sstevel@tonic-gate 			    DDI_NOSLEEP);
4290Sstevel@tonic-gate 			return;
4300Sstevel@tonic-gate 		}
4310Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 		ASSERT(fcp);
4340Sstevel@tonic-gate 
435*3752Scindi 		mutex_enter(&fcp->fc_free_lock);
4360Sstevel@tonic-gate 		fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
4370Sstevel@tonic-gate 		((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
438*3752Scindi 	} else {
439*3752Scindi 		return;
4400Sstevel@tonic-gate 	}
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	/*
4430Sstevel@tonic-gate 	 * Resource not in cache, return
4440Sstevel@tonic-gate 	 */
445*3752Scindi 	if (fep == NULL) {
446*3752Scindi 		mutex_exit(&fcp->fc_free_lock);
4470Sstevel@tonic-gate 		return;
448*3752Scindi 	}
4490Sstevel@tonic-gate 
450*3752Scindi 	/*
451*3752Scindi 	 * Updates to FM cache pointers require us to grab fmc_lock
452*3752Scindi 	 * to synchronize access to the cache for ndi_fmc_insert()
453*3752Scindi 	 * and ndi_fmc_error()
454*3752Scindi 	 */
4553114Scindi 	mutex_enter(&fcp->fc_lock);
4560Sstevel@tonic-gate 	fep->fce_prev->fce_next = fep->fce_next;
4570Sstevel@tonic-gate 	if (fep == fcp->fc_tail)
4580Sstevel@tonic-gate 		fcp->fc_tail = fep->fce_prev;
4590Sstevel@tonic-gate 	else
4600Sstevel@tonic-gate 		fep->fce_next->fce_prev = fep->fce_prev;
4613114Scindi 	mutex_exit(&fcp->fc_lock);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	/* Add entry back to the free list */
4640Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_free;
4650Sstevel@tonic-gate 	fcp->fc_free = fep;
4663114Scindi 	mutex_exit(&fcp->fc_free_lock);
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate 
4691865Sdilpreet int
4701865Sdilpreet ndi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr,
4711865Sdilpreet     const void *bus_err_state)
4721865Sdilpreet {
4731865Sdilpreet 	int status, fatal = 0, nonfatal = 0;
4741865Sdilpreet 	ndi_fmc_t *fcp = NULL;
4751865Sdilpreet 	ndi_fmcentry_t *fep;
4761865Sdilpreet 	struct i_ddi_fmhdl *fmhdl;
4771865Sdilpreet 
4781865Sdilpreet 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
4791865Sdilpreet 
4801865Sdilpreet 	fmhdl = DEVI(dip)->devi_fmhdl;
4811865Sdilpreet 	ASSERT(fmhdl);
4821865Sdilpreet 	status = DDI_FM_UNKNOWN;
4831865Sdilpreet 
4841865Sdilpreet 	if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
4851865Sdilpreet 		fcp = fmhdl->fh_dma_cache;
4861865Sdilpreet 		ASSERT(fcp);
4871865Sdilpreet 	} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
4881865Sdilpreet 		fcp = fmhdl->fh_acc_cache;
4891865Sdilpreet 		ASSERT(fcp);
4901865Sdilpreet 	}
4911865Sdilpreet 
4921865Sdilpreet 	if (fcp != NULL) {
4931865Sdilpreet 
4941865Sdilpreet 		/*
4951865Sdilpreet 		 * Check active resource entries
4961865Sdilpreet 		 */
4971865Sdilpreet 		mutex_enter(&fcp->fc_lock);
4981865Sdilpreet 		for (fep = fcp->fc_active->fce_next; fep != NULL;
4991865Sdilpreet 		    fep = fep->fce_next) {
5001865Sdilpreet 			ddi_fmcompare_t compare_func;
5011865Sdilpreet 
5021865Sdilpreet 			/*
5031865Sdilpreet 			 * Compare captured error state with handle
5041865Sdilpreet 			 * resources.  During the comparison and
5051865Sdilpreet 			 * subsequent error handling, we block
5061865Sdilpreet 			 * attempts to free the cache entry.
5071865Sdilpreet 			 */
5081865Sdilpreet 			compare_func = (flag == ACC_HANDLE) ?
5091865Sdilpreet 			    i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
5101865Sdilpreet 				fep->fce_resource) :
5111865Sdilpreet 			    i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
5121865Sdilpreet 				fep->fce_resource);
5131865Sdilpreet 
5141865Sdilpreet 			status = compare_func(dip, fep->fce_resource,
5151865Sdilpreet 			    bus_err_state, fep->fce_bus_specific);
5161865Sdilpreet 			if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK)
5171865Sdilpreet 				continue;
5181865Sdilpreet 
5191865Sdilpreet 			if (status == DDI_FM_FATAL)
5201865Sdilpreet 				++fatal;
5211865Sdilpreet 			else if (status == DDI_FM_NONFATAL)
5221865Sdilpreet 				++nonfatal;
5231865Sdilpreet 
5241865Sdilpreet 			/* Set the error for this resource handle */
5251865Sdilpreet 			if (flag == ACC_HANDLE) {
5261865Sdilpreet 				ddi_acc_handle_t ap = fep->fce_resource;
5271865Sdilpreet 
5281865Sdilpreet 				i_ddi_fm_acc_err_set(ap, derr->fme_ena, status,
5291865Sdilpreet 				    DDI_FM_ERR_UNEXPECTED);
5301865Sdilpreet 				ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
5311865Sdilpreet 				derr->fme_acc_handle = ap;
5321865Sdilpreet 			} else {
5331865Sdilpreet 				ddi_dma_handle_t dp = fep->fce_resource;
5341865Sdilpreet 
5351865Sdilpreet 				i_ddi_fm_dma_err_set(dp, derr->fme_ena, status,
5361865Sdilpreet 				    DDI_FM_ERR_UNEXPECTED);
5371865Sdilpreet 				ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
5381865Sdilpreet 				derr->fme_dma_handle = dp;
5391865Sdilpreet 			}
5401865Sdilpreet 			break;
5411865Sdilpreet 		}
5421865Sdilpreet 		mutex_exit(&fcp->fc_lock);
5431865Sdilpreet 	}
5441865Sdilpreet 	return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL :
5451865Sdilpreet 	    DDI_FM_UNKNOWN);
5461865Sdilpreet }
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate /*
5490Sstevel@tonic-gate  * Check error state against the handle resource stored in the specified
5500Sstevel@tonic-gate  * FM cache.  If tdip != NULL, we check only the cache entries for tdip.
5510Sstevel@tonic-gate  * The caller must ensure that tdip is valid throughout the call and
5520Sstevel@tonic-gate  * all FM data structures can be safely accesses.
5530Sstevel@tonic-gate  *
5540Sstevel@tonic-gate  * If tdip == NULL, we check all children that have registered their
5550Sstevel@tonic-gate  * FM_DMA_CHK or FM_ACC_CHK capabilities.
5560Sstevel@tonic-gate  *
5570Sstevel@tonic-gate  * The following status values may be returned:
5580Sstevel@tonic-gate  *
5590Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one cache entry comparison yields a
5600Sstevel@tonic-gate  *			fatal error.
5610Sstevel@tonic-gate  *
5620Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one cache entry comparison yields a
5630Sstevel@tonic-gate  *			non-fatal error and no comparison yields a fatal error.
5640Sstevel@tonic-gate  *
5650Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
5660Sstevel@tonic-gate  *			non-fatal errors.
5670Sstevel@tonic-gate  *
5680Sstevel@tonic-gate  */
5690Sstevel@tonic-gate int
5701865Sdilpreet ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena,
5711865Sdilpreet     const void *bus_err_state)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate 	int status, fatal = 0, nonfatal = 0;
5740Sstevel@tonic-gate 	ddi_fm_error_t derr;
5750Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
5760Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
5810Sstevel@tonic-gate 	fmhdl = DEVI(dip)->devi_fmhdl;
5820Sstevel@tonic-gate 	ASSERT(fmhdl);
5831865Sdilpreet 
5841865Sdilpreet 	bzero(&derr, sizeof (ddi_fm_error_t));
5851865Sdilpreet 	derr.fme_version = DDI_FME_VERSION;
5861865Sdilpreet 	derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
5871865Sdilpreet 	derr.fme_ena = ena;
5881865Sdilpreet 
5890Sstevel@tonic-gate 	for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 		if (tdip != NULL && tdip != tgt->ft_dip)
5920Sstevel@tonic-gate 			continue;
5930Sstevel@tonic-gate 
5941865Sdilpreet 		/*
5951865Sdilpreet 		 * Attempt to find the entry in this childs handle cache
5961865Sdilpreet 		 */
5971865Sdilpreet 		status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr,
5981865Sdilpreet 		    bus_err_state);
5990Sstevel@tonic-gate 
6001865Sdilpreet 		if (status == DDI_FM_FATAL)
6011865Sdilpreet 			++fatal;
6021865Sdilpreet 		else if (status == DDI_FM_NONFATAL)
6031865Sdilpreet 			++nonfatal;
6041865Sdilpreet 		else
6051865Sdilpreet 			continue;
6060Sstevel@tonic-gate 
6071865Sdilpreet 		/*
6081865Sdilpreet 		 * Call our child to process this error.
6091865Sdilpreet 		 */
6101865Sdilpreet 		status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr,
6111865Sdilpreet 		    tgt->ft_errhdl->eh_impl);
6120Sstevel@tonic-gate 
6131865Sdilpreet 		if (status == DDI_FM_FATAL)
6141865Sdilpreet 			++fatal;
6151865Sdilpreet 		else if (status == DDI_FM_NONFATAL)
6161865Sdilpreet 			++nonfatal;
6171865Sdilpreet 	}
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	if (fatal)
6220Sstevel@tonic-gate 		return (DDI_FM_FATAL);
6230Sstevel@tonic-gate 	else if (nonfatal)
6240Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	return (DDI_FM_UNKNOWN);
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate 
6293159Sstephh int
6303159Sstephh ndi_fmc_entry_error_all(dev_info_t *dip, int flag, ddi_fm_error_t *derr)
6313159Sstephh {
6323159Sstephh 	ndi_fmc_t *fcp = NULL;
6333159Sstephh 	ndi_fmcentry_t *fep;
6343159Sstephh 	struct i_ddi_fmhdl *fmhdl;
6353159Sstephh 	int nonfatal = 0;
6363159Sstephh 
6373159Sstephh 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
6383159Sstephh 
6393159Sstephh 	fmhdl = DEVI(dip)->devi_fmhdl;
6403159Sstephh 	ASSERT(fmhdl);
6413159Sstephh 
6423159Sstephh 	if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
6433159Sstephh 		fcp = fmhdl->fh_dma_cache;
6443159Sstephh 		ASSERT(fcp);
6453159Sstephh 	} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
6463159Sstephh 		fcp = fmhdl->fh_acc_cache;
6473159Sstephh 		ASSERT(fcp);
6483159Sstephh 	}
6493159Sstephh 
6503159Sstephh 	if (fcp != NULL) {
6513159Sstephh 		/*
6523159Sstephh 		 * Check active resource entries
6533159Sstephh 		 */
6543159Sstephh 		mutex_enter(&fcp->fc_lock);
6553159Sstephh 		for (fep = fcp->fc_active->fce_next; fep != NULL;
6563159Sstephh 		    fep = fep->fce_next) {
6573159Sstephh 			/* Set the error for this resource handle */
6583159Sstephh 			nonfatal++;
6593159Sstephh 			if (flag == ACC_HANDLE) {
6603159Sstephh 				ddi_acc_handle_t ap = fep->fce_resource;
6613159Sstephh 
6623159Sstephh 				i_ddi_fm_acc_err_set(ap, derr->fme_ena,
6633159Sstephh 				    DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
6643159Sstephh 				ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
6653159Sstephh 				derr->fme_acc_handle = ap;
6663159Sstephh 			} else {
6673159Sstephh 				ddi_dma_handle_t dp = fep->fce_resource;
6683159Sstephh 
6693159Sstephh 				i_ddi_fm_dma_err_set(dp, derr->fme_ena,
6703159Sstephh 				    DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
6713159Sstephh 				ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
6723159Sstephh 				derr->fme_dma_handle = dp;
6733159Sstephh 			}
6743159Sstephh 		}
6753159Sstephh 		mutex_exit(&fcp->fc_lock);
6763159Sstephh 	}
6773159Sstephh 	return (nonfatal ? DDI_FM_NONFATAL : DDI_FM_UNKNOWN);
6783159Sstephh }
6793159Sstephh 
6800Sstevel@tonic-gate /*
6810Sstevel@tonic-gate  * Dispatch registered error handlers for dip.  If tdip != NULL, only
6820Sstevel@tonic-gate  * the error handler (if available) for tdip is invoked.  Otherwise,
6830Sstevel@tonic-gate  * all registered error handlers are invoked.
6840Sstevel@tonic-gate  *
6850Sstevel@tonic-gate  * The following status values may be returned:
6860Sstevel@tonic-gate  *
6870Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one error handler returns a
6880Sstevel@tonic-gate  *			fatal error.
6890Sstevel@tonic-gate  *
6900Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one error handler returns a
6910Sstevel@tonic-gate  *			non-fatal error and none returned a fatal error.
6920Sstevel@tonic-gate  *
6930Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - if at least one error handler returns
6940Sstevel@tonic-gate  *			unknown status and none return fatal or non-fatal.
6950Sstevel@tonic-gate  *
6960Sstevel@tonic-gate  *	DDI_FM_OK - if all error handlers return DDI_FM_OK
6970Sstevel@tonic-gate  */
6980Sstevel@tonic-gate int
6990Sstevel@tonic-gate ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip,
7000Sstevel@tonic-gate     const ddi_fm_error_t *nerr)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	int status;
7030Sstevel@tonic-gate 	int unknown = 0, fatal = 0, nonfatal = 0;
7040Sstevel@tonic-gate 	struct i_ddi_fmhdl *hdl;
7050Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	status = DDI_FM_UNKNOWN;
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
7100Sstevel@tonic-gate 	hdl = DEVI(dip)->devi_fmhdl;
7110Sstevel@tonic-gate 	tgt = hdl->fh_tgts;
7120Sstevel@tonic-gate 	while (tgt != NULL) {
7130Sstevel@tonic-gate 		if (tdip == NULL || tdip == tgt->ft_dip) {
7140Sstevel@tonic-gate 			struct i_ddi_errhdl *errhdl;
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 			errhdl = tgt->ft_errhdl;
7170Sstevel@tonic-gate 			status = errhdl->eh_func(tgt->ft_dip, nerr,
7180Sstevel@tonic-gate 			    errhdl->eh_impl);
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 			if (status == DDI_FM_FATAL)
7210Sstevel@tonic-gate 				++fatal;
7220Sstevel@tonic-gate 			else if (status == DDI_FM_NONFATAL)
7230Sstevel@tonic-gate 				++nonfatal;
7240Sstevel@tonic-gate 			else if (status == DDI_FM_UNKNOWN)
7250Sstevel@tonic-gate 				++unknown;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 			/* Only interested in one target */
7280Sstevel@tonic-gate 			if (tdip != NULL)
7290Sstevel@tonic-gate 				break;
7300Sstevel@tonic-gate 		}
7310Sstevel@tonic-gate 		tgt = tgt->ft_next;
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate 	if (fatal)
7360Sstevel@tonic-gate 		return (DDI_FM_FATAL);
7370Sstevel@tonic-gate 	else if (nonfatal)
7380Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
7390Sstevel@tonic-gate 	else if (unknown)
7400Sstevel@tonic-gate 		return (DDI_FM_UNKNOWN);
7410Sstevel@tonic-gate 	else
7420Sstevel@tonic-gate 		return (DDI_FM_OK);
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate /*
7460Sstevel@tonic-gate  * Set error status for specified access or DMA handle
7470Sstevel@tonic-gate  *
7480Sstevel@tonic-gate  * May be called in any context but caller must insure validity of
7490Sstevel@tonic-gate  * handle.
7500Sstevel@tonic-gate  */
7510Sstevel@tonic-gate void
7520Sstevel@tonic-gate ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe)
7530Sstevel@tonic-gate {
7540Sstevel@tonic-gate 	i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status,
7550Sstevel@tonic-gate 	    dfe->fme_flag);
7560Sstevel@tonic-gate }
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate void
7590Sstevel@tonic-gate ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe)
7600Sstevel@tonic-gate {
7610Sstevel@tonic-gate 	i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status,
7620Sstevel@tonic-gate 	    dfe->fme_flag);
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate /*
7660Sstevel@tonic-gate  * Call parent busop fm initialization routine.
7670Sstevel@tonic-gate  *
7680Sstevel@tonic-gate  * Called during driver attach(1M)
7690Sstevel@tonic-gate  */
7700Sstevel@tonic-gate int
7710Sstevel@tonic-gate i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc)
7720Sstevel@tonic-gate {
7730Sstevel@tonic-gate 	int pcap;
7740Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	if (dip == ddi_root_node())
7770Sstevel@tonic-gate 		return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE);
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
7800Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
7810Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL)
7840Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 	pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init)
7870Sstevel@tonic-gate 	    (pdip, dip, tcap, ibc);
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	return (pcap);
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate /*
7930Sstevel@tonic-gate  * Call parent busop fm clean-up routine.
7940Sstevel@tonic-gate  *
7950Sstevel@tonic-gate  * Called during driver detach(1M)
7960Sstevel@tonic-gate  */
7970Sstevel@tonic-gate void
7980Sstevel@tonic-gate i_ndi_busop_fm_fini(dev_info_t *dip)
7990Sstevel@tonic-gate {
8000Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	if (dip == ddi_root_node())
8030Sstevel@tonic-gate 		return;
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
8060Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
8070Sstevel@tonic-gate 		return;
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL)
8100Sstevel@tonic-gate 		return;
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip);
8130Sstevel@tonic-gate }
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate /*
8160Sstevel@tonic-gate  * The following routines provide exclusive access to a nexus resource
8170Sstevel@tonic-gate  *
8180Sstevel@tonic-gate  * These busops may be called in user or kernel driver context.
8190Sstevel@tonic-gate  */
8200Sstevel@tonic-gate void
8210Sstevel@tonic-gate i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle)
8220Sstevel@tonic-gate {
8230Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
8260Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
8270Sstevel@tonic-gate 		return;
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL)
8300Sstevel@tonic-gate 		return;
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter)
8330Sstevel@tonic-gate 	    (pdip, handle);
8340Sstevel@tonic-gate }
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate void
8370Sstevel@tonic-gate i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle)
8380Sstevel@tonic-gate {
8390Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
8420Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
8430Sstevel@tonic-gate 		return;
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL)
8460Sstevel@tonic-gate 		return;
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle);
8490Sstevel@tonic-gate }
850