xref: /titanic_50/usr/src/uts/intel/io/iommulib.c (revision d8fc7d072b750ad0603730cafd1e016769ea8ee1)
120906b23SVikram Hegde /*
220906b23SVikram Hegde  * CDDL HEADER START
320906b23SVikram Hegde  *
420906b23SVikram Hegde  * The contents of this file are subject to the terms of the
520906b23SVikram Hegde  * Common Development and Distribution License (the "License").
620906b23SVikram Hegde  * You may not use this file except in compliance with the License.
720906b23SVikram Hegde  *
820906b23SVikram Hegde  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
920906b23SVikram Hegde  * or http://www.opensolaris.org/os/licensing.
1020906b23SVikram Hegde  * See the License for the specific language governing permissions
1120906b23SVikram Hegde  * and limitations under the License.
1220906b23SVikram Hegde  *
1320906b23SVikram Hegde  * When distributing Covered Code, include this CDDL HEADER in each
1420906b23SVikram Hegde  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1520906b23SVikram Hegde  * If applicable, add the following below this CDDL HEADER, with the
1620906b23SVikram Hegde  * fields enclosed by brackets "[]" replaced with your own identifying
1720906b23SVikram Hegde  * information: Portions Copyright [yyyy] [name of copyright owner]
1820906b23SVikram Hegde  *
1920906b23SVikram Hegde  * CDDL HEADER END
2020906b23SVikram Hegde  */
2120906b23SVikram Hegde /*
2220906b23SVikram Hegde  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2320906b23SVikram Hegde  * Use is subject to license terms.
2420906b23SVikram Hegde  */
2520906b23SVikram Hegde 
2620906b23SVikram Hegde #pragma ident	"@(#)iommulib.c	1.6	08/09/07 SMI"
2720906b23SVikram Hegde 
2820906b23SVikram Hegde #include <sys/sunddi.h>
2920906b23SVikram Hegde #include <sys/sunndi.h>
3020906b23SVikram Hegde #include <sys/errno.h>
3120906b23SVikram Hegde #include <sys/modctl.h>
3220906b23SVikram Hegde #include <sys/iommulib.h>
3320906b23SVikram Hegde 
3420906b23SVikram Hegde /* ******** Type definitions private to this file  ********************** */
3520906b23SVikram Hegde 
3620906b23SVikram Hegde /* 1 per IOMMU unit. There may be more than one per dip */
3720906b23SVikram Hegde typedef struct iommulib_unit {
3820906b23SVikram Hegde 	kmutex_t ilu_lock;
3920906b23SVikram Hegde 	uint64_t ilu_ref;
4020906b23SVikram Hegde 	uint32_t ilu_unitid;
4120906b23SVikram Hegde 	dev_info_t *ilu_dip;
4220906b23SVikram Hegde 	iommulib_ops_t *ilu_ops;
4320906b23SVikram Hegde 	void* ilu_data;
4420906b23SVikram Hegde 	struct iommulib_unit *ilu_next;
4520906b23SVikram Hegde 	struct iommulib_unit *ilu_prev;
4620906b23SVikram Hegde } iommulib_unit_t;
4720906b23SVikram Hegde 
4820906b23SVikram Hegde typedef struct iommulib_cache {
4920906b23SVikram Hegde 	dev_info_t *cache_rdip;
5020906b23SVikram Hegde 	iommulib_unit_t *cache_unit;
5120906b23SVikram Hegde 	struct iommulib_cache *cache_next;
5220906b23SVikram Hegde 	struct iommulib_cache *cache_prev;
5320906b23SVikram Hegde } iommulib_cache_t;
5420906b23SVikram Hegde 
5520906b23SVikram Hegde typedef struct iommulib_nex {
5620906b23SVikram Hegde 	dev_info_t *nex_dip;
5720906b23SVikram Hegde 	iommulib_nexops_t nex_ops;
5820906b23SVikram Hegde 	struct iommulib_nex *nex_next;
5920906b23SVikram Hegde 	struct iommulib_nex *nex_prev;
6020906b23SVikram Hegde } iommulib_nex_t;
6120906b23SVikram Hegde 
6220906b23SVikram Hegde /* ********* Function prototypes ********************* */
6320906b23SVikram Hegde static int lookup_cache(dev_info_t *rdip, iommulib_unit_t **unitpp);
6420906b23SVikram Hegde static void insert_cache(dev_info_t *rdip, iommulib_unit_t *unitp);
6520906b23SVikram Hegde 
6620906b23SVikram Hegde 
6720906b23SVikram Hegde /* *********  Globals ************************ */
6820906b23SVikram Hegde 
6920906b23SVikram Hegde /* IOMMU side: Following data protected by lock */
7020906b23SVikram Hegde static kmutex_t iommulib_lock;
7120906b23SVikram Hegde static iommulib_unit_t   *iommulib_list;
7220906b23SVikram Hegde static uint64_t iommulib_unit_ids = 0;
7320906b23SVikram Hegde static uint64_t iommulib_num_units = 0;
7420906b23SVikram Hegde 
7520906b23SVikram Hegde /* rootnex side data */
7620906b23SVikram Hegde 
7720906b23SVikram Hegde static kmutex_t iommulib_nexus_lock;
7820906b23SVikram Hegde static iommulib_nex_t *iommulib_nexus_list;
7920906b23SVikram Hegde 
8020906b23SVikram Hegde #define	IOMMULIB_CACHE_SIZE 256
8120906b23SVikram Hegde static kmutex_t iommulib_cache_lock;
8220906b23SVikram Hegde static iommulib_cache_t **iommulib_cache;
8320906b23SVikram Hegde 
8420906b23SVikram Hegde /* tunable via /etc/system */
8520906b23SVikram Hegde static uint_t iommulib_cache_size = IOMMULIB_CACHE_SIZE;
8620906b23SVikram Hegde 
8720906b23SVikram Hegde /* can be set atomically without lock */
8820906b23SVikram Hegde static volatile uint32_t iommulib_fini;
8920906b23SVikram Hegde 
9020906b23SVikram Hegde /* debug flag */
9120906b23SVikram Hegde static int iommulib_debug;
9220906b23SVikram Hegde 
9320906b23SVikram Hegde /*
9420906b23SVikram Hegde  * Module linkage information for the kernel.
9520906b23SVikram Hegde  */
9620906b23SVikram Hegde static struct modlmisc modlmisc = {
9720906b23SVikram Hegde 	&mod_miscops, "IOMMU library module"
9820906b23SVikram Hegde };
9920906b23SVikram Hegde 
10020906b23SVikram Hegde static struct modlinkage modlinkage = {
10120906b23SVikram Hegde 	MODREV_1, (void *)&modlmisc, NULL
10220906b23SVikram Hegde };
10320906b23SVikram Hegde 
10420906b23SVikram Hegde int
10520906b23SVikram Hegde _init(void)
10620906b23SVikram Hegde {
10720906b23SVikram Hegde 	/*
10820906b23SVikram Hegde 	 * static mutexes automagically initialized
10920906b23SVikram Hegde 	 * by being allocated in zeroed memory
11020906b23SVikram Hegde 	 */
11120906b23SVikram Hegde 	mutex_enter(&iommulib_cache_lock);
11220906b23SVikram Hegde 	iommulib_cache = kmem_zalloc(
11320906b23SVikram Hegde 	    sizeof (iommulib_cache_t *) * iommulib_cache_size, KM_SLEEP);
11420906b23SVikram Hegde 	mutex_exit(&iommulib_cache_lock);
11520906b23SVikram Hegde 
11620906b23SVikram Hegde 	return (mod_install(&modlinkage));
11720906b23SVikram Hegde }
11820906b23SVikram Hegde 
11920906b23SVikram Hegde int
12020906b23SVikram Hegde _fini(void)
12120906b23SVikram Hegde {
12220906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
12320906b23SVikram Hegde 	if (iommulib_list != NULL || iommulib_nexus_list != NULL) {
12420906b23SVikram Hegde 		mutex_exit(&iommulib_lock);
12520906b23SVikram Hegde 		return (EBUSY);
12620906b23SVikram Hegde 	}
12720906b23SVikram Hegde 	iommulib_fini = 1;
12820906b23SVikram Hegde 
12920906b23SVikram Hegde 	mutex_enter(&iommulib_cache_lock);
13020906b23SVikram Hegde 	kmem_free(iommulib_cache,
13120906b23SVikram Hegde 	    sizeof (iommulib_cache_t *) * iommulib_cache_size);
13220906b23SVikram Hegde 	iommulib_cache = NULL;
13320906b23SVikram Hegde 	mutex_exit(&iommulib_cache_lock);
13420906b23SVikram Hegde 
13520906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
13620906b23SVikram Hegde 	return (mod_remove(&modlinkage));
13720906b23SVikram Hegde }
13820906b23SVikram Hegde 
13920906b23SVikram Hegde int
14020906b23SVikram Hegde _info(struct modinfo *modinfop)
14120906b23SVikram Hegde {
14220906b23SVikram Hegde 	return (mod_info(&modlinkage, modinfop));
14320906b23SVikram Hegde }
14420906b23SVikram Hegde 
14520906b23SVikram Hegde /*
14620906b23SVikram Hegde  * Routines with iommulib_iommu_* are invoked from the
14720906b23SVikram Hegde  * IOMMU driver.
14820906b23SVikram Hegde  * Routines with iommulib_nex* are invoked from the
14920906b23SVikram Hegde  * nexus driver (typically rootnex)
15020906b23SVikram Hegde  */
15120906b23SVikram Hegde 
15220906b23SVikram Hegde int
15320906b23SVikram Hegde iommulib_nexus_register(dev_info_t *dip, iommulib_nexops_t *nexops,
15420906b23SVikram Hegde     iommulib_nexhandle_t *handle)
15520906b23SVikram Hegde {
15620906b23SVikram Hegde 	iommulib_nex_t *nexp;
15720906b23SVikram Hegde 	int instance = ddi_get_instance(dip);
15820906b23SVikram Hegde 	const char *driver = ddi_driver_name(dip);
15920906b23SVikram Hegde 	dev_info_t *pdip = ddi_get_parent(dip);
16020906b23SVikram Hegde 	const char *f = "iommulib_nexus_register";
16120906b23SVikram Hegde 
16220906b23SVikram Hegde 	ASSERT(nexops);
16320906b23SVikram Hegde 	ASSERT(handle);
16420906b23SVikram Hegde 
16520906b23SVikram Hegde 	*handle = NULL;
16620906b23SVikram Hegde 
16720906b23SVikram Hegde 	/*
16820906b23SVikram Hegde 	 * Root node is never busy held
16920906b23SVikram Hegde 	 */
17020906b23SVikram Hegde 	if (dip != ddi_root_node() && (i_ddi_node_state(dip) < DS_PROBED ||
17120906b23SVikram Hegde 	    !DEVI_BUSY_OWNED(pdip))) {
17220906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: NEXUS devinfo node not in DS_PROBED "
17320906b23SVikram Hegde 		    "or busy held for nexops vector (%p). Failing registration",
17420906b23SVikram Hegde 		    f, (void *)nexops);
17520906b23SVikram Hegde 		return (DDI_FAILURE);
17620906b23SVikram Hegde 	}
17720906b23SVikram Hegde 
17820906b23SVikram Hegde 	if (nexops->nops_vers != IOMMU_NEXOPS_VERSION) {
17920906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: Invalid IOMMULIB nexops version "
18020906b23SVikram Hegde 		    "in nexops vector (%p). Failing NEXUS registration",
18120906b23SVikram Hegde 		    f, driver, instance, (void *)nexops);
18220906b23SVikram Hegde 		return (DDI_FAILURE);
18320906b23SVikram Hegde 	}
18420906b23SVikram Hegde 
18520906b23SVikram Hegde 	ASSERT(nexops->nops_data == NULL);
18620906b23SVikram Hegde 
18720906b23SVikram Hegde 	if (nexops->nops_id == NULL) {
18820906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL ID field. "
18920906b23SVikram Hegde 		    "Failing registration for nexops vector: %p",
19020906b23SVikram Hegde 		    f, driver, instance, (void *)nexops);
19120906b23SVikram Hegde 		return (DDI_FAILURE);
19220906b23SVikram Hegde 	}
19320906b23SVikram Hegde 
19420906b23SVikram Hegde 	if (nexops->nops_dma_allochdl == NULL) {
19520906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_allochdl op. "
19620906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
19720906b23SVikram Hegde 		    driver, instance, (void *)nexops);
19820906b23SVikram Hegde 		return (DDI_FAILURE);
19920906b23SVikram Hegde 	}
20020906b23SVikram Hegde 
20120906b23SVikram Hegde 	if (nexops->nops_dma_freehdl == NULL) {
20220906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_freehdl op. "
20320906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
20420906b23SVikram Hegde 		    driver, instance, (void *)nexops);
20520906b23SVikram Hegde 		return (DDI_FAILURE);
20620906b23SVikram Hegde 	}
20720906b23SVikram Hegde 
20820906b23SVikram Hegde 	if (nexops->nops_dma_bindhdl == NULL) {
20920906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_bindhdl op. "
21020906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
21120906b23SVikram Hegde 		    driver, instance, (void *)nexops);
21220906b23SVikram Hegde 		return (DDI_FAILURE);
21320906b23SVikram Hegde 	}
21420906b23SVikram Hegde 
21520906b23SVikram Hegde 	if (nexops->nops_dma_sync == NULL) {
21620906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_sync op. "
21720906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
21820906b23SVikram Hegde 		    driver, instance, (void *)nexops);
21920906b23SVikram Hegde 		return (DDI_FAILURE);
22020906b23SVikram Hegde 	}
22120906b23SVikram Hegde 
22220906b23SVikram Hegde 
22320906b23SVikram Hegde 	if (nexops->nops_dma_reset_cookies == NULL) {
22420906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_reset_cookies op. "
22520906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
22620906b23SVikram Hegde 		    driver, instance, (void *)nexops);
22720906b23SVikram Hegde 		return (DDI_FAILURE);
22820906b23SVikram Hegde 	}
22920906b23SVikram Hegde 
23020906b23SVikram Hegde 	if (nexops->nops_dma_get_cookies == NULL) {
23120906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_get_cookies op. "
23220906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
23320906b23SVikram Hegde 		    driver, instance, (void *)nexops);
23420906b23SVikram Hegde 		return (DDI_FAILURE);
23520906b23SVikram Hegde 	}
23620906b23SVikram Hegde 
23720906b23SVikram Hegde 	if (nexops->nops_dma_win == NULL) {
23820906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_win op. "
23920906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
24020906b23SVikram Hegde 		    driver, instance, (void *)nexops);
24120906b23SVikram Hegde 		return (DDI_FAILURE);
24220906b23SVikram Hegde 	}
24320906b23SVikram Hegde 
24420906b23SVikram Hegde 	/* Check for legacy ops */
24520906b23SVikram Hegde 	if (nexops->nops_dma_map == NULL) {
24620906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL legacy nops_dma_map op. "
24720906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
24820906b23SVikram Hegde 		    driver, instance, (void *)nexops);
24920906b23SVikram Hegde 		return (DDI_FAILURE);
25020906b23SVikram Hegde 	}
25120906b23SVikram Hegde 
25220906b23SVikram Hegde 	if (nexops->nops_dma_mctl == NULL) {
25320906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL legacy nops_dma_mctl op. "
25420906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
25520906b23SVikram Hegde 		    driver, instance, (void *)nexops);
25620906b23SVikram Hegde 		return (DDI_FAILURE);
25720906b23SVikram Hegde 	}
25820906b23SVikram Hegde 
25920906b23SVikram Hegde 	nexp = kmem_zalloc(sizeof (iommulib_nex_t), KM_SLEEP);
26020906b23SVikram Hegde 
26120906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
26220906b23SVikram Hegde 	if (iommulib_fini == 1) {
26320906b23SVikram Hegde 		mutex_exit(&iommulib_lock);
26420906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: IOMMULIB unloading. "
26520906b23SVikram Hegde 		    "Failing NEXUS register.", f);
26620906b23SVikram Hegde 		kmem_free(nexp, sizeof (iommulib_nex_t));
26720906b23SVikram Hegde 		return (DDI_FAILURE);
26820906b23SVikram Hegde 	}
26920906b23SVikram Hegde 
27020906b23SVikram Hegde 	/*
27120906b23SVikram Hegde 	 * fini/register race conditions have been handled. Now create the
27220906b23SVikram Hegde 	 * nexus struct
27320906b23SVikram Hegde 	 */
27420906b23SVikram Hegde 	ndi_hold_devi(dip);
27520906b23SVikram Hegde 	nexp->nex_dip = dip;
27620906b23SVikram Hegde 	nexp->nex_ops = *nexops;
27720906b23SVikram Hegde 
27820906b23SVikram Hegde 	mutex_enter(&iommulib_nexus_lock);
27920906b23SVikram Hegde 	nexp->nex_next = iommulib_nexus_list;
28020906b23SVikram Hegde 	iommulib_nexus_list = nexp;
28120906b23SVikram Hegde 	nexp->nex_prev = NULL;
28220906b23SVikram Hegde 
28320906b23SVikram Hegde 	if (nexp->nex_next != NULL)
28420906b23SVikram Hegde 		nexp->nex_next->nex_prev = nexp;
28520906b23SVikram Hegde 
28620906b23SVikram Hegde 	mutex_exit(&iommulib_nexus_lock);
28720906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
28820906b23SVikram Hegde 
28920906b23SVikram Hegde 	cmn_err(CE_NOTE, "!%s: %s%d: Succesfully registered NEXUS %s "
29020906b23SVikram Hegde 	    "nexops=%p", f, driver, instance, ddi_node_name(dip),
29120906b23SVikram Hegde 	    (void *)nexops);
29220906b23SVikram Hegde 
29320906b23SVikram Hegde 	*handle = nexp;
29420906b23SVikram Hegde 
29520906b23SVikram Hegde 	return (DDI_SUCCESS);
29620906b23SVikram Hegde }
29720906b23SVikram Hegde 
29820906b23SVikram Hegde int
29920906b23SVikram Hegde iommulib_nexus_unregister(iommulib_nexhandle_t handle)
30020906b23SVikram Hegde {
30120906b23SVikram Hegde 	dev_info_t *dip;
30220906b23SVikram Hegde 	int instance;
30320906b23SVikram Hegde 	const char *driver;
30420906b23SVikram Hegde 	iommulib_nex_t *nexp = (iommulib_nex_t *)handle;
30520906b23SVikram Hegde 	const char *f = "iommulib_nexus_unregister";
30620906b23SVikram Hegde 
30720906b23SVikram Hegde 	ASSERT(nexp);
30820906b23SVikram Hegde 
30920906b23SVikram Hegde 	mutex_enter(&iommulib_nexus_lock);
31020906b23SVikram Hegde 
31120906b23SVikram Hegde 	dip = nexp->nex_dip;
31220906b23SVikram Hegde 	driver = ddi_driver_name(dip);
31320906b23SVikram Hegde 	instance = ddi_get_instance(dip);
31420906b23SVikram Hegde 
31520906b23SVikram Hegde 	/* A future enhancement would be to add ref-counts */
31620906b23SVikram Hegde 
31720906b23SVikram Hegde 	if (nexp->nex_prev == NULL) {
31820906b23SVikram Hegde 		iommulib_nexus_list = nexp->nex_next;
31920906b23SVikram Hegde 	} else {
32020906b23SVikram Hegde 		nexp->nex_prev->nex_next = nexp->nex_next;
32120906b23SVikram Hegde 	}
32220906b23SVikram Hegde 
32320906b23SVikram Hegde 	if (nexp->nex_next != NULL)
32420906b23SVikram Hegde 		nexp->nex_next->nex_prev = nexp->nex_prev;
32520906b23SVikram Hegde 
32620906b23SVikram Hegde 	mutex_exit(&iommulib_nexus_lock);
32720906b23SVikram Hegde 
32820906b23SVikram Hegde 	kmem_free(nexp, sizeof (iommulib_nex_t));
32920906b23SVikram Hegde 
33020906b23SVikram Hegde 	cmn_err(CE_WARN, "%s: %s%d: NEXUS (%s) handle successfully "
33120906b23SVikram Hegde 	    "unregistered from IOMMULIB", f, driver, instance,
33220906b23SVikram Hegde 	    ddi_node_name(dip));
33320906b23SVikram Hegde 
33420906b23SVikram Hegde 	ndi_rele_devi(dip);
33520906b23SVikram Hegde 
33620906b23SVikram Hegde 	return (DDI_SUCCESS);
33720906b23SVikram Hegde }
33820906b23SVikram Hegde 
33920906b23SVikram Hegde static iommulib_nexops_t *
34020906b23SVikram Hegde lookup_nexops(dev_info_t *dip)
34120906b23SVikram Hegde {
34220906b23SVikram Hegde 	iommulib_nex_t  *nexp;
34320906b23SVikram Hegde 
34420906b23SVikram Hegde 	mutex_enter(&iommulib_nexus_lock);
34520906b23SVikram Hegde 	nexp = iommulib_nexus_list;
34620906b23SVikram Hegde 	while (nexp) {
34720906b23SVikram Hegde 		if (nexp->nex_dip == dip)
34820906b23SVikram Hegde 			break;
34920906b23SVikram Hegde 		nexp = nexp->nex_next;
35020906b23SVikram Hegde 	}
35120906b23SVikram Hegde 	mutex_exit(&iommulib_nexus_lock);
35220906b23SVikram Hegde 
35320906b23SVikram Hegde 	return (nexp ? &nexp->nex_ops : NULL);
35420906b23SVikram Hegde }
35520906b23SVikram Hegde 
35620906b23SVikram Hegde int
35720906b23SVikram Hegde iommulib_iommu_register(dev_info_t *dip, iommulib_ops_t *ops,
35820906b23SVikram Hegde     iommulib_handle_t *handle)
35920906b23SVikram Hegde {
36020906b23SVikram Hegde 	const char *vendor;
36120906b23SVikram Hegde 	iommulib_unit_t *unitp;
36220906b23SVikram Hegde 	int instance = ddi_get_instance(dip);
36320906b23SVikram Hegde 	const char *driver = ddi_driver_name(dip);
36420906b23SVikram Hegde 	dev_info_t *pdip = ddi_get_parent(dip);
36520906b23SVikram Hegde 	const char *f = "iommulib_register";
36620906b23SVikram Hegde 
36720906b23SVikram Hegde 	ASSERT(ops);
36820906b23SVikram Hegde 	ASSERT(handle);
36920906b23SVikram Hegde 
37020906b23SVikram Hegde 	if (i_ddi_node_state(dip) < DS_PROBED || !DEVI_BUSY_OWNED(pdip)) {
37120906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: devinfo node not in DS_PROBED or "
37220906b23SVikram Hegde 		    "busy held for ops vector (%p). Failing registration",
37320906b23SVikram Hegde 		    f, (void *)ops);
37420906b23SVikram Hegde 		return (DDI_FAILURE);
37520906b23SVikram Hegde 	}
37620906b23SVikram Hegde 
37720906b23SVikram Hegde 
37820906b23SVikram Hegde 	if (ops->ilops_vers != IOMMU_OPS_VERSION) {
37920906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: Invalid IOMMULIB ops version "
38020906b23SVikram Hegde 		    "in ops vector (%p). Failing registration", f, driver,
38120906b23SVikram Hegde 		    instance, (void *)ops);
38220906b23SVikram Hegde 		return (DDI_FAILURE);
38320906b23SVikram Hegde 	}
38420906b23SVikram Hegde 
38520906b23SVikram Hegde 	switch (ops->ilops_vendor) {
38620906b23SVikram Hegde 	case AMD_IOMMU:
38720906b23SVikram Hegde 		vendor = "AMD";
38820906b23SVikram Hegde 		break;
38920906b23SVikram Hegde 	case INTEL_IOMMU:
39020906b23SVikram Hegde 		vendor = "Intel";
39120906b23SVikram Hegde 		break;
39220906b23SVikram Hegde 	case INVALID_VENDOR:
39320906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: vendor field (%x) not initialized. "
39420906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
39520906b23SVikram Hegde 		    driver, instance, ops->ilops_vendor, (void *)ops);
39620906b23SVikram Hegde 		return (DDI_FAILURE);
39720906b23SVikram Hegde 	default:
39820906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: Invalid vendor field (%x). "
39920906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
40020906b23SVikram Hegde 		    driver, instance, ops->ilops_vendor, (void *)ops);
40120906b23SVikram Hegde 		return (DDI_FAILURE);
40220906b23SVikram Hegde 	}
40320906b23SVikram Hegde 
40420906b23SVikram Hegde 	cmn_err(CE_NOTE, "%s: %s%d: Detected IOMMU registration from vendor %s",
40520906b23SVikram Hegde 	    f, driver, instance, vendor);
40620906b23SVikram Hegde 
40720906b23SVikram Hegde 	if (ops->ilops_data == NULL) {
40820906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL IOMMU data field. "
40920906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
41020906b23SVikram Hegde 		    driver, instance, (void *)ops);
41120906b23SVikram Hegde 		return (DDI_FAILURE);
41220906b23SVikram Hegde 	}
41320906b23SVikram Hegde 
41420906b23SVikram Hegde 	if (ops->ilops_id == NULL) {
41520906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL ID field. "
41620906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
41720906b23SVikram Hegde 		    driver, instance, (void *)ops);
41820906b23SVikram Hegde 		return (DDI_FAILURE);
41920906b23SVikram Hegde 	}
42020906b23SVikram Hegde 
42120906b23SVikram Hegde 	if (ops->ilops_probe == NULL) {
42220906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL probe op. "
42320906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
42420906b23SVikram Hegde 		    driver, instance, (void *)ops);
42520906b23SVikram Hegde 		return (DDI_FAILURE);
42620906b23SVikram Hegde 	}
42720906b23SVikram Hegde 
42820906b23SVikram Hegde 	if (ops->ilops_dma_allochdl == NULL) {
42920906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL dma_allochdl op. "
43020906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
43120906b23SVikram Hegde 		    driver, instance, (void *)ops);
43220906b23SVikram Hegde 		return (DDI_FAILURE);
43320906b23SVikram Hegde 	}
43420906b23SVikram Hegde 
43520906b23SVikram Hegde 	if (ops->ilops_dma_freehdl == NULL) {
43620906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL dma_freehdl op. "
43720906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
43820906b23SVikram Hegde 		    driver, instance, (void *)ops);
43920906b23SVikram Hegde 		return (DDI_FAILURE);
44020906b23SVikram Hegde 	}
44120906b23SVikram Hegde 
44220906b23SVikram Hegde 	if (ops->ilops_dma_bindhdl == NULL) {
44320906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL dma_bindhdl op. "
44420906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
44520906b23SVikram Hegde 		    driver, instance, (void *)ops);
44620906b23SVikram Hegde 		return (DDI_FAILURE);
44720906b23SVikram Hegde 	}
44820906b23SVikram Hegde 
44920906b23SVikram Hegde 	if (ops->ilops_dma_sync == NULL) {
45020906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL dma_sync op. "
45120906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
45220906b23SVikram Hegde 		    driver, instance, (void *)ops);
45320906b23SVikram Hegde 		return (DDI_FAILURE);
45420906b23SVikram Hegde 	}
45520906b23SVikram Hegde 
45620906b23SVikram Hegde 	if (ops->ilops_dma_win == NULL) {
45720906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL dma_win op. "
45820906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
45920906b23SVikram Hegde 		    driver, instance, (void *)ops);
46020906b23SVikram Hegde 		return (DDI_FAILURE);
46120906b23SVikram Hegde 	}
46220906b23SVikram Hegde 
46320906b23SVikram Hegde 	/* Check for legacy ops */
46420906b23SVikram Hegde 	if (ops->ilops_dma_map == NULL) {
46520906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL legacy dma_map op. "
46620906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
46720906b23SVikram Hegde 		    driver, instance, (void *)ops);
46820906b23SVikram Hegde 		return (DDI_FAILURE);
46920906b23SVikram Hegde 	}
47020906b23SVikram Hegde 
47120906b23SVikram Hegde 	if (ops->ilops_dma_mctl == NULL) {
47220906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: NULL legacy dma_mctl op. "
47320906b23SVikram Hegde 		    "Failing registration for ops vector: %p", f,
47420906b23SVikram Hegde 		    driver, instance, (void *)ops);
47520906b23SVikram Hegde 		return (DDI_FAILURE);
47620906b23SVikram Hegde 	}
47720906b23SVikram Hegde 
47820906b23SVikram Hegde 	unitp = kmem_zalloc(sizeof (iommulib_unit_t), KM_SLEEP);
47920906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
48020906b23SVikram Hegde 	if (iommulib_fini == 1) {
48120906b23SVikram Hegde 		mutex_exit(&iommulib_lock);
48220906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: IOMMULIB unloading. Failing register.",
48320906b23SVikram Hegde 		    f);
48420906b23SVikram Hegde 		kmem_free(unitp, sizeof (iommulib_unit_t));
48520906b23SVikram Hegde 		return (DDI_FAILURE);
48620906b23SVikram Hegde 	}
48720906b23SVikram Hegde 
48820906b23SVikram Hegde 	/*
48920906b23SVikram Hegde 	 * fini/register race conditions have been handled. Now create the
49020906b23SVikram Hegde 	 * IOMMU unit
49120906b23SVikram Hegde 	 */
49220906b23SVikram Hegde 	mutex_init(&unitp->ilu_lock, NULL, MUTEX_DEFAULT, NULL);
49320906b23SVikram Hegde 
49420906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
49520906b23SVikram Hegde 	unitp->ilu_unitid = ++iommulib_unit_ids;
49620906b23SVikram Hegde 	unitp->ilu_ref = 0;
49720906b23SVikram Hegde 	ndi_hold_devi(dip);
49820906b23SVikram Hegde 	unitp->ilu_dip = dip;
49920906b23SVikram Hegde 	unitp->ilu_ops = ops;
50020906b23SVikram Hegde 	unitp->ilu_data = ops->ilops_data;
50120906b23SVikram Hegde 
50220906b23SVikram Hegde 	unitp->ilu_next = iommulib_list;
50320906b23SVikram Hegde 	unitp->ilu_prev = NULL;
50420906b23SVikram Hegde 	iommulib_list->ilu_prev = unitp;
50520906b23SVikram Hegde 	iommulib_list = unitp;
50620906b23SVikram Hegde 
50720906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
50820906b23SVikram Hegde 
50920906b23SVikram Hegde 	iommulib_num_units++;
51020906b23SVikram Hegde 
51120906b23SVikram Hegde 	*handle = unitp;
51220906b23SVikram Hegde 
51320906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
51420906b23SVikram Hegde 
51520906b23SVikram Hegde 	cmn_err(CE_NOTE, "%s: %s%d: Succesfully registered IOMMU unit "
51620906b23SVikram Hegde 	    "from vendor=%s, ops=%p, data=%p, IOMMULIB unitid=%u",
51720906b23SVikram Hegde 	    f, driver, instance, vendor, (void *)ops, (void *)unitp->ilu_data,
51820906b23SVikram Hegde 	    unitp->ilu_unitid);
51920906b23SVikram Hegde 
52020906b23SVikram Hegde 	return (DDI_SUCCESS);
52120906b23SVikram Hegde }
52220906b23SVikram Hegde 
52320906b23SVikram Hegde int
52420906b23SVikram Hegde iommulib_iommu_unregister(iommulib_handle_t handle)
52520906b23SVikram Hegde {
52620906b23SVikram Hegde 	uint32_t unitid;
52720906b23SVikram Hegde 	dev_info_t *dip;
52820906b23SVikram Hegde 	int instance;
52920906b23SVikram Hegde 	const char *driver;
53020906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
53120906b23SVikram Hegde 	const char *f = "iommulib_unregister";
53220906b23SVikram Hegde 
53320906b23SVikram Hegde 	ASSERT(unitp);
53420906b23SVikram Hegde 
53520906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
53620906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
53720906b23SVikram Hegde 
53820906b23SVikram Hegde 	unitid = unitp->ilu_unitid;
53920906b23SVikram Hegde 	dip = unitp->ilu_dip;
54020906b23SVikram Hegde 	driver = ddi_driver_name(dip);
54120906b23SVikram Hegde 	instance = ddi_get_instance(dip);
54220906b23SVikram Hegde 
54320906b23SVikram Hegde 	if (unitp->ilu_ref != 0) {
54420906b23SVikram Hegde 		mutex_exit(&unitp->ilu_lock);
54520906b23SVikram Hegde 		mutex_exit(&iommulib_lock);
54620906b23SVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: IOMMULIB handle is busy. Cannot "
54720906b23SVikram Hegde 		    "unregister IOMMULIB unitid %u",
54820906b23SVikram Hegde 		    f, driver, instance, unitid);
54920906b23SVikram Hegde 		return (DDI_FAILURE);
55020906b23SVikram Hegde 	}
55120906b23SVikram Hegde 	unitp->ilu_unitid = 0;
55220906b23SVikram Hegde 	ASSERT(unitp->ilu_ref == 0);
55320906b23SVikram Hegde 
55420906b23SVikram Hegde 	if (unitp->ilu_prev == NULL) {
55520906b23SVikram Hegde 		iommulib_list = unitp->ilu_next;
55620906b23SVikram Hegde 		unitp->ilu_next->ilu_prev = NULL;
55720906b23SVikram Hegde 	} else {
55820906b23SVikram Hegde 		unitp->ilu_prev->ilu_next = unitp->ilu_next;
55920906b23SVikram Hegde 		unitp->ilu_next->ilu_prev = unitp->ilu_prev;
56020906b23SVikram Hegde 	}
56120906b23SVikram Hegde 
56220906b23SVikram Hegde 	iommulib_num_units--;
56320906b23SVikram Hegde 
56420906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
56520906b23SVikram Hegde 
56620906b23SVikram Hegde 	mutex_destroy(&unitp->ilu_lock);
56720906b23SVikram Hegde 	kmem_free(unitp, sizeof (iommulib_unit_t));
56820906b23SVikram Hegde 
56920906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
57020906b23SVikram Hegde 
57120906b23SVikram Hegde 	cmn_err(CE_WARN, "%s: %s%d: IOMMULIB handle (unitid=%u) successfully "
57220906b23SVikram Hegde 	    "unregistered", f, driver, instance, unitid);
57320906b23SVikram Hegde 
57420906b23SVikram Hegde 	ndi_rele_devi(dip);
57520906b23SVikram Hegde 
57620906b23SVikram Hegde 	return (DDI_SUCCESS);
57720906b23SVikram Hegde }
57820906b23SVikram Hegde 
57920906b23SVikram Hegde int
58020906b23SVikram Hegde iommulib_nex_open(dev_info_t *rdip, uint_t *errorp)
58120906b23SVikram Hegde {
58220906b23SVikram Hegde 	iommulib_unit_t *unitp;
58320906b23SVikram Hegde 	int instance = ddi_get_instance(rdip);
58420906b23SVikram Hegde 	const char *driver = ddi_driver_name(rdip);
58520906b23SVikram Hegde 	const char *f = "iommulib_nex_open";
58620906b23SVikram Hegde 
58720906b23SVikram Hegde 	*errorp = 0;
58820906b23SVikram Hegde 	DEVI(rdip)->devi_iommulib_handle = NULL;
58920906b23SVikram Hegde 
59020906b23SVikram Hegde 	/* prevent use of IOMMU for AMD IOMMU's DMA */
59120906b23SVikram Hegde 	if (strcmp(driver, "amd_iommu") == 0) {
59220906b23SVikram Hegde 		*errorp = ENOTSUP;
59320906b23SVikram Hegde 		return (DDI_FAILURE);
59420906b23SVikram Hegde 	}
59520906b23SVikram Hegde 
59620906b23SVikram Hegde 	if (lookup_cache(rdip, &unitp) == DDI_SUCCESS) {
59720906b23SVikram Hegde 		DEVI(rdip)->devi_iommulib_handle =
59820906b23SVikram Hegde 		    (iommulib_handle_t)unitp;
59920906b23SVikram Hegde 		return (DDI_SUCCESS);
60020906b23SVikram Hegde 	}
60120906b23SVikram Hegde 
60220906b23SVikram Hegde 
60320906b23SVikram Hegde 	/*
60420906b23SVikram Hegde 	 * Ok this dip is not in the cache. Use the probe entry point
60520906b23SVikram Hegde 	 * to determine in a hardware specific manner whether this
60620906b23SVikram Hegde 	 * dip is controlled by an IOMMU. If yes, insert it into the
60720906b23SVikram Hegde 	 * cache and return the handle corresponding to the IOMMU unit.
60820906b23SVikram Hegde 	 */
60920906b23SVikram Hegde 
61020906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
61120906b23SVikram Hegde 	for (unitp = iommulib_list; unitp; unitp = unitp->ilu_next) {
61220906b23SVikram Hegde 		if (unitp->ilu_ops->ilops_probe(rdip) == DDI_SUCCESS)
61320906b23SVikram Hegde 			break;
61420906b23SVikram Hegde 	}
61520906b23SVikram Hegde 
61620906b23SVikram Hegde 	if (unitp == NULL) {
61720906b23SVikram Hegde 		mutex_exit(&iommulib_lock);
61820906b23SVikram Hegde 		if (iommulib_debug) {
619*d8fc7d07SVikram Hegde 			char *buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
62020906b23SVikram Hegde 			cmn_err(CE_WARN, "%s: %s%d: devinfo node (%p): is not "
62120906b23SVikram Hegde 			    "controlled by an IOMMU: path=%s", f, driver,
62220906b23SVikram Hegde 			    instance, (void *)rdip, ddi_pathname(rdip, buf));
623*d8fc7d07SVikram Hegde 			kmem_free(buf, MAXPATHLEN);
62420906b23SVikram Hegde 		}
62520906b23SVikram Hegde 		*errorp = ENOTSUP;
62620906b23SVikram Hegde 		return (DDI_FAILURE);
62720906b23SVikram Hegde 	}
62820906b23SVikram Hegde 
62920906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
63020906b23SVikram Hegde 	unitp->ilu_ref++;
63120906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
63220906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
63320906b23SVikram Hegde 
63420906b23SVikram Hegde 	insert_cache(rdip, unitp);
63520906b23SVikram Hegde 
63620906b23SVikram Hegde 	DEVI(rdip)->devi_iommulib_handle = unitp;
63720906b23SVikram Hegde 
63820906b23SVikram Hegde 	return (DDI_SUCCESS);
63920906b23SVikram Hegde }
64020906b23SVikram Hegde 
64120906b23SVikram Hegde void
64220906b23SVikram Hegde iommulib_nex_close(dev_info_t *rdip)
64320906b23SVikram Hegde {
64420906b23SVikram Hegde 	iommulib_unit_t *unitp;
64520906b23SVikram Hegde 	const char *driver;
64620906b23SVikram Hegde 	int instance;
64720906b23SVikram Hegde 	uint32_t unitid;
64820906b23SVikram Hegde 	const char *f = "iommulib_nex_close";
64920906b23SVikram Hegde 
65020906b23SVikram Hegde 	unitp = (iommulib_unit_t *)DEVI(rdip)->devi_iommulib_handle;
65120906b23SVikram Hegde 	if (unitp == NULL)
65220906b23SVikram Hegde 		return;
65320906b23SVikram Hegde 
65420906b23SVikram Hegde 	DEVI(rdip)->devi_iommulib_handle = NULL;
65520906b23SVikram Hegde 
65620906b23SVikram Hegde 	/*
65720906b23SVikram Hegde 	 * Assume we don't support DR of IOMMUs. The mapping of
65820906b23SVikram Hegde 	 * dips to IOMMU units should not change. Let the mapping
65920906b23SVikram Hegde 	 * persist in the cache.
66020906b23SVikram Hegde 	 */
66120906b23SVikram Hegde 
66220906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
66320906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
66420906b23SVikram Hegde 	unitid = unitp->ilu_unitid;
66520906b23SVikram Hegde 	driver = ddi_driver_name(unitp->ilu_dip);
66620906b23SVikram Hegde 	instance = ddi_get_instance(unitp->ilu_dip);
66720906b23SVikram Hegde 	unitp->ilu_ref--;
66820906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
66920906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
67020906b23SVikram Hegde 
67120906b23SVikram Hegde 	if (iommulib_debug) {
672*d8fc7d07SVikram Hegde 		char *buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
673*d8fc7d07SVikram Hegde 		(void) ddi_pathname(rdip, buf);
67420906b23SVikram Hegde 		cmn_err(CE_NOTE, "%s: %s%d: closing IOMMU for dip (%p), "
67520906b23SVikram Hegde 		    "unitid=%u rdip path = %s", f, driver, instance,
67620906b23SVikram Hegde 		    (void *)rdip, unitid, buf);
677*d8fc7d07SVikram Hegde 		kmem_free(buf, MAXPATHLEN);
67820906b23SVikram Hegde 	}
67920906b23SVikram Hegde }
68020906b23SVikram Hegde 
68120906b23SVikram Hegde int
68220906b23SVikram Hegde iommulib_nexdma_allochdl(dev_info_t *dip, dev_info_t *rdip,
68320906b23SVikram Hegde     ddi_dma_attr_t *attr, int (*waitfp)(caddr_t),
68420906b23SVikram Hegde     caddr_t arg, ddi_dma_handle_t *dma_handlep)
68520906b23SVikram Hegde {
68620906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
68720906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
68820906b23SVikram Hegde 
68920906b23SVikram Hegde 	ASSERT(unitp);
69020906b23SVikram Hegde 
69120906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
69220906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_allochdl(handle, dip, rdip,
69320906b23SVikram Hegde 	    attr, waitfp, arg, dma_handlep));
69420906b23SVikram Hegde }
69520906b23SVikram Hegde 
69620906b23SVikram Hegde int
69720906b23SVikram Hegde iommulib_nexdma_freehdl(dev_info_t *dip, dev_info_t *rdip,
69820906b23SVikram Hegde     ddi_dma_handle_t dma_handle)
69920906b23SVikram Hegde {
70020906b23SVikram Hegde 	int error;
70120906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
70220906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
70320906b23SVikram Hegde 
70420906b23SVikram Hegde 	ASSERT(unitp);
70520906b23SVikram Hegde 
70620906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
70720906b23SVikram Hegde 	error = unitp->ilu_ops->ilops_dma_freehdl(handle, dip,
70820906b23SVikram Hegde 	    rdip, dma_handle);
70920906b23SVikram Hegde 
71020906b23SVikram Hegde 	iommulib_nex_close(rdip);
71120906b23SVikram Hegde 
71220906b23SVikram Hegde 	return (error);
71320906b23SVikram Hegde }
71420906b23SVikram Hegde 
71520906b23SVikram Hegde int
71620906b23SVikram Hegde iommulib_nexdma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
71720906b23SVikram Hegde     ddi_dma_handle_t dma_handle, struct ddi_dma_req *dmareq,
71820906b23SVikram Hegde     ddi_dma_cookie_t *cookiep, uint_t *ccountp)
71920906b23SVikram Hegde {
72020906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
72120906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
72220906b23SVikram Hegde 
72320906b23SVikram Hegde 	ASSERT(unitp);
72420906b23SVikram Hegde 
72520906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
72620906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_bindhdl(handle, dip, rdip, dma_handle,
72720906b23SVikram Hegde 	    dmareq, cookiep, ccountp));
72820906b23SVikram Hegde }
72920906b23SVikram Hegde 
73020906b23SVikram Hegde int
73120906b23SVikram Hegde iommulib_nexdma_unbindhdl(dev_info_t *dip, dev_info_t *rdip,
73220906b23SVikram Hegde     ddi_dma_handle_t dma_handle)
73320906b23SVikram Hegde {
73420906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
73520906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
73620906b23SVikram Hegde 
73720906b23SVikram Hegde 	ASSERT(unitp);
73820906b23SVikram Hegde 
73920906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
74020906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_unbindhdl(handle, dip, rdip,
74120906b23SVikram Hegde 	    dma_handle));
74220906b23SVikram Hegde }
74320906b23SVikram Hegde 
74420906b23SVikram Hegde int
74520906b23SVikram Hegde iommulib_nexdma_sync(dev_info_t *dip, dev_info_t *rdip,
74620906b23SVikram Hegde     ddi_dma_handle_t dma_handle, off_t off, size_t len,
74720906b23SVikram Hegde     uint_t cache_flags)
74820906b23SVikram Hegde {
74920906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
75020906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
75120906b23SVikram Hegde 
75220906b23SVikram Hegde 	ASSERT(unitp);
75320906b23SVikram Hegde 
75420906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
75520906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_sync(handle, dip, rdip, dma_handle,
75620906b23SVikram Hegde 	    off, len, cache_flags));
75720906b23SVikram Hegde }
75820906b23SVikram Hegde 
75920906b23SVikram Hegde int
76020906b23SVikram Hegde iommulib_nexdma_win(dev_info_t *dip, dev_info_t *rdip,
76120906b23SVikram Hegde     ddi_dma_handle_t dma_handle, uint_t win, off_t *offp, size_t *lenp,
76220906b23SVikram Hegde     ddi_dma_cookie_t *cookiep, uint_t *ccountp)
76320906b23SVikram Hegde {
76420906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
76520906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
76620906b23SVikram Hegde 
76720906b23SVikram Hegde 	ASSERT(unitp);
76820906b23SVikram Hegde 
76920906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
77020906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_win(handle, dip, rdip, dma_handle,
77120906b23SVikram Hegde 	    win, offp, lenp, cookiep, ccountp));
77220906b23SVikram Hegde }
77320906b23SVikram Hegde 
77420906b23SVikram Hegde /* Obsolete DMA routines */
77520906b23SVikram Hegde 
77620906b23SVikram Hegde int
77720906b23SVikram Hegde iommulib_nexdma_map(dev_info_t *dip, dev_info_t *rdip,
77820906b23SVikram Hegde     struct ddi_dma_req *dmareq, ddi_dma_handle_t *dma_handle)
77920906b23SVikram Hegde {
78020906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
78120906b23SVikram Hegde 	iommulib_unit_t *unitp = handle;
78220906b23SVikram Hegde 
78320906b23SVikram Hegde 	ASSERT(unitp);
78420906b23SVikram Hegde 
78520906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
78620906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_map(handle, dip, rdip, dmareq,
78720906b23SVikram Hegde 	    dma_handle));
78820906b23SVikram Hegde }
78920906b23SVikram Hegde 
79020906b23SVikram Hegde int
79120906b23SVikram Hegde iommulib_nexdma_mctl(dev_info_t *dip, dev_info_t *rdip,
79220906b23SVikram Hegde     ddi_dma_handle_t dma_handle, enum ddi_dma_ctlops request,
79320906b23SVikram Hegde     off_t *offp, size_t *lenp, caddr_t *objpp, uint_t cache_flags)
79420906b23SVikram Hegde {
79520906b23SVikram Hegde 	iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle;
79620906b23SVikram Hegde 	iommulib_unit_t *unitp = (iommulib_unit_t *)handle;
79720906b23SVikram Hegde 
79820906b23SVikram Hegde 	ASSERT(unitp);
79920906b23SVikram Hegde 
80020906b23SVikram Hegde 	/* No need to grab lock - the handle is reference counted */
80120906b23SVikram Hegde 	return (unitp->ilu_ops->ilops_dma_mctl(handle, dip, rdip, dma_handle,
80220906b23SVikram Hegde 	    request, offp, lenp, objpp, cache_flags));
80320906b23SVikram Hegde }
80420906b23SVikram Hegde 
80520906b23SVikram Hegde /* Utility routines invoked by IOMMU drivers */
80620906b23SVikram Hegde int
80720906b23SVikram Hegde iommulib_iommu_dma_allochdl(dev_info_t *dip, dev_info_t *rdip,
80820906b23SVikram Hegde     ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg,
80920906b23SVikram Hegde     ddi_dma_handle_t *handlep)
81020906b23SVikram Hegde {
81120906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
81220906b23SVikram Hegde 	if (nexops == NULL)
81320906b23SVikram Hegde 		return (DDI_FAILURE);
81420906b23SVikram Hegde 	return (nexops->nops_dma_allochdl(dip, rdip, attr, waitfp, arg,
81520906b23SVikram Hegde 	    handlep));
81620906b23SVikram Hegde }
81720906b23SVikram Hegde 
81820906b23SVikram Hegde int
81920906b23SVikram Hegde iommulib_iommu_dma_freehdl(dev_info_t *dip, dev_info_t *rdip,
82020906b23SVikram Hegde     ddi_dma_handle_t handle)
82120906b23SVikram Hegde {
82220906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
82320906b23SVikram Hegde 	if (nexops == NULL)
82420906b23SVikram Hegde 		return (DDI_FAILURE);
82520906b23SVikram Hegde 	return (nexops->nops_dma_freehdl(dip, rdip, handle));
82620906b23SVikram Hegde }
82720906b23SVikram Hegde 
82820906b23SVikram Hegde int
82920906b23SVikram Hegde iommulib_iommu_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip,
83020906b23SVikram Hegde     ddi_dma_handle_t handle, struct ddi_dma_req *dmareq,
83120906b23SVikram Hegde     ddi_dma_cookie_t *cookiep, uint_t *ccountp)
83220906b23SVikram Hegde {
83320906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
83420906b23SVikram Hegde 	if (nexops == NULL)
83520906b23SVikram Hegde 		return (DDI_FAILURE);
83620906b23SVikram Hegde 	return (nexops->nops_dma_bindhdl(dip, rdip, handle, dmareq,
83720906b23SVikram Hegde 	    cookiep, ccountp));
83820906b23SVikram Hegde }
83920906b23SVikram Hegde 
84020906b23SVikram Hegde int
84120906b23SVikram Hegde iommulib_iommu_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip,
84220906b23SVikram Hegde     ddi_dma_handle_t handle)
84320906b23SVikram Hegde {
84420906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
84520906b23SVikram Hegde 	if (nexops == NULL)
84620906b23SVikram Hegde 		return (DDI_FAILURE);
84720906b23SVikram Hegde 	return (nexops->nops_dma_unbindhdl(dip, rdip, handle));
84820906b23SVikram Hegde }
84920906b23SVikram Hegde 
85020906b23SVikram Hegde void
85120906b23SVikram Hegde iommulib_iommu_dma_reset_cookies(dev_info_t *dip, ddi_dma_handle_t handle)
85220906b23SVikram Hegde {
85320906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
85420906b23SVikram Hegde 	nexops->nops_dma_reset_cookies(dip, handle);
85520906b23SVikram Hegde }
85620906b23SVikram Hegde 
85720906b23SVikram Hegde int
85820906b23SVikram Hegde iommulib_iommu_dma_get_cookies(dev_info_t *dip, ddi_dma_handle_t handle,
85920906b23SVikram Hegde     ddi_dma_cookie_t *cookiep, uint_t *ccountp)
86020906b23SVikram Hegde {
86120906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
86220906b23SVikram Hegde 	if (nexops == NULL)
86320906b23SVikram Hegde 		return (DDI_FAILURE);
86420906b23SVikram Hegde 	return (nexops->nops_dma_get_cookies(dip, handle, cookiep, ccountp));
86520906b23SVikram Hegde }
86620906b23SVikram Hegde 
86720906b23SVikram Hegde int
86820906b23SVikram Hegde iommulib_iommu_dma_sync(dev_info_t *dip, dev_info_t *rdip,
86920906b23SVikram Hegde     ddi_dma_handle_t handle, off_t off, size_t len, uint_t cache_flags)
87020906b23SVikram Hegde {
87120906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
87220906b23SVikram Hegde 	if (nexops == NULL)
87320906b23SVikram Hegde 		return (DDI_FAILURE);
87420906b23SVikram Hegde 	return (nexops->nops_dma_sync(dip, rdip, handle, off, len,
87520906b23SVikram Hegde 	    cache_flags));
87620906b23SVikram Hegde }
87720906b23SVikram Hegde 
87820906b23SVikram Hegde int
87920906b23SVikram Hegde iommulib_iommu_dma_win(dev_info_t *dip, dev_info_t *rdip,
88020906b23SVikram Hegde     ddi_dma_handle_t handle, uint_t win, off_t *offp, size_t *lenp,
88120906b23SVikram Hegde     ddi_dma_cookie_t *cookiep, uint_t *ccountp)
88220906b23SVikram Hegde {
88320906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
88420906b23SVikram Hegde 	if (nexops == NULL)
88520906b23SVikram Hegde 		return (DDI_FAILURE);
88620906b23SVikram Hegde 	return (nexops->nops_dma_win(dip, rdip, handle, win, offp, lenp,
88720906b23SVikram Hegde 	    cookiep, ccountp));
88820906b23SVikram Hegde }
88920906b23SVikram Hegde 
89020906b23SVikram Hegde int
89120906b23SVikram Hegde iommulib_iommu_dma_map(dev_info_t *dip, dev_info_t *rdip,
89220906b23SVikram Hegde     struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep)
89320906b23SVikram Hegde {
89420906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
89520906b23SVikram Hegde 	if (nexops == NULL)
89620906b23SVikram Hegde 		return (DDI_FAILURE);
89720906b23SVikram Hegde 	return (nexops->nops_dma_map(dip, rdip, dmareq, handlep));
89820906b23SVikram Hegde }
89920906b23SVikram Hegde 
90020906b23SVikram Hegde int
90120906b23SVikram Hegde iommulib_iommu_dma_mctl(dev_info_t *dip, dev_info_t *rdip,
90220906b23SVikram Hegde     ddi_dma_handle_t handle, enum ddi_dma_ctlops request, off_t *offp,
90320906b23SVikram Hegde     size_t *lenp, caddr_t *objpp, uint_t cache_flags)
90420906b23SVikram Hegde {
90520906b23SVikram Hegde 	iommulib_nexops_t *nexops = lookup_nexops(dip);
90620906b23SVikram Hegde 	if (nexops == NULL)
90720906b23SVikram Hegde 		return (DDI_FAILURE);
90820906b23SVikram Hegde 	return (nexops->nops_dma_mctl(dip, rdip, handle, request, offp, lenp,
90920906b23SVikram Hegde 	    objpp, cache_flags));
91020906b23SVikram Hegde }
91120906b23SVikram Hegde 
91220906b23SVikram Hegde int
91320906b23SVikram Hegde iommulib_iommu_getunitid(iommulib_handle_t handle, uint64_t *unitidp)
91420906b23SVikram Hegde {
91520906b23SVikram Hegde 	iommulib_unit_t *unitp;
91620906b23SVikram Hegde 	uint64_t unitid;
91720906b23SVikram Hegde 
91820906b23SVikram Hegde 	unitp = (iommulib_unit_t *)handle;
91920906b23SVikram Hegde 
92020906b23SVikram Hegde 	ASSERT(unitp);
92120906b23SVikram Hegde 	ASSERT(unitidp);
92220906b23SVikram Hegde 
92320906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
92420906b23SVikram Hegde 	unitid = unitp->ilu_unitid;
92520906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
92620906b23SVikram Hegde 
92720906b23SVikram Hegde 	ASSERT(unitid > 0);
92820906b23SVikram Hegde 	*unitidp = (uint64_t)unitid;
92920906b23SVikram Hegde 
93020906b23SVikram Hegde 	return (DDI_SUCCESS);
93120906b23SVikram Hegde }
93220906b23SVikram Hegde 
93320906b23SVikram Hegde dev_info_t *
93420906b23SVikram Hegde iommulib_iommu_getdip(iommulib_handle_t handle)
93520906b23SVikram Hegde {
93620906b23SVikram Hegde 	iommulib_unit_t *unitp;
93720906b23SVikram Hegde 	dev_info_t *dip;
93820906b23SVikram Hegde 
93920906b23SVikram Hegde 	unitp = (iommulib_unit_t *)handle;
94020906b23SVikram Hegde 
94120906b23SVikram Hegde 	ASSERT(unitp);
94220906b23SVikram Hegde 
94320906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
94420906b23SVikram Hegde 	dip = unitp->ilu_dip;
94520906b23SVikram Hegde 	ASSERT(dip);
94620906b23SVikram Hegde 	ndi_hold_devi(dip);
94720906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
94820906b23SVikram Hegde 
94920906b23SVikram Hegde 	return (dip);
95020906b23SVikram Hegde }
95120906b23SVikram Hegde 
95220906b23SVikram Hegde iommulib_ops_t *
95320906b23SVikram Hegde iommulib_iommu_getops(iommulib_handle_t handle)
95420906b23SVikram Hegde {
95520906b23SVikram Hegde 	iommulib_unit_t *unitp;
95620906b23SVikram Hegde 	iommulib_ops_t *ops;
95720906b23SVikram Hegde 
95820906b23SVikram Hegde 	unitp = (iommulib_unit_t *)handle;
95920906b23SVikram Hegde 
96020906b23SVikram Hegde 	ASSERT(unitp);
96120906b23SVikram Hegde 
96220906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
96320906b23SVikram Hegde 	ops = unitp->ilu_ops;
96420906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
96520906b23SVikram Hegde 
96620906b23SVikram Hegde 	ASSERT(ops);
96720906b23SVikram Hegde 
96820906b23SVikram Hegde 	return (ops);
96920906b23SVikram Hegde }
97020906b23SVikram Hegde 
97120906b23SVikram Hegde void *
97220906b23SVikram Hegde iommulib_iommu_getdata(iommulib_handle_t handle)
97320906b23SVikram Hegde {
97420906b23SVikram Hegde 	iommulib_unit_t *unitp;
97520906b23SVikram Hegde 	void *data;
97620906b23SVikram Hegde 
97720906b23SVikram Hegde 	unitp = (iommulib_unit_t *)handle;
97820906b23SVikram Hegde 
97920906b23SVikram Hegde 	ASSERT(unitp);
98020906b23SVikram Hegde 
98120906b23SVikram Hegde 	mutex_enter(&unitp->ilu_lock);
98220906b23SVikram Hegde 	data = unitp->ilu_data;
98320906b23SVikram Hegde 	mutex_exit(&unitp->ilu_lock);
98420906b23SVikram Hegde 
98520906b23SVikram Hegde 	ASSERT(data);
98620906b23SVikram Hegde 
98720906b23SVikram Hegde 	return (data);
98820906b23SVikram Hegde }
98920906b23SVikram Hegde 
99020906b23SVikram Hegde /*
99120906b23SVikram Hegde  * Internal routines
99220906b23SVikram Hegde  */
99320906b23SVikram Hegde 
99420906b23SVikram Hegde static uint32_t
99520906b23SVikram Hegde hashfn(uint64_t ptr)
99620906b23SVikram Hegde {
99720906b23SVikram Hegde 	return (ptr % iommulib_cache_size);
99820906b23SVikram Hegde }
99920906b23SVikram Hegde 
100020906b23SVikram Hegde static int
100120906b23SVikram Hegde lookup_cache(dev_info_t *rdip, iommulib_unit_t **unitpp)
100220906b23SVikram Hegde {
100320906b23SVikram Hegde 	uint64_t idx;
100420906b23SVikram Hegde 	iommulib_cache_t *cachep;
100520906b23SVikram Hegde 	iommulib_unit_t *unitp;
100620906b23SVikram Hegde 	int retval = DDI_FAILURE;
100720906b23SVikram Hegde 
100820906b23SVikram Hegde 	*unitpp = NULL;
100920906b23SVikram Hegde 
101020906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
101120906b23SVikram Hegde 	mutex_enter(&iommulib_cache_lock);
101220906b23SVikram Hegde 
101320906b23SVikram Hegde 	ASSERT(iommulib_cache);
101420906b23SVikram Hegde 
101520906b23SVikram Hegde 	idx = hashfn((uint64_t)(uintptr_t)rdip);
101620906b23SVikram Hegde 
101720906b23SVikram Hegde 	ASSERT(idx < iommulib_cache_size);
101820906b23SVikram Hegde 
101920906b23SVikram Hegde 	for (cachep = iommulib_cache[idx]; cachep;
102020906b23SVikram Hegde 	    cachep = cachep->cache_next) {
102120906b23SVikram Hegde 		if (cachep->cache_rdip == rdip)
102220906b23SVikram Hegde 			break;
102320906b23SVikram Hegde 	}
102420906b23SVikram Hegde 
102520906b23SVikram Hegde 	if (cachep != NULL) {
102620906b23SVikram Hegde 		unitp = cachep->cache_unit;
102720906b23SVikram Hegde 		mutex_enter(&unitp->ilu_lock);
102820906b23SVikram Hegde 		unitp->ilu_ref++;
102920906b23SVikram Hegde 		mutex_exit(&unitp->ilu_lock);
103020906b23SVikram Hegde 		*unitpp = unitp;
103120906b23SVikram Hegde 		retval = DDI_SUCCESS;
103220906b23SVikram Hegde 	}
103320906b23SVikram Hegde 
103420906b23SVikram Hegde 	mutex_exit(&iommulib_cache_lock);
103520906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
103620906b23SVikram Hegde 	return (retval);
103720906b23SVikram Hegde }
103820906b23SVikram Hegde 
103920906b23SVikram Hegde static void
104020906b23SVikram Hegde insert_cache(dev_info_t *rdip, iommulib_unit_t *unitp)
104120906b23SVikram Hegde {
104220906b23SVikram Hegde 	uint32_t idx;
104320906b23SVikram Hegde 	iommulib_cache_t *cachep;
104420906b23SVikram Hegde 
104520906b23SVikram Hegde 	mutex_enter(&iommulib_lock);
104620906b23SVikram Hegde 	mutex_enter(&iommulib_cache_lock);
104720906b23SVikram Hegde 
104820906b23SVikram Hegde 	ASSERT(iommulib_cache);
104920906b23SVikram Hegde 
105020906b23SVikram Hegde 	idx = hashfn((uint64_t)(uintptr_t)rdip);
105120906b23SVikram Hegde 
105220906b23SVikram Hegde 	ASSERT(idx < iommulib_cache_size);
105320906b23SVikram Hegde 
105420906b23SVikram Hegde 	for (cachep = iommulib_cache[idx]; cachep;
105520906b23SVikram Hegde 	    cachep = cachep->cache_next) {
105620906b23SVikram Hegde 		if (cachep->cache_rdip == rdip)
105720906b23SVikram Hegde 			break;
105820906b23SVikram Hegde 	}
105920906b23SVikram Hegde 
106020906b23SVikram Hegde 	if (cachep == NULL) {
106120906b23SVikram Hegde 		cachep = kmem_zalloc(sizeof (iommulib_cache_t), KM_SLEEP);
106220906b23SVikram Hegde 		cachep->cache_rdip = rdip;
106320906b23SVikram Hegde 		cachep->cache_unit = unitp;	/* ref-count set by caller */
106420906b23SVikram Hegde 		cachep->cache_prev = NULL;
106520906b23SVikram Hegde 		cachep->cache_next = iommulib_cache[idx];
106620906b23SVikram Hegde 		if (cachep->cache_next)
106720906b23SVikram Hegde 			cachep->cache_next->cache_prev = cachep;
106820906b23SVikram Hegde 		iommulib_cache[idx] = cachep;
106920906b23SVikram Hegde 	}
107020906b23SVikram Hegde 
107120906b23SVikram Hegde 	mutex_exit(&iommulib_cache_lock);
107220906b23SVikram Hegde 	mutex_exit(&iommulib_lock);
107320906b23SVikram Hegde }
1074