xref: /titanic_51/usr/src/uts/common/os/ndifm.c (revision 6b804b7dd19910467b5d3bdaca7ed2f66cc5ad58)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
500d0963fSdilpreet  * Common Development and Distribution License (the "License").
600d0963fSdilpreet  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*6b804b7dSstephh  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * Fault Management for Nexus Device Drivers
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * In addition to implementing and supporting Fault Management for Device
327c478bd9Sstevel@tonic-gate  * Drivers (ddifm.c), nexus drivers must support their children by
337c478bd9Sstevel@tonic-gate  * reporting FM capabilities, intializing interrupt block cookies
347c478bd9Sstevel@tonic-gate  * for error handling callbacks and caching mapped resources for lookup
357c478bd9Sstevel@tonic-gate  * during the detection of an IO transaction error.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * It is typically the nexus driver that receives an error indication
387c478bd9Sstevel@tonic-gate  * for a fault that may have occurred in the data path of an IO transaction.
397c478bd9Sstevel@tonic-gate  * Errors may be detected or received via an interrupt, a callback from
407c478bd9Sstevel@tonic-gate  * another subsystem (e.g. a cpu trap) or examination of control data.
417c478bd9Sstevel@tonic-gate  *
427c478bd9Sstevel@tonic-gate  * Upon detection of an error, the nexus has a responsibility to alert
437c478bd9Sstevel@tonic-gate  * its children of the error and the transaction associated with that
447c478bd9Sstevel@tonic-gate  * error.  The actual implementation may vary depending upon the capabilities
457c478bd9Sstevel@tonic-gate  * of the nexus, its underlying hardware and its children.  In this file,
467c478bd9Sstevel@tonic-gate  * we provide support for typical nexus driver fault management tasks.
477c478bd9Sstevel@tonic-gate  *
487c478bd9Sstevel@tonic-gate  * Fault Management Initialization
497c478bd9Sstevel@tonic-gate  *
507c478bd9Sstevel@tonic-gate  *      Nexus drivers must implement two new busops, bus_fm_init() and
517c478bd9Sstevel@tonic-gate  *      bus_fm_fini().  bus_fm_init() is called from a child nexus or device
527c478bd9Sstevel@tonic-gate  *      driver and is expected to initialize any per-child state and return
537c478bd9Sstevel@tonic-gate  *      the FM and error interrupt priority levels of the nexus driver.
547c478bd9Sstevel@tonic-gate  *      Similarly, bus_fm_fini() is called by child drivers and should
557c478bd9Sstevel@tonic-gate  *      clean-up any resources allocated during bus_fm_init().
567c478bd9Sstevel@tonic-gate  *      These functions are called from passive kernel context, typically from
577c478bd9Sstevel@tonic-gate  *      driver attach(9F) and detach(9F) entry points.
587c478bd9Sstevel@tonic-gate  *
597c478bd9Sstevel@tonic-gate  * Error Handler Dispatching
607c478bd9Sstevel@tonic-gate  *
617c478bd9Sstevel@tonic-gate  *      Nexus drivers implemented to support error handler capabilities
627c478bd9Sstevel@tonic-gate  *	should invoke registered error handler callbacks for child drivers
637c478bd9Sstevel@tonic-gate  *	thought to be involved in the error.
647c478bd9Sstevel@tonic-gate  *	ndi_fm_handler_dispatch() is used to invoke
657c478bd9Sstevel@tonic-gate  *      all error handlers and returns one of the following status
667c478bd9Sstevel@tonic-gate  *      indications:
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  *      DDI_FM_OK - No errors found by any child
697c478bd9Sstevel@tonic-gate  *      DDI_FM_FATAL - one or more children have detected a fatal error
707c478bd9Sstevel@tonic-gate  *      DDI_FM_NONFATAL - no fatal errors, but one or more children have
717c478bd9Sstevel@tonic-gate  *                            detected a non-fatal error
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  *      ndi_fm_handler_dispatch() may be called in any context
747c478bd9Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
757c478bd9Sstevel@tonic-gate  *      returned during initialization.
767c478bd9Sstevel@tonic-gate  *
777c478bd9Sstevel@tonic-gate  * Protected Accesses
787c478bd9Sstevel@tonic-gate  *
797c478bd9Sstevel@tonic-gate  *      When an access handle is mapped or a DMA handle is bound via the
807c478bd9Sstevel@tonic-gate  *      standard busops, bus_map() or bus_dma_bindhdl(), a child driver
817c478bd9Sstevel@tonic-gate  *      implemented to support DDI_FM_ACCCHK_CAPABLE or
827c478bd9Sstevel@tonic-gate  *	DDI_FM_DMACHK_CAPABLE capabilites
837c478bd9Sstevel@tonic-gate  *	expects the nexus to flag any errors detected for transactions
847c478bd9Sstevel@tonic-gate  *	associated with the mapped or bound handles.
857c478bd9Sstevel@tonic-gate  *
867c478bd9Sstevel@tonic-gate  *      Children nexus or device drivers will set the following flags
877c478bd9Sstevel@tonic-gate  *      in their ddi_device_access or dma_attr_flags when requesting
887c478bd9Sstevel@tonic-gate  *      the an access or DMA handle mapping:
897c478bd9Sstevel@tonic-gate  *
907c478bd9Sstevel@tonic-gate  *      DDI_DMA_FLAGERR - nexus should set error status for any errors
917c478bd9Sstevel@tonic-gate  *                              detected for a failed DMA transaction.
927c478bd9Sstevel@tonic-gate  *      DDI_ACC_FLAGERR - nexus should set error status for any errors
937c478bd9Sstevel@tonic-gate  *                              detected for a failed PIO transaction.
947c478bd9Sstevel@tonic-gate  *
957c478bd9Sstevel@tonic-gate  *      A nexus is expected to provide additional error detection and
967c478bd9Sstevel@tonic-gate  *      handling for handles with these flags set.
977c478bd9Sstevel@tonic-gate  *
987c478bd9Sstevel@tonic-gate  * Exclusive Bus Access
997c478bd9Sstevel@tonic-gate  *
1007c478bd9Sstevel@tonic-gate  *      In cases where a driver requires a high level of fault tolerance
1017c478bd9Sstevel@tonic-gate  *      for a programmed IO transaction, it is neccessary to grant exclusive
1027c478bd9Sstevel@tonic-gate  *      access to the bus resource.  Exclusivity guarantees that a fault
1037c478bd9Sstevel@tonic-gate  *      resulting from a transaction on the bus can be easily traced and
1047c478bd9Sstevel@tonic-gate  *      reported to the driver requesting the transaction.
1057c478bd9Sstevel@tonic-gate  *
1067c478bd9Sstevel@tonic-gate  *      Nexus drivers must implement two new busops to support exclusive
1077c478bd9Sstevel@tonic-gate  *      access, bus_fm_access_enter() and bus_fm_access_exit().  The IO
1087c478bd9Sstevel@tonic-gate  *      framework will use these functions when it must set-up access
1097c478bd9Sstevel@tonic-gate  *      handles that set devacc_attr_access to DDI_ACC_CAUTIOUS in
1107c478bd9Sstevel@tonic-gate  *      their ddi_device_acc_attr_t request.
1117c478bd9Sstevel@tonic-gate  *
1127c478bd9Sstevel@tonic-gate  *      Upon receipt of a bus_fm_access_enter() request, the nexus must prevent
1137c478bd9Sstevel@tonic-gate  *      all other access requests until it receives bus_fm_access_exit()
1147c478bd9Sstevel@tonic-gate  *      for the requested bus instance. bus_fm_access_enter() and
1157c478bd9Sstevel@tonic-gate  *	bus_fm_access_exit() may be called from user, kernel or kernel
1167c478bd9Sstevel@tonic-gate  *	interrupt context.
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  * Access and DMA Handle Caching
1197c478bd9Sstevel@tonic-gate  *
1207c478bd9Sstevel@tonic-gate  *      To aid a nexus driver in associating access or DMA handles with
1217c478bd9Sstevel@tonic-gate  *      a detected error, the nexus should cache all handles that are
1227c478bd9Sstevel@tonic-gate  *      associated with DDI_ACC_FLAGERR, DDI_ACC_CAUTIOUS_ACC or
1237c478bd9Sstevel@tonic-gate  *	DDI_DMA_FLAGERR requests from its children.  ndi_fmc_insert() is
1247c478bd9Sstevel@tonic-gate  *	called by a nexus to cache handles with the above protection flags
1257c478bd9Sstevel@tonic-gate  *	and ndi_fmc_remove() is called when that handle is unmapped or
1267c478bd9Sstevel@tonic-gate  *	unbound by the requesting child.  ndi_fmc_insert() and
1277c478bd9Sstevel@tonic-gate  *	ndi_fmc_remove() may be called from any user or kernel context.
1287c478bd9Sstevel@tonic-gate  *
1297c478bd9Sstevel@tonic-gate  *	FM caches are allocated during ddi_fm_init() and maintained
1307c478bd9Sstevel@tonic-gate  *	as an array of elements that may be on one of two lists:
1317c478bd9Sstevel@tonic-gate  *	free or active.  The free list is a singly-linked list of
1327c478bd9Sstevel@tonic-gate  *	elements available for activity.  ndi_fm_insert() moves the
1337c478bd9Sstevel@tonic-gate  *	element at the head of the free to the active list.  The active
1347c478bd9Sstevel@tonic-gate  *	list is a doubly-linked searchable list.
1357c478bd9Sstevel@tonic-gate  *	When a handle is unmapped or unbound, its associated cache
1367c478bd9Sstevel@tonic-gate  *	entry is removed from the active list back to the free list.
1377c478bd9Sstevel@tonic-gate  *
1387c478bd9Sstevel@tonic-gate  *      Upon detection of an error, the nexus may invoke ndi_fmc_error() to
1397c478bd9Sstevel@tonic-gate  *      iterate over the handle cache of one or more of its FM compliant
1407c478bd9Sstevel@tonic-gate  *      children.  A comparison callback function is provided upon each
1417c478bd9Sstevel@tonic-gate  *      invocation of ndi_fmc_error() to tell the IO framework if a
1427c478bd9Sstevel@tonic-gate  *      handle is associated with an error.  If so, the framework will
1437c478bd9Sstevel@tonic-gate  *      set the error status for that handle before returning from
1447c478bd9Sstevel@tonic-gate  *      ndi_fmc_error().
1457c478bd9Sstevel@tonic-gate  *
1467c478bd9Sstevel@tonic-gate  *      ndi_fmc_error() may be called in any context
1477c478bd9Sstevel@tonic-gate  *      subject to the constraints specified by the interrupt iblock cookie
1487c478bd9Sstevel@tonic-gate  *      returned during initialization of the nexus and its children.
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  */
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate #include <sys/types.h>
1537c478bd9Sstevel@tonic-gate #include <sys/param.h>
1547c478bd9Sstevel@tonic-gate #include <sys/debug.h>
1557c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
1567c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
1577c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
1587c478bd9Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
1597c478bd9Sstevel@tonic-gate #include <sys/devctl.h>
1607c478bd9Sstevel@tonic-gate #include <sys/nvpair.h>
1617c478bd9Sstevel@tonic-gate #include <sys/ddifm.h>
1627c478bd9Sstevel@tonic-gate #include <sys/ndifm.h>
1637c478bd9Sstevel@tonic-gate #include <sys/spl.h>
1647c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
1657c478bd9Sstevel@tonic-gate #include <sys/devops.h>
1667c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
1677c478bd9Sstevel@tonic-gate #include <sys/fm/io/ddi.h>
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate /*
1707c478bd9Sstevel@tonic-gate  * Allocate and initialize a fault management resource cache
1717c478bd9Sstevel@tonic-gate  * A fault management cache consists of a set of cache elements that
1727c478bd9Sstevel@tonic-gate  * may be on one of two lists: free or active.
1737c478bd9Sstevel@tonic-gate  *
1747c478bd9Sstevel@tonic-gate  * At creation time, every element but one is placed on the free list
1757c478bd9Sstevel@tonic-gate  * except for the first element.  This element is reserved as the first
1767c478bd9Sstevel@tonic-gate  * element of the active list and serves as an anchor for the active
1777c478bd9Sstevel@tonic-gate  * list in ndi_fmc_insert() and ndi_fmc_remove().  In these functions,
1787c478bd9Sstevel@tonic-gate  * it is not neccessary to check for the existence or validity of
1797c478bd9Sstevel@tonic-gate  * the active list.
1807c478bd9Sstevel@tonic-gate  */
1817c478bd9Sstevel@tonic-gate void
1827c478bd9Sstevel@tonic-gate i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc)
1837c478bd9Sstevel@tonic-gate {
1847c478bd9Sstevel@tonic-gate 	ndi_fmc_t *fcp;
1857c478bd9Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	ASSERT(qlen > 1);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
1907c478bd9Sstevel@tonic-gate 	mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
191fa52d01bScindi 	mutex_init(&fcp->fc_free_lock, NULL, MUTEX_DRIVER, NULL);
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 	/* Preallocate and initialize entries for this fm cache */
1947c478bd9Sstevel@tonic-gate 	fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	fcp->fc_len = qlen;
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	/* Intialize the active and free lists */
1997c478bd9Sstevel@tonic-gate 	fcp->fc_active = fcp->fc_tail = fcp->fc_elems;
2007c478bd9Sstevel@tonic-gate 	fcp->fc_free = fcp->fc_elems + 1;
2017c478bd9Sstevel@tonic-gate 	qlen--;
2027c478bd9Sstevel@tonic-gate 	for (fep = fcp->fc_free; qlen > 1; qlen--) {
2037c478bd9Sstevel@tonic-gate 		fep->fce_prev = fep + 1;
2047c478bd9Sstevel@tonic-gate 		fep++;
2057c478bd9Sstevel@tonic-gate 	}
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	*fcpp = fcp;
2087c478bd9Sstevel@tonic-gate }
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate /*
2117c478bd9Sstevel@tonic-gate  * Destroy and resources associated with the given fault management cache.
2127c478bd9Sstevel@tonic-gate  */
2137c478bd9Sstevel@tonic-gate void
2147c478bd9Sstevel@tonic-gate i_ndi_fmc_destroy(ndi_fmc_t *fcp)
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate 	if (fcp == NULL)
2177c478bd9Sstevel@tonic-gate 		return;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	kmem_free(fcp->fc_elems, fcp->fc_len * sizeof (ndi_fmcentry_t));
2207c478bd9Sstevel@tonic-gate 	kmem_free(fcp, sizeof (ndi_fmc_t));
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate /*
2247c478bd9Sstevel@tonic-gate  * Grow an existing fault management cache by grow_sz number of entries
2257c478bd9Sstevel@tonic-gate  */
2267c478bd9Sstevel@tonic-gate static int
2277c478bd9Sstevel@tonic-gate fmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	int olen, nlen;
2307c478bd9Sstevel@tonic-gate 	void *resource;
2317c478bd9Sstevel@tonic-gate 	ndi_fmcentry_t *ncp, *oep, *nep, *nnep;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	ASSERT(grow_sz);
234fa52d01bScindi 	ASSERT(MUTEX_HELD(&fcp->fc_free_lock));
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/* Allocate a new cache */
2377c478bd9Sstevel@tonic-gate 	nlen = grow_sz + fcp->fc_len;
2387c478bd9Sstevel@tonic-gate 	if ((ncp = kmem_zalloc(nlen * sizeof (ndi_fmcentry_t),
2397c478bd9Sstevel@tonic-gate 	    KM_NOSLEEP)) == NULL)
2407c478bd9Sstevel@tonic-gate 		return (1);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	/* Migrate old cache to new cache */
2437c478bd9Sstevel@tonic-gate 	oep = fcp->fc_elems;
2447c478bd9Sstevel@tonic-gate 	olen = fcp->fc_len;
2457c478bd9Sstevel@tonic-gate 	for (nep = ncp; ; olen--) {
2467c478bd9Sstevel@tonic-gate 		resource = nep->fce_resource = oep->fce_resource;
2477c478bd9Sstevel@tonic-gate 		nep->fce_bus_specific = oep->fce_bus_specific;
2487c478bd9Sstevel@tonic-gate 		if (resource) {
2497c478bd9Sstevel@tonic-gate 			if (flag == DMA_HANDLE) {
2507c478bd9Sstevel@tonic-gate 				((ddi_dma_impl_t *)resource)->
2517c478bd9Sstevel@tonic-gate 				    dmai_error.err_fep = nep;
2527c478bd9Sstevel@tonic-gate 			} else if (flag == ACC_HANDLE) {
2537c478bd9Sstevel@tonic-gate 				((ddi_acc_impl_t *)resource)->
2547c478bd9Sstevel@tonic-gate 				    ahi_err->err_fep = nep;
2557c478bd9Sstevel@tonic-gate 			}
2567c478bd9Sstevel@tonic-gate 		}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 		/*
2597c478bd9Sstevel@tonic-gate 		 * This is the last entry.  Set the tail pointer and
2607c478bd9Sstevel@tonic-gate 		 * terminate processing of the old cache.
2617c478bd9Sstevel@tonic-gate 		 */
2627c478bd9Sstevel@tonic-gate 		if (olen == 1) {
2637c478bd9Sstevel@tonic-gate 			fcp->fc_tail = nep;
2647c478bd9Sstevel@tonic-gate 			++nep;
2657c478bd9Sstevel@tonic-gate 			break;
2667c478bd9Sstevel@tonic-gate 		}
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 		/*
2697c478bd9Sstevel@tonic-gate 		 * Set the next and previous pointer for the new cache
2707c478bd9Sstevel@tonic-gate 		 * entry.
2717c478bd9Sstevel@tonic-gate 		 */
2727c478bd9Sstevel@tonic-gate 		nnep = nep + 1;
2737c478bd9Sstevel@tonic-gate 		nep->fce_next = nnep;
2747c478bd9Sstevel@tonic-gate 		nnep->fce_prev = nep;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 		/* Advance to the next entry */
2777c478bd9Sstevel@tonic-gate 		++oep;
2787c478bd9Sstevel@tonic-gate 		nep = nnep;
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	/* Initialize and add remaining new cache entries to the free list */
2823c9b99a0Scindi 	for (fcp->fc_free = nep; nlen > fcp->fc_len + 1; nlen--) {
2837c478bd9Sstevel@tonic-gate 		nep->fce_prev = nep + 1;
2847c478bd9Sstevel@tonic-gate 		nep++;
2857c478bd9Sstevel@tonic-gate 	}
2867c478bd9Sstevel@tonic-gate 
2873c9b99a0Scindi 	oep = fcp->fc_elems;
2883c9b99a0Scindi 	olen = fcp->fc_len;
2893c9b99a0Scindi 	nlen = grow_sz + olen;
2903c9b99a0Scindi 
2913c9b99a0Scindi 	/*
2923c9b99a0Scindi 	 * Update the FM cache array and active list pointers.
2933c9b99a0Scindi 	 * Updates to these pointers require us to acquire the
2943c9b99a0Scindi 	 * FMA cache lock to prevent accesses to a stale active
2953c9b99a0Scindi 	 * list in ndi_fmc_error().
2963c9b99a0Scindi 	 */
2973c9b99a0Scindi 
2983c9b99a0Scindi 	mutex_enter(&fcp->fc_lock);
2997c478bd9Sstevel@tonic-gate 	fcp->fc_active = ncp;
3007c478bd9Sstevel@tonic-gate 	fcp->fc_elems = ncp;
3013c9b99a0Scindi 	fcp->fc_len = nlen;
3023c9b99a0Scindi 	mutex_exit(&fcp->fc_lock);
3033c9b99a0Scindi 
3043c9b99a0Scindi 	kmem_free(oep, olen * sizeof (ndi_fmcentry_t));
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	return (0);
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate /*
3107c478bd9Sstevel@tonic-gate  * ndi_fmc_insert -
3117c478bd9Sstevel@tonic-gate  * 	Add a new entry to the specified cache.
3127c478bd9Sstevel@tonic-gate  *
3137c478bd9Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL
3147c478bd9Sstevel@tonic-gate  */
3157c478bd9Sstevel@tonic-gate void
3167c478bd9Sstevel@tonic-gate ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
3177c478bd9Sstevel@tonic-gate {
3187c478bd9Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
3197c478bd9Sstevel@tonic-gate 	ndi_fmc_t *fcp;
3207c478bd9Sstevel@tonic-gate 	ndi_fmcentry_t *fep, **fpp;
3217c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	ASSERT(devi);
3247c478bd9Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
3277c478bd9Sstevel@tonic-gate 	if (fmhdl == NULL) {
3287c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
3297c478bd9Sstevel@tonic-gate 		return;
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
3337c478bd9Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
3347c478bd9Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
3357c478bd9Sstevel@tonic-gate 			    DDI_NOSLEEP);
3367c478bd9Sstevel@tonic-gate 			return;
3377c478bd9Sstevel@tonic-gate 		}
3387c478bd9Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
3397c478bd9Sstevel@tonic-gate 		fpp = &((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
3407c478bd9Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
3417c478bd9Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
3427c478bd9Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
3437c478bd9Sstevel@tonic-gate 			    DDI_NOSLEEP);
3447c478bd9Sstevel@tonic-gate 			return;
3457c478bd9Sstevel@tonic-gate 		}
3467c478bd9Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
3477c478bd9Sstevel@tonic-gate 		fpp = &((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 	ASSERT(*fpp == NULL);
3507c478bd9Sstevel@tonic-gate 
351fa52d01bScindi 	mutex_enter(&fcp->fc_free_lock);
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	/* Get an entry from the free list */
3547c478bd9Sstevel@tonic-gate 	fep = fcp->fc_free;
3557c478bd9Sstevel@tonic-gate 	if (fep == NULL) {
3567c478bd9Sstevel@tonic-gate 		if (fmc_grow(fcp, flag,
3577c478bd9Sstevel@tonic-gate 		    (flag == ACC_HANDLE ? default_acccache_sz :
3587c478bd9Sstevel@tonic-gate 		    default_dmacache_sz)) != 0) {
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 			/* Unable to get an entry or grow this cache */
3617c478bd9Sstevel@tonic-gate 			atomic_add_64(
3627c478bd9Sstevel@tonic-gate 			    &fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1);
363fa52d01bScindi 			mutex_exit(&fcp->fc_free_lock);
3647c478bd9Sstevel@tonic-gate 			return;
3657c478bd9Sstevel@tonic-gate 		}
3667c478bd9Sstevel@tonic-gate 		atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1);
3677c478bd9Sstevel@tonic-gate 		fep = fcp->fc_free;
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 	fcp->fc_free = fep->fce_prev;
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	/*
3727c478bd9Sstevel@tonic-gate 	 * Set-up the handle resource and bus_specific information.
3737c478bd9Sstevel@tonic-gate 	 * Also remember the pointer back to the cache for quick removal.
3747c478bd9Sstevel@tonic-gate 	 */
3757c478bd9Sstevel@tonic-gate 	fep->fce_bus_specific = bus_specific;
3767c478bd9Sstevel@tonic-gate 	fep->fce_resource = resource;
3777c478bd9Sstevel@tonic-gate 	fep->fce_next = NULL;
3787c478bd9Sstevel@tonic-gate 	*fpp = fep;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	/* Add entry to the end of the active list */
381fa52d01bScindi 	mutex_enter(&fcp->fc_lock);
3827c478bd9Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_tail;
3837c478bd9Sstevel@tonic-gate 	fcp->fc_tail->fce_next = fep;
3847c478bd9Sstevel@tonic-gate 	fcp->fc_tail = fep;
3857c478bd9Sstevel@tonic-gate 	mutex_exit(&fcp->fc_lock);
386*6b804b7dSstephh 	mutex_exit(&fcp->fc_free_lock);
3877c478bd9Sstevel@tonic-gate }
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate /*
3907c478bd9Sstevel@tonic-gate  * 	Remove an entry from the specified cache of access or dma mappings
3917c478bd9Sstevel@tonic-gate  *
3927c478bd9Sstevel@tonic-gate  * 	This function must be called at or below LOCK_LEVEL.
3937c478bd9Sstevel@tonic-gate  */
3947c478bd9Sstevel@tonic-gate void
3957c478bd9Sstevel@tonic-gate ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
3967c478bd9Sstevel@tonic-gate {
3977c478bd9Sstevel@tonic-gate 	ndi_fmc_t *fcp;
3987c478bd9Sstevel@tonic-gate 	ndi_fmcentry_t *fep;
3997c478bd9Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
4007c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	ASSERT(devi);
4037c478bd9Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	fmhdl = devi->devi_fmhdl;
4067c478bd9Sstevel@tonic-gate 	if (fmhdl == NULL) {
4077c478bd9Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
4087c478bd9Sstevel@tonic-gate 		return;
4097c478bd9Sstevel@tonic-gate 	}
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	/* Find cache entry pointer for this resource */
4127c478bd9Sstevel@tonic-gate 	if (flag == DMA_HANDLE) {
4137c478bd9Sstevel@tonic-gate 		if (!DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
4147c478bd9Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
4157c478bd9Sstevel@tonic-gate 			    DDI_NOSLEEP);
4167c478bd9Sstevel@tonic-gate 			return;
4177c478bd9Sstevel@tonic-gate 		}
4187c478bd9Sstevel@tonic-gate 		fcp = fmhdl->fh_dma_cache;
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 		ASSERT(fcp);
4217c478bd9Sstevel@tonic-gate 
4223c9b99a0Scindi 		mutex_enter(&fcp->fc_free_lock);
4237c478bd9Sstevel@tonic-gate 		fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
4247c478bd9Sstevel@tonic-gate 		((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
4257c478bd9Sstevel@tonic-gate 	} else if (flag == ACC_HANDLE) {
4267c478bd9Sstevel@tonic-gate 		if (!DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
4277c478bd9Sstevel@tonic-gate 			i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL,
4287c478bd9Sstevel@tonic-gate 			    DDI_NOSLEEP);
4297c478bd9Sstevel@tonic-gate 			return;
4307c478bd9Sstevel@tonic-gate 		}
4317c478bd9Sstevel@tonic-gate 		fcp = fmhdl->fh_acc_cache;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 		ASSERT(fcp);
4347c478bd9Sstevel@tonic-gate 
4353c9b99a0Scindi 		mutex_enter(&fcp->fc_free_lock);
4367c478bd9Sstevel@tonic-gate 		fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
4377c478bd9Sstevel@tonic-gate 		((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
4383c9b99a0Scindi 	} else {
4393c9b99a0Scindi 		return;
4407c478bd9Sstevel@tonic-gate 	}
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	/*
4437c478bd9Sstevel@tonic-gate 	 * Resource not in cache, return
4447c478bd9Sstevel@tonic-gate 	 */
4453c9b99a0Scindi 	if (fep == NULL) {
4463c9b99a0Scindi 		mutex_exit(&fcp->fc_free_lock);
4477c478bd9Sstevel@tonic-gate 		return;
4483c9b99a0Scindi 	}
4497c478bd9Sstevel@tonic-gate 
4503c9b99a0Scindi 	/*
4513c9b99a0Scindi 	 * Updates to FM cache pointers require us to grab fmc_lock
4523c9b99a0Scindi 	 * to synchronize access to the cache for ndi_fmc_insert()
4533c9b99a0Scindi 	 * and ndi_fmc_error()
4543c9b99a0Scindi 	 */
455fa52d01bScindi 	mutex_enter(&fcp->fc_lock);
4567c478bd9Sstevel@tonic-gate 	fep->fce_prev->fce_next = fep->fce_next;
4577c478bd9Sstevel@tonic-gate 	if (fep == fcp->fc_tail)
4587c478bd9Sstevel@tonic-gate 		fcp->fc_tail = fep->fce_prev;
4597c478bd9Sstevel@tonic-gate 	else
4607c478bd9Sstevel@tonic-gate 		fep->fce_next->fce_prev = fep->fce_prev;
461fa52d01bScindi 	mutex_exit(&fcp->fc_lock);
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	/* Add entry back to the free list */
4647c478bd9Sstevel@tonic-gate 	fep->fce_prev = fcp->fc_free;
4657c478bd9Sstevel@tonic-gate 	fcp->fc_free = fep;
466fa52d01bScindi 	mutex_exit(&fcp->fc_free_lock);
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate 
46900d0963fSdilpreet int
47000d0963fSdilpreet ndi_fmc_entry_error(dev_info_t *dip, int flag, ddi_fm_error_t *derr,
47100d0963fSdilpreet     const void *bus_err_state)
47200d0963fSdilpreet {
47300d0963fSdilpreet 	int status, fatal = 0, nonfatal = 0;
47400d0963fSdilpreet 	ndi_fmc_t *fcp = NULL;
47500d0963fSdilpreet 	ndi_fmcentry_t *fep;
47600d0963fSdilpreet 	struct i_ddi_fmhdl *fmhdl;
47700d0963fSdilpreet 
47800d0963fSdilpreet 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
47900d0963fSdilpreet 
48000d0963fSdilpreet 	fmhdl = DEVI(dip)->devi_fmhdl;
48100d0963fSdilpreet 	ASSERT(fmhdl);
48200d0963fSdilpreet 	status = DDI_FM_UNKNOWN;
48300d0963fSdilpreet 
48400d0963fSdilpreet 	if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
48500d0963fSdilpreet 		fcp = fmhdl->fh_dma_cache;
48600d0963fSdilpreet 		ASSERT(fcp);
48700d0963fSdilpreet 	} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
48800d0963fSdilpreet 		fcp = fmhdl->fh_acc_cache;
48900d0963fSdilpreet 		ASSERT(fcp);
49000d0963fSdilpreet 	}
49100d0963fSdilpreet 
49200d0963fSdilpreet 	if (fcp != NULL) {
49300d0963fSdilpreet 
49400d0963fSdilpreet 		/*
49500d0963fSdilpreet 		 * Check active resource entries
49600d0963fSdilpreet 		 */
49700d0963fSdilpreet 		mutex_enter(&fcp->fc_lock);
49800d0963fSdilpreet 		for (fep = fcp->fc_active->fce_next; fep != NULL;
49900d0963fSdilpreet 		    fep = fep->fce_next) {
50000d0963fSdilpreet 			ddi_fmcompare_t compare_func;
50100d0963fSdilpreet 
50200d0963fSdilpreet 			/*
50300d0963fSdilpreet 			 * Compare captured error state with handle
50400d0963fSdilpreet 			 * resources.  During the comparison and
50500d0963fSdilpreet 			 * subsequent error handling, we block
50600d0963fSdilpreet 			 * attempts to free the cache entry.
50700d0963fSdilpreet 			 */
50800d0963fSdilpreet 			compare_func = (flag == ACC_HANDLE) ?
50900d0963fSdilpreet 			    i_ddi_fm_acc_err_cf_get((ddi_acc_handle_t)
51000d0963fSdilpreet 			    fep->fce_resource) :
51100d0963fSdilpreet 			    i_ddi_fm_dma_err_cf_get((ddi_dma_handle_t)
51200d0963fSdilpreet 			    fep->fce_resource);
51300d0963fSdilpreet 
51400d0963fSdilpreet 			status = compare_func(dip, fep->fce_resource,
51500d0963fSdilpreet 			    bus_err_state, fep->fce_bus_specific);
51600d0963fSdilpreet 			if (status == DDI_FM_UNKNOWN || status == DDI_FM_OK)
51700d0963fSdilpreet 				continue;
51800d0963fSdilpreet 
51900d0963fSdilpreet 			if (status == DDI_FM_FATAL)
52000d0963fSdilpreet 				++fatal;
52100d0963fSdilpreet 			else if (status == DDI_FM_NONFATAL)
52200d0963fSdilpreet 				++nonfatal;
52300d0963fSdilpreet 
52400d0963fSdilpreet 			/* Set the error for this resource handle */
52500d0963fSdilpreet 			if (flag == ACC_HANDLE) {
52600d0963fSdilpreet 				ddi_acc_handle_t ap = fep->fce_resource;
52700d0963fSdilpreet 
52800d0963fSdilpreet 				i_ddi_fm_acc_err_set(ap, derr->fme_ena, status,
52900d0963fSdilpreet 				    DDI_FM_ERR_UNEXPECTED);
53000d0963fSdilpreet 				ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
53100d0963fSdilpreet 				derr->fme_acc_handle = ap;
53200d0963fSdilpreet 			} else {
53300d0963fSdilpreet 				ddi_dma_handle_t dp = fep->fce_resource;
53400d0963fSdilpreet 
53500d0963fSdilpreet 				i_ddi_fm_dma_err_set(dp, derr->fme_ena, status,
53600d0963fSdilpreet 				    DDI_FM_ERR_UNEXPECTED);
53700d0963fSdilpreet 				ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
53800d0963fSdilpreet 				derr->fme_dma_handle = dp;
53900d0963fSdilpreet 			}
54000d0963fSdilpreet 			break;
54100d0963fSdilpreet 		}
54200d0963fSdilpreet 		mutex_exit(&fcp->fc_lock);
54300d0963fSdilpreet 	}
54400d0963fSdilpreet 	return (fatal ? DDI_FM_FATAL : nonfatal ? DDI_FM_NONFATAL :
54500d0963fSdilpreet 	    DDI_FM_UNKNOWN);
54600d0963fSdilpreet }
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate /*
5497c478bd9Sstevel@tonic-gate  * Check error state against the handle resource stored in the specified
5507c478bd9Sstevel@tonic-gate  * FM cache.  If tdip != NULL, we check only the cache entries for tdip.
5517c478bd9Sstevel@tonic-gate  * The caller must ensure that tdip is valid throughout the call and
5527c478bd9Sstevel@tonic-gate  * all FM data structures can be safely accesses.
5537c478bd9Sstevel@tonic-gate  *
5547c478bd9Sstevel@tonic-gate  * If tdip == NULL, we check all children that have registered their
5557c478bd9Sstevel@tonic-gate  * FM_DMA_CHK or FM_ACC_CHK capabilities.
5567c478bd9Sstevel@tonic-gate  *
5577c478bd9Sstevel@tonic-gate  * The following status values may be returned:
5587c478bd9Sstevel@tonic-gate  *
5597c478bd9Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one cache entry comparison yields a
5607c478bd9Sstevel@tonic-gate  *			fatal error.
5617c478bd9Sstevel@tonic-gate  *
5627c478bd9Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one cache entry comparison yields a
5637c478bd9Sstevel@tonic-gate  *			non-fatal error and no comparison yields a fatal error.
5647c478bd9Sstevel@tonic-gate  *
5657c478bd9Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - cache entry comparisons did not yield fatal or
5667c478bd9Sstevel@tonic-gate  *			non-fatal errors.
5677c478bd9Sstevel@tonic-gate  *
5687c478bd9Sstevel@tonic-gate  */
5697c478bd9Sstevel@tonic-gate int
57000d0963fSdilpreet ndi_fmc_error(dev_info_t *dip, dev_info_t *tdip, int flag, uint64_t ena,
57100d0963fSdilpreet     const void *bus_err_state)
5727c478bd9Sstevel@tonic-gate {
5737c478bd9Sstevel@tonic-gate 	int status, fatal = 0, nonfatal = 0;
5747c478bd9Sstevel@tonic-gate 	ddi_fm_error_t derr;
5757c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
5767c478bd9Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
5817c478bd9Sstevel@tonic-gate 	fmhdl = DEVI(dip)->devi_fmhdl;
5827c478bd9Sstevel@tonic-gate 	ASSERT(fmhdl);
58300d0963fSdilpreet 
58400d0963fSdilpreet 	bzero(&derr, sizeof (ddi_fm_error_t));
58500d0963fSdilpreet 	derr.fme_version = DDI_FME_VERSION;
58600d0963fSdilpreet 	derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
58700d0963fSdilpreet 	derr.fme_ena = ena;
58800d0963fSdilpreet 
5897c478bd9Sstevel@tonic-gate 	for (tgt = fmhdl->fh_tgts; tgt != NULL; tgt = tgt->ft_next) {
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		if (tdip != NULL && tdip != tgt->ft_dip)
5927c478bd9Sstevel@tonic-gate 			continue;
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 		/*
59500d0963fSdilpreet 		 * Attempt to find the entry in this childs handle cache
5967c478bd9Sstevel@tonic-gate 		 */
59700d0963fSdilpreet 		status = ndi_fmc_entry_error(tgt->ft_dip, flag, &derr,
59800d0963fSdilpreet 		    bus_err_state);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 		if (status == DDI_FM_FATAL)
6017c478bd9Sstevel@tonic-gate 			++fatal;
6027c478bd9Sstevel@tonic-gate 		else if (status == DDI_FM_NONFATAL)
6037c478bd9Sstevel@tonic-gate 			++nonfatal;
60400d0963fSdilpreet 		else
60500d0963fSdilpreet 			continue;
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 		/*
6087c478bd9Sstevel@tonic-gate 		 * Call our child to process this error.
6097c478bd9Sstevel@tonic-gate 		 */
61000d0963fSdilpreet 		status = tgt->ft_errhdl->eh_func(tgt->ft_dip, &derr,
61100d0963fSdilpreet 		    tgt->ft_errhdl->eh_impl);
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 		if (status == DDI_FM_FATAL)
6147c478bd9Sstevel@tonic-gate 			++fatal;
6157c478bd9Sstevel@tonic-gate 		else if (status == DDI_FM_NONFATAL)
6167c478bd9Sstevel@tonic-gate 			++nonfatal;
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	if (fatal)
6227c478bd9Sstevel@tonic-gate 		return (DDI_FM_FATAL);
6237c478bd9Sstevel@tonic-gate 	else if (nonfatal)
6247c478bd9Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	return (DDI_FM_UNKNOWN);
6277c478bd9Sstevel@tonic-gate }
6287c478bd9Sstevel@tonic-gate 
6298aec9182Sstephh int
6308aec9182Sstephh ndi_fmc_entry_error_all(dev_info_t *dip, int flag, ddi_fm_error_t *derr)
6318aec9182Sstephh {
6328aec9182Sstephh 	ndi_fmc_t *fcp = NULL;
6338aec9182Sstephh 	ndi_fmcentry_t *fep;
6348aec9182Sstephh 	struct i_ddi_fmhdl *fmhdl;
6358aec9182Sstephh 	int nonfatal = 0;
6368aec9182Sstephh 
6378aec9182Sstephh 	ASSERT(flag == DMA_HANDLE || flag == ACC_HANDLE);
6388aec9182Sstephh 
6398aec9182Sstephh 	fmhdl = DEVI(dip)->devi_fmhdl;
6408aec9182Sstephh 	ASSERT(fmhdl);
6418aec9182Sstephh 
6428aec9182Sstephh 	if (flag == DMA_HANDLE && DDI_FM_DMA_ERR_CAP(fmhdl->fh_cap)) {
6438aec9182Sstephh 		fcp = fmhdl->fh_dma_cache;
6448aec9182Sstephh 		ASSERT(fcp);
6458aec9182Sstephh 	} else if (flag == ACC_HANDLE && DDI_FM_ACC_ERR_CAP(fmhdl->fh_cap)) {
6468aec9182Sstephh 		fcp = fmhdl->fh_acc_cache;
6478aec9182Sstephh 		ASSERT(fcp);
6488aec9182Sstephh 	}
6498aec9182Sstephh 
6508aec9182Sstephh 	if (fcp != NULL) {
6518aec9182Sstephh 		/*
6528aec9182Sstephh 		 * Check active resource entries
6538aec9182Sstephh 		 */
6548aec9182Sstephh 		mutex_enter(&fcp->fc_lock);
6558aec9182Sstephh 		for (fep = fcp->fc_active->fce_next; fep != NULL;
6568aec9182Sstephh 		    fep = fep->fce_next) {
6578aec9182Sstephh 			/* Set the error for this resource handle */
6588aec9182Sstephh 			nonfatal++;
6598aec9182Sstephh 			if (flag == ACC_HANDLE) {
6608aec9182Sstephh 				ddi_acc_handle_t ap = fep->fce_resource;
6618aec9182Sstephh 
6628aec9182Sstephh 				i_ddi_fm_acc_err_set(ap, derr->fme_ena,
6638aec9182Sstephh 				    DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
6648aec9182Sstephh 				ddi_fm_acc_err_get(ap, derr, DDI_FME_VERSION);
6658aec9182Sstephh 				derr->fme_acc_handle = ap;
6668aec9182Sstephh 			} else {
6678aec9182Sstephh 				ddi_dma_handle_t dp = fep->fce_resource;
6688aec9182Sstephh 
6698aec9182Sstephh 				i_ddi_fm_dma_err_set(dp, derr->fme_ena,
6708aec9182Sstephh 				    DDI_FM_NONFATAL, DDI_FM_ERR_UNEXPECTED);
6718aec9182Sstephh 				ddi_fm_dma_err_get(dp, derr, DDI_FME_VERSION);
6728aec9182Sstephh 				derr->fme_dma_handle = dp;
6738aec9182Sstephh 			}
6748aec9182Sstephh 		}
6758aec9182Sstephh 		mutex_exit(&fcp->fc_lock);
6768aec9182Sstephh 	}
6778aec9182Sstephh 	return (nonfatal ? DDI_FM_NONFATAL : DDI_FM_UNKNOWN);
6788aec9182Sstephh }
6798aec9182Sstephh 
6807c478bd9Sstevel@tonic-gate /*
6817c478bd9Sstevel@tonic-gate  * Dispatch registered error handlers for dip.  If tdip != NULL, only
6827c478bd9Sstevel@tonic-gate  * the error handler (if available) for tdip is invoked.  Otherwise,
6837c478bd9Sstevel@tonic-gate  * all registered error handlers are invoked.
6847c478bd9Sstevel@tonic-gate  *
6857c478bd9Sstevel@tonic-gate  * The following status values may be returned:
6867c478bd9Sstevel@tonic-gate  *
6877c478bd9Sstevel@tonic-gate  *	DDI_FM_FATAL - if at least one error handler returns a
6887c478bd9Sstevel@tonic-gate  *			fatal error.
6897c478bd9Sstevel@tonic-gate  *
6907c478bd9Sstevel@tonic-gate  *	DDI_FM_NONFATAL - if at least one error handler returns a
6917c478bd9Sstevel@tonic-gate  *			non-fatal error and none returned a fatal error.
6927c478bd9Sstevel@tonic-gate  *
6937c478bd9Sstevel@tonic-gate  *	DDI_FM_UNKNOWN - if at least one error handler returns
6947c478bd9Sstevel@tonic-gate  *			unknown status and none return fatal or non-fatal.
6957c478bd9Sstevel@tonic-gate  *
6967c478bd9Sstevel@tonic-gate  *	DDI_FM_OK - if all error handlers return DDI_FM_OK
6977c478bd9Sstevel@tonic-gate  */
6987c478bd9Sstevel@tonic-gate int
6997c478bd9Sstevel@tonic-gate ndi_fm_handler_dispatch(dev_info_t *dip, dev_info_t *tdip,
7007c478bd9Sstevel@tonic-gate     const ddi_fm_error_t *nerr)
7017c478bd9Sstevel@tonic-gate {
7027c478bd9Sstevel@tonic-gate 	int status;
7037c478bd9Sstevel@tonic-gate 	int unknown = 0, fatal = 0, nonfatal = 0;
7047c478bd9Sstevel@tonic-gate 	struct i_ddi_fmhdl *hdl;
7057c478bd9Sstevel@tonic-gate 	struct i_ddi_fmtgt *tgt;
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	status = DDI_FM_UNKNOWN;
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_enter(dip);
7107c478bd9Sstevel@tonic-gate 	hdl = DEVI(dip)->devi_fmhdl;
7117c478bd9Sstevel@tonic-gate 	tgt = hdl->fh_tgts;
7127c478bd9Sstevel@tonic-gate 	while (tgt != NULL) {
7137c478bd9Sstevel@tonic-gate 		if (tdip == NULL || tdip == tgt->ft_dip) {
7147c478bd9Sstevel@tonic-gate 			struct i_ddi_errhdl *errhdl;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 			errhdl = tgt->ft_errhdl;
7177c478bd9Sstevel@tonic-gate 			status = errhdl->eh_func(tgt->ft_dip, nerr,
7187c478bd9Sstevel@tonic-gate 			    errhdl->eh_impl);
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 			if (status == DDI_FM_FATAL)
7217c478bd9Sstevel@tonic-gate 				++fatal;
7227c478bd9Sstevel@tonic-gate 			else if (status == DDI_FM_NONFATAL)
7237c478bd9Sstevel@tonic-gate 				++nonfatal;
7247c478bd9Sstevel@tonic-gate 			else if (status == DDI_FM_UNKNOWN)
7257c478bd9Sstevel@tonic-gate 				++unknown;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 			/* Only interested in one target */
7287c478bd9Sstevel@tonic-gate 			if (tdip != NULL)
7297c478bd9Sstevel@tonic-gate 				break;
7307c478bd9Sstevel@tonic-gate 		}
7317c478bd9Sstevel@tonic-gate 		tgt = tgt->ft_next;
7327c478bd9Sstevel@tonic-gate 	}
7337c478bd9Sstevel@tonic-gate 	i_ddi_fm_handler_exit(dip);
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	if (fatal)
7367c478bd9Sstevel@tonic-gate 		return (DDI_FM_FATAL);
7377c478bd9Sstevel@tonic-gate 	else if (nonfatal)
7387c478bd9Sstevel@tonic-gate 		return (DDI_FM_NONFATAL);
7397c478bd9Sstevel@tonic-gate 	else if (unknown)
7407c478bd9Sstevel@tonic-gate 		return (DDI_FM_UNKNOWN);
7417c478bd9Sstevel@tonic-gate 	else
7427c478bd9Sstevel@tonic-gate 		return (DDI_FM_OK);
7437c478bd9Sstevel@tonic-gate }
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate /*
7467c478bd9Sstevel@tonic-gate  * Set error status for specified access or DMA handle
7477c478bd9Sstevel@tonic-gate  *
7487c478bd9Sstevel@tonic-gate  * May be called in any context but caller must insure validity of
7497c478bd9Sstevel@tonic-gate  * handle.
7507c478bd9Sstevel@tonic-gate  */
7517c478bd9Sstevel@tonic-gate void
7527c478bd9Sstevel@tonic-gate ndi_fm_acc_err_set(ddi_acc_handle_t handle, ddi_fm_error_t *dfe)
7537c478bd9Sstevel@tonic-gate {
7547c478bd9Sstevel@tonic-gate 	i_ddi_fm_acc_err_set(handle, dfe->fme_ena, dfe->fme_status,
7557c478bd9Sstevel@tonic-gate 	    dfe->fme_flag);
7567c478bd9Sstevel@tonic-gate }
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate void
7597c478bd9Sstevel@tonic-gate ndi_fm_dma_err_set(ddi_dma_handle_t handle, ddi_fm_error_t *dfe)
7607c478bd9Sstevel@tonic-gate {
7617c478bd9Sstevel@tonic-gate 	i_ddi_fm_dma_err_set(handle, dfe->fme_ena, dfe->fme_status,
7627c478bd9Sstevel@tonic-gate 	    dfe->fme_flag);
7637c478bd9Sstevel@tonic-gate }
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate /*
7667c478bd9Sstevel@tonic-gate  * Call parent busop fm initialization routine.
7677c478bd9Sstevel@tonic-gate  *
7687c478bd9Sstevel@tonic-gate  * Called during driver attach(1M)
7697c478bd9Sstevel@tonic-gate  */
7707c478bd9Sstevel@tonic-gate int
7717c478bd9Sstevel@tonic-gate i_ndi_busop_fm_init(dev_info_t *dip, int tcap, ddi_iblock_cookie_t *ibc)
7727c478bd9Sstevel@tonic-gate {
7737c478bd9Sstevel@tonic-gate 	int pcap;
7747c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	if (dip == ddi_root_node())
7777c478bd9Sstevel@tonic-gate 		return (ddi_system_fmcap | DDI_FM_EREPORT_CAPABLE);
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
7807c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
7817c478bd9Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init == NULL)
7847c478bd9Sstevel@tonic-gate 		return (DDI_FM_NOT_CAPABLE);
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 	pcap = (*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_init)
7877c478bd9Sstevel@tonic-gate 	    (pdip, dip, tcap, ibc);
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	return (pcap);
7907c478bd9Sstevel@tonic-gate }
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate /*
7937c478bd9Sstevel@tonic-gate  * Call parent busop fm clean-up routine.
7947c478bd9Sstevel@tonic-gate  *
7957c478bd9Sstevel@tonic-gate  * Called during driver detach(1M)
7967c478bd9Sstevel@tonic-gate  */
7977c478bd9Sstevel@tonic-gate void
7987c478bd9Sstevel@tonic-gate i_ndi_busop_fm_fini(dev_info_t *dip)
7997c478bd9Sstevel@tonic-gate {
8007c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	if (dip == ddi_root_node())
8037c478bd9Sstevel@tonic-gate 		return;
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
8067c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
8077c478bd9Sstevel@tonic-gate 		return;
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini == NULL)
8107c478bd9Sstevel@tonic-gate 		return;
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_fini)(pdip, dip);
8137c478bd9Sstevel@tonic-gate }
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate /*
8167c478bd9Sstevel@tonic-gate  * The following routines provide exclusive access to a nexus resource
8177c478bd9Sstevel@tonic-gate  *
8187c478bd9Sstevel@tonic-gate  * These busops may be called in user or kernel driver context.
8197c478bd9Sstevel@tonic-gate  */
8207c478bd9Sstevel@tonic-gate void
8217c478bd9Sstevel@tonic-gate i_ndi_busop_access_enter(dev_info_t *dip, ddi_acc_handle_t handle)
8227c478bd9Sstevel@tonic-gate {
8237c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
8267c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
8277c478bd9Sstevel@tonic-gate 		return;
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter == NULL)
8307c478bd9Sstevel@tonic-gate 		return;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_enter)
8337c478bd9Sstevel@tonic-gate 	    (pdip, handle);
8347c478bd9Sstevel@tonic-gate }
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate void
8377c478bd9Sstevel@tonic-gate i_ndi_busop_access_exit(dev_info_t *dip, ddi_acc_handle_t handle)
8387c478bd9Sstevel@tonic-gate {
8397c478bd9Sstevel@tonic-gate 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 	/* Valid operation for BUSO_REV_6 and above */
8427c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_6)
8437c478bd9Sstevel@tonic-gate 		return;
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate 	if (DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit == NULL)
8467c478bd9Sstevel@tonic-gate 		return;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	(*DEVI(pdip)->devi_ops->devo_bus_ops->bus_fm_access_exit)(pdip, handle);
8497c478bd9Sstevel@tonic-gate }
850