xref: /illumos-gate/usr/src/uts/i86pc/io/immu_dvma.c (revision 584b574a)
13a634bfcSVikram Hegde /*
23a634bfcSVikram Hegde  * CDDL HEADER START
33a634bfcSVikram Hegde  *
43a634bfcSVikram Hegde  * The contents of this file are subject to the terms of the
53a634bfcSVikram Hegde  * Common Development and Distribution License (the "License").
63a634bfcSVikram Hegde  * You may not use this file except in compliance with the License.
73a634bfcSVikram Hegde  *
83a634bfcSVikram Hegde  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93a634bfcSVikram Hegde  * or http://www.opensolaris.org/os/licensing.
103a634bfcSVikram Hegde  * See the License for the specific language governing permissions
113a634bfcSVikram Hegde  * and limitations under the License.
123a634bfcSVikram Hegde  *
133a634bfcSVikram Hegde  * When distributing Covered Code, include this CDDL HEADER in each
143a634bfcSVikram Hegde  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153a634bfcSVikram Hegde  * If applicable, add the following below this CDDL HEADER, with the
163a634bfcSVikram Hegde  * fields enclosed by brackets "[]" replaced with your own identifying
173a634bfcSVikram Hegde  * information: Portions Copyright [yyyy] [name of copyright owner]
183a634bfcSVikram Hegde  *
193a634bfcSVikram Hegde  * CDDL HEADER END
203a634bfcSVikram Hegde  */
213a634bfcSVikram Hegde /*
229e986f0eSFrank Van Der Linden  * Portions Copyright (c) 2010, Oracle and/or its affiliates.
239e986f0eSFrank Van Der Linden  * All rights reserved.
243a634bfcSVikram Hegde  */
253a634bfcSVikram Hegde /*
263a634bfcSVikram Hegde  * Copyright (c) 2009, Intel Corporation.
273a634bfcSVikram Hegde  * All rights reserved.
283a634bfcSVikram Hegde  */
29cd21e7c5SGarrett D'Amore /*
30cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
312cedd8f0SHans Rosenfeld  * Copyright 2017 Joyent, Inc.
32cd21e7c5SGarrett D'Amore  */
333a634bfcSVikram Hegde 
343a634bfcSVikram Hegde /*
353a634bfcSVikram Hegde  * DVMA code
363a634bfcSVikram Hegde  * This file contains Intel IOMMU code that deals with DVMA
373a634bfcSVikram Hegde  * i.e. DMA remapping.
383a634bfcSVikram Hegde  */
393a634bfcSVikram Hegde 
403a634bfcSVikram Hegde #include <sys/sysmacros.h>
413a634bfcSVikram Hegde #include <sys/pcie.h>
423a634bfcSVikram Hegde #include <sys/pci_cfgspace.h>
433a634bfcSVikram Hegde #include <vm/hat_i86.h>
443a634bfcSVikram Hegde #include <sys/memlist.h>
453a634bfcSVikram Hegde #include <sys/acpi/acpi.h>
463a634bfcSVikram Hegde #include <sys/acpica.h>
473a634bfcSVikram Hegde #include <sys/modhash.h>
483a634bfcSVikram Hegde #include <sys/immu.h>
4950200e77SFrank Van Der Linden #include <sys/x86_archext.h>
5050200e77SFrank Van Der Linden #include <sys/archsystm.h>
513a634bfcSVikram Hegde 
523a634bfcSVikram Hegde #undef	TEST
533a634bfcSVikram Hegde 
543a634bfcSVikram Hegde /*
553a634bfcSVikram Hegde  * Macros based on PCI spec
563a634bfcSVikram Hegde  */
573a634bfcSVikram Hegde #define	IMMU_PCI_REV2CLASS(r)   ((r) >> 8)  /* classcode from revid */
583a634bfcSVikram Hegde #define	IMMU_PCI_CLASS2BASE(c)  ((c) >> 16) /* baseclass from classcode */
593a634bfcSVikram Hegde #define	IMMU_PCI_CLASS2SUB(c)   (((c) >> 8) & 0xff); /* classcode */
603a634bfcSVikram Hegde 
613a634bfcSVikram Hegde #define	IMMU_CONTIG_PADDR(d, p) \
622cedd8f0SHans Rosenfeld 	((d).dck_paddr && ((d).dck_paddr + (d).dck_npages * IMMU_PAGESIZE) \
632cedd8f0SHans Rosenfeld 	    == (p))
643a634bfcSVikram Hegde 
653a634bfcSVikram Hegde typedef struct dvma_arg {
663a634bfcSVikram Hegde 	immu_t *dva_immu;
673a634bfcSVikram Hegde 	dev_info_t *dva_rdip;
683a634bfcSVikram Hegde 	dev_info_t *dva_ddip;
693a634bfcSVikram Hegde 	domain_t *dva_domain;
703a634bfcSVikram Hegde 	int dva_level;
713a634bfcSVikram Hegde 	immu_flags_t dva_flags;
723a634bfcSVikram Hegde 	list_t *dva_list;
733a634bfcSVikram Hegde 	int dva_error;
743a634bfcSVikram Hegde } dvma_arg_t;
753a634bfcSVikram Hegde 
763a634bfcSVikram Hegde static domain_t *domain_create(immu_t *immu, dev_info_t *ddip,
773a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags);
783a634bfcSVikram Hegde static immu_devi_t *create_immu_devi(dev_info_t *rdip, int bus,
793a634bfcSVikram Hegde     int dev, int func, immu_flags_t immu_flags);
803a634bfcSVikram Hegde static void destroy_immu_devi(immu_devi_t *immu_devi);
8150200e77SFrank Van Der Linden static boolean_t dvma_map(domain_t *domain, uint64_t sdvma,
8250200e77SFrank Van Der Linden     uint64_t nvpages, immu_dcookie_t *dcookies, int dcount, dev_info_t *rdip,
833a634bfcSVikram Hegde     immu_flags_t immu_flags);
843a634bfcSVikram Hegde 
85e03dceedSVikram Hegde /* Extern globals */
86e03dceedSVikram Hegde extern struct memlist  *phys_install;
873a634bfcSVikram Hegde 
8850200e77SFrank Van Der Linden /*
8950200e77SFrank Van Der Linden  * iommulib interface functions.
9050200e77SFrank Van Der Linden  */
9150200e77SFrank Van Der Linden static int immu_probe(iommulib_handle_t unitp, dev_info_t *dip);
9250200e77SFrank Van Der Linden static int immu_allochdl(iommulib_handle_t handle,
9350200e77SFrank Van Der Linden     dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
9450200e77SFrank Van Der Linden     int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep);
9550200e77SFrank Van Der Linden static int immu_freehdl(iommulib_handle_t handle,
9650200e77SFrank Van Der Linden     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle);
9750200e77SFrank Van Der Linden static int immu_bindhdl(iommulib_handle_t handle, dev_info_t *dip,
9850200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, struct ddi_dma_req *dma_req,
9950200e77SFrank Van Der Linden     ddi_dma_cookie_t *cookiep, uint_t *ccountp);
10050200e77SFrank Van Der Linden static int immu_unbindhdl(iommulib_handle_t handle,
10150200e77SFrank Van Der Linden     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle);
10250200e77SFrank Van Der Linden static int immu_sync(iommulib_handle_t handle, dev_info_t *dip,
10350200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off, size_t len,
10450200e77SFrank Van Der Linden     uint_t cachefl);
10550200e77SFrank Van Der Linden static int immu_win(iommulib_handle_t handle, dev_info_t *dip,
10650200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win,
10750200e77SFrank Van Der Linden     off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep, uint_t *ccountp);
10850200e77SFrank Van Der Linden static int immu_mapobject(iommulib_handle_t handle, dev_info_t *dip,
10950200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
11050200e77SFrank Van Der Linden     struct ddi_dma_req *dmareq, ddi_dma_obj_t *dmao);
11150200e77SFrank Van Der Linden static int immu_unmapobject(iommulib_handle_t handle, dev_info_t *dip,
11250200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, ddi_dma_obj_t *dmao);
1133a634bfcSVikram Hegde 
1143a634bfcSVikram Hegde /* static Globals */
1153a634bfcSVikram Hegde 
1163a634bfcSVikram Hegde /*
1173a634bfcSVikram Hegde  * Used to setup DMA objects (memory regions)
1183a634bfcSVikram Hegde  * for DMA reads by IOMMU units
1193a634bfcSVikram Hegde  */
1203a634bfcSVikram Hegde static ddi_dma_attr_t immu_dma_attr = {
1213a634bfcSVikram Hegde 	DMA_ATTR_V0,
1223a634bfcSVikram Hegde 	0U,
123d2256d26SFrank Van Der Linden 	0xffffffffffffffffULL,
1243a634bfcSVikram Hegde 	0xffffffffU,
1253a634bfcSVikram Hegde 	MMU_PAGESIZE, /* MMU page aligned */
1263a634bfcSVikram Hegde 	0x1,
1273a634bfcSVikram Hegde 	0x1,
1283a634bfcSVikram Hegde 	0xffffffffU,
129d2256d26SFrank Van Der Linden 	0xffffffffffffffffULL,
1303a634bfcSVikram Hegde 	1,
1313a634bfcSVikram Hegde 	4,
1323a634bfcSVikram Hegde 	0
1333a634bfcSVikram Hegde };
1343a634bfcSVikram Hegde 
1353a634bfcSVikram Hegde static ddi_device_acc_attr_t immu_acc_attr = {
1363a634bfcSVikram Hegde 	DDI_DEVICE_ATTR_V0,
1373a634bfcSVikram Hegde 	DDI_NEVERSWAP_ACC,
1383a634bfcSVikram Hegde 	DDI_STRICTORDER_ACC
1393a634bfcSVikram Hegde };
1403a634bfcSVikram Hegde 
14150200e77SFrank Van Der Linden struct iommulib_ops immulib_ops = {
14250200e77SFrank Van Der Linden 	IOMMU_OPS_VERSION,
14350200e77SFrank Van Der Linden 	INTEL_IOMMU,
14450200e77SFrank Van Der Linden 	"Intel IOMMU",
14550200e77SFrank Van Der Linden 	NULL,
14650200e77SFrank Van Der Linden 	immu_probe,
14750200e77SFrank Van Der Linden 	immu_allochdl,
14850200e77SFrank Van Der Linden 	immu_freehdl,
14950200e77SFrank Van Der Linden 	immu_bindhdl,
15050200e77SFrank Van Der Linden 	immu_unbindhdl,
15150200e77SFrank Van Der Linden 	immu_sync,
15250200e77SFrank Van Der Linden 	immu_win,
15350200e77SFrank Van Der Linden 	immu_mapobject,
15450200e77SFrank Van Der Linden 	immu_unmapobject,
15550200e77SFrank Van Der Linden };
15650200e77SFrank Van Der Linden 
15750200e77SFrank Van Der Linden /*
15850200e77SFrank Van Der Linden  * Fake physical address range used to set up initial prealloc mappings.
15950200e77SFrank Van Der Linden  * This memory is never actually accessed. It is mapped read-only,
16050200e77SFrank Van Der Linden  * and is overwritten as soon as the first DMA bind operation is
16150200e77SFrank Van Der Linden  * performed. Since 0 is a special case, just start at the 2nd
16250200e77SFrank Van Der Linden  * physical page.
16350200e77SFrank Van Der Linden  */
16450200e77SFrank Van Der Linden 
16550200e77SFrank Van Der Linden static immu_dcookie_t immu_precookie = { MMU_PAGESIZE, IMMU_NPREPTES };
1663a634bfcSVikram Hegde 
1673a634bfcSVikram Hegde /* globals private to this file */
1683a634bfcSVikram Hegde static kmutex_t immu_domain_lock;
1693a634bfcSVikram Hegde static list_t immu_unity_domain_list;
1703a634bfcSVikram Hegde static list_t immu_xlate_domain_list;
1713a634bfcSVikram Hegde 
1723a634bfcSVikram Hegde /* structure used to store idx into each level of the page tables */
1733a634bfcSVikram Hegde typedef struct xlate {
1743a634bfcSVikram Hegde 	int xlt_level;
1753a634bfcSVikram Hegde 	uint_t xlt_idx;
1763a634bfcSVikram Hegde 	pgtable_t *xlt_pgtable;
1773a634bfcSVikram Hegde } xlate_t;
1783a634bfcSVikram Hegde 
1793a634bfcSVikram Hegde /* 0 is reserved by Vt-d spec. Solaris reserves 1 */
1803a634bfcSVikram Hegde #define	IMMU_UNITY_DID   1
1813a634bfcSVikram Hegde 
1823a634bfcSVikram Hegde static mod_hash_t *bdf_domain_hash;
1833a634bfcSVikram Hegde 
18450200e77SFrank Van Der Linden int immu_use_alh;
18550200e77SFrank Van Der Linden int immu_use_tm;
18650200e77SFrank Van Der Linden 
1873a634bfcSVikram Hegde static domain_t *
bdf_domain_lookup(immu_devi_t * immu_devi)1883a634bfcSVikram Hegde bdf_domain_lookup(immu_devi_t *immu_devi)
1893a634bfcSVikram Hegde {
1903a634bfcSVikram Hegde 	domain_t *domain;
1913a634bfcSVikram Hegde 	int16_t seg = immu_devi->imd_seg;
1923a634bfcSVikram Hegde 	int16_t bus = immu_devi->imd_bus;
1933a634bfcSVikram Hegde 	int16_t devfunc = immu_devi->imd_devfunc;
1943a634bfcSVikram Hegde 	uintptr_t bdf = (seg << 16 | bus << 8 | devfunc);
1953a634bfcSVikram Hegde 
1963a634bfcSVikram Hegde 	if (seg < 0 || bus < 0 || devfunc < 0) {
1973a634bfcSVikram Hegde 		return (NULL);
1983a634bfcSVikram Hegde 	}
1993a634bfcSVikram Hegde 
2003a634bfcSVikram Hegde 	domain = NULL;
2013a634bfcSVikram Hegde 	if (mod_hash_find(bdf_domain_hash,
2023a634bfcSVikram Hegde 	    (void *)bdf, (void *)&domain) == 0) {
2033a634bfcSVikram Hegde 		ASSERT(domain);
2043a634bfcSVikram Hegde 		ASSERT(domain->dom_did > 0);
2053a634bfcSVikram Hegde 		return (domain);
2063a634bfcSVikram Hegde 	} else {
2073a634bfcSVikram Hegde 		return (NULL);
2083a634bfcSVikram Hegde 	}
2093a634bfcSVikram Hegde }
2103a634bfcSVikram Hegde 
2113a634bfcSVikram Hegde static void
bdf_domain_insert(immu_devi_t * immu_devi,domain_t * domain)2123a634bfcSVikram Hegde bdf_domain_insert(immu_devi_t *immu_devi, domain_t *domain)
2133a634bfcSVikram Hegde {
2143a634bfcSVikram Hegde 	int16_t seg = immu_devi->imd_seg;
2153a634bfcSVikram Hegde 	int16_t bus = immu_devi->imd_bus;
2163a634bfcSVikram Hegde 	int16_t devfunc = immu_devi->imd_devfunc;
2173a634bfcSVikram Hegde 	uintptr_t bdf = (seg << 16 | bus << 8 | devfunc);
2183a634bfcSVikram Hegde 
2193a634bfcSVikram Hegde 	if (seg < 0 || bus < 0 || devfunc < 0) {
2203a634bfcSVikram Hegde 		return;
2213a634bfcSVikram Hegde 	}
2223a634bfcSVikram Hegde 
22350200e77SFrank Van Der Linden 	(void) mod_hash_insert(bdf_domain_hash, (void *)bdf, (void *)domain);
2243a634bfcSVikram Hegde }
2253a634bfcSVikram Hegde 
2263a634bfcSVikram Hegde static int
match_lpc(dev_info_t * pdip,void * arg)2273a634bfcSVikram Hegde match_lpc(dev_info_t *pdip, void *arg)
2283a634bfcSVikram Hegde {
2293a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
2303a634bfcSVikram Hegde 	dvma_arg_t *dvap = (dvma_arg_t *)arg;
2313a634bfcSVikram Hegde 
2323a634bfcSVikram Hegde 	if (list_is_empty(dvap->dva_list)) {
2333a634bfcSVikram Hegde 		return (DDI_WALK_TERMINATE);
2343a634bfcSVikram Hegde 	}
2353a634bfcSVikram Hegde 
2363a634bfcSVikram Hegde 	immu_devi = list_head(dvap->dva_list);
2373a634bfcSVikram Hegde 	for (; immu_devi; immu_devi = list_next(dvap->dva_list,
2383a634bfcSVikram Hegde 	    immu_devi)) {
2393a634bfcSVikram Hegde 		if (immu_devi->imd_dip == pdip) {
2403a634bfcSVikram Hegde 			dvap->dva_ddip = pdip;
2413a634bfcSVikram Hegde 			dvap->dva_error = DDI_SUCCESS;
2423a634bfcSVikram Hegde 			return (DDI_WALK_TERMINATE);
2433a634bfcSVikram Hegde 		}
2443a634bfcSVikram Hegde 	}
2453a634bfcSVikram Hegde 
2463a634bfcSVikram Hegde 	return (DDI_WALK_CONTINUE);
2473a634bfcSVikram Hegde }
2483a634bfcSVikram Hegde 
2493a634bfcSVikram Hegde static void
immu_devi_set_spclist(dev_info_t * dip,immu_t * immu)2503a634bfcSVikram Hegde immu_devi_set_spclist(dev_info_t *dip, immu_t *immu)
2513a634bfcSVikram Hegde {
2523a634bfcSVikram Hegde 	list_t *spclist = NULL;
2533a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
2543a634bfcSVikram Hegde 
2553a634bfcSVikram Hegde 	immu_devi = IMMU_DEVI(dip);
2563a634bfcSVikram Hegde 	if (immu_devi->imd_display == B_TRUE) {
2573a634bfcSVikram Hegde 		spclist = &(immu->immu_dvma_gfx_list);
2583a634bfcSVikram Hegde 	} else if (immu_devi->imd_lpc == B_TRUE) {
2593a634bfcSVikram Hegde 		spclist = &(immu->immu_dvma_lpc_list);
2603a634bfcSVikram Hegde 	}
2613a634bfcSVikram Hegde 
2623a634bfcSVikram Hegde 	if (spclist) {
2633a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
2643a634bfcSVikram Hegde 		list_insert_head(spclist, immu_devi);
2653a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
2663a634bfcSVikram Hegde 	}
2673a634bfcSVikram Hegde }
2683a634bfcSVikram Hegde 
2693a634bfcSVikram Hegde /*
2703a634bfcSVikram Hegde  * Set the immu_devi struct in the immu_devi field of a devinfo node
2713a634bfcSVikram Hegde  */
2723a634bfcSVikram Hegde int
immu_devi_set(dev_info_t * dip,immu_flags_t immu_flags)2733a634bfcSVikram Hegde immu_devi_set(dev_info_t *dip, immu_flags_t immu_flags)
2743a634bfcSVikram Hegde {
2753a634bfcSVikram Hegde 	int bus, dev, func;
2763a634bfcSVikram Hegde 	immu_devi_t *new_imd;
2773a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
2783a634bfcSVikram Hegde 
2793a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
2803a634bfcSVikram Hegde 	if (immu_devi != NULL) {
2813a634bfcSVikram Hegde 		return (DDI_SUCCESS);
2823a634bfcSVikram Hegde 	}
2833a634bfcSVikram Hegde 
2843a634bfcSVikram Hegde 	bus = dev = func = -1;
2853a634bfcSVikram Hegde 
2863a634bfcSVikram Hegde 	/*
2873a634bfcSVikram Hegde 	 * Assume a new immu_devi struct is needed
2883a634bfcSVikram Hegde 	 */
2893a634bfcSVikram Hegde 	if (!DEVI_IS_PCI(dip) || acpica_get_bdf(dip, &bus, &dev, &func) != 0) {
2903a634bfcSVikram Hegde 		/*
2913a634bfcSVikram Hegde 		 * No BDF. Set bus = -1 to indicate this.
2923a634bfcSVikram Hegde 		 * We still need to create a immu_devi struct
2933a634bfcSVikram Hegde 		 * though
2943a634bfcSVikram Hegde 		 */
2953a634bfcSVikram Hegde 		bus = -1;
2963a634bfcSVikram Hegde 		dev = 0;
2973a634bfcSVikram Hegde 		func = 0;
2983a634bfcSVikram Hegde 	}
2993a634bfcSVikram Hegde 
3003a634bfcSVikram Hegde 	new_imd = create_immu_devi(dip, bus, dev, func, immu_flags);
3013a634bfcSVikram Hegde 	if (new_imd  == NULL) {
3023a634bfcSVikram Hegde 		ddi_err(DER_WARN, dip, "Failed to create immu_devi "
3033a634bfcSVikram Hegde 		    "structure");
3043a634bfcSVikram Hegde 		return (DDI_FAILURE);
3053a634bfcSVikram Hegde 	}
3063a634bfcSVikram Hegde 
3073a634bfcSVikram Hegde 	/*
3083a634bfcSVikram Hegde 	 * Check if some other thread allocated a immu_devi while we
3093a634bfcSVikram Hegde 	 * didn't own the lock.
3103a634bfcSVikram Hegde 	 */
3113a634bfcSVikram Hegde 	mutex_enter(&(DEVI(dip)->devi_lock));
3123a634bfcSVikram Hegde 	if (IMMU_DEVI(dip) == NULL) {
3133a634bfcSVikram Hegde 		IMMU_DEVI_SET(dip, new_imd);
3143a634bfcSVikram Hegde 	} else {
3153a634bfcSVikram Hegde 		destroy_immu_devi(new_imd);
3163a634bfcSVikram Hegde 	}
3173a634bfcSVikram Hegde 	mutex_exit(&(DEVI(dip)->devi_lock));
3183a634bfcSVikram Hegde 
3193a634bfcSVikram Hegde 	return (DDI_SUCCESS);
3203a634bfcSVikram Hegde }
3213a634bfcSVikram Hegde 
3223a634bfcSVikram Hegde static dev_info_t *
get_lpc_devinfo(immu_t * immu,dev_info_t * rdip,immu_flags_t immu_flags)3233a634bfcSVikram Hegde get_lpc_devinfo(immu_t *immu, dev_info_t *rdip, immu_flags_t immu_flags)
3243a634bfcSVikram Hegde {
3253a634bfcSVikram Hegde 	dvma_arg_t dvarg = {0};
3263a634bfcSVikram Hegde 	dvarg.dva_list = &(immu->immu_dvma_lpc_list);
3273a634bfcSVikram Hegde 	dvarg.dva_rdip = rdip;
3283a634bfcSVikram Hegde 	dvarg.dva_error = DDI_FAILURE;
3293a634bfcSVikram Hegde 
3303a634bfcSVikram Hegde 	if (immu_walk_ancestor(rdip, NULL, match_lpc,
3313a634bfcSVikram Hegde 	    &dvarg, NULL, immu_flags) != DDI_SUCCESS) {
3323a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "Could not walk ancestors to "
3333a634bfcSVikram Hegde 		    "find lpc_devinfo for ISA device");
3343a634bfcSVikram Hegde 		return (NULL);
3353a634bfcSVikram Hegde 	}
3363a634bfcSVikram Hegde 
3373a634bfcSVikram Hegde 	if (dvarg.dva_error != DDI_SUCCESS || dvarg.dva_ddip == NULL) {
3383a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "Could not find lpc_devinfo for "
3393a634bfcSVikram Hegde 		    "ISA device");
3403a634bfcSVikram Hegde 		return (NULL);
3413a634bfcSVikram Hegde 	}
3423a634bfcSVikram Hegde 
3433a634bfcSVikram Hegde 	return (dvarg.dva_ddip);
3443a634bfcSVikram Hegde }
3453a634bfcSVikram Hegde 
3463a634bfcSVikram Hegde static dev_info_t *
get_gfx_devinfo(dev_info_t * rdip)3473a634bfcSVikram Hegde get_gfx_devinfo(dev_info_t *rdip)
3483a634bfcSVikram Hegde {
3493a634bfcSVikram Hegde 	immu_t *immu;
3503a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
3513a634bfcSVikram Hegde 	list_t *list_gfx;
3523a634bfcSVikram Hegde 
3533a634bfcSVikram Hegde 	/*
35450200e77SFrank Van Der Linden 	 * The GFX device may not be on the same iommu unit as "agpgart"
3553a634bfcSVikram Hegde 	 * so search globally
3563a634bfcSVikram Hegde 	 */
3573a634bfcSVikram Hegde 	immu_devi = NULL;
3583a634bfcSVikram Hegde 	immu = list_head(&immu_list);
3593a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
3603a634bfcSVikram Hegde 		list_gfx = &(immu->immu_dvma_gfx_list);
3613a634bfcSVikram Hegde 		if (!list_is_empty(list_gfx)) {
3623a634bfcSVikram Hegde 			immu_devi = list_head(list_gfx);
3633a634bfcSVikram Hegde 			break;
3643a634bfcSVikram Hegde 		}
3653a634bfcSVikram Hegde 	}
3663a634bfcSVikram Hegde 
3673a634bfcSVikram Hegde 	if (immu_devi == NULL) {
36850200e77SFrank Van Der Linden 		ddi_err(DER_WARN, rdip, "iommu: No GFX device. "
369e03dceedSVikram Hegde 		    "Cannot redirect agpgart");
3703a634bfcSVikram Hegde 		return (NULL);
3713a634bfcSVikram Hegde 	}
3723a634bfcSVikram Hegde 
37350200e77SFrank Van Der Linden 	ddi_err(DER_LOG, rdip, "iommu: GFX redirect to %s",
3743a634bfcSVikram Hegde 	    ddi_node_name(immu_devi->imd_dip));
3753a634bfcSVikram Hegde 
3763a634bfcSVikram Hegde 	return (immu_devi->imd_dip);
3773a634bfcSVikram Hegde }
3783a634bfcSVikram Hegde 
3793a634bfcSVikram Hegde static immu_flags_t
dma_to_immu_flags(struct ddi_dma_req * dmareq)3803a634bfcSVikram Hegde dma_to_immu_flags(struct ddi_dma_req *dmareq)
3813a634bfcSVikram Hegde {
3823a634bfcSVikram Hegde 	immu_flags_t flags = 0;
3833a634bfcSVikram Hegde 
3843a634bfcSVikram Hegde 	if (dmareq->dmar_fp == DDI_DMA_SLEEP) {
3853a634bfcSVikram Hegde 		flags |= IMMU_FLAGS_SLEEP;
3863a634bfcSVikram Hegde 	} else {
3873a634bfcSVikram Hegde 		flags |= IMMU_FLAGS_NOSLEEP;
3883a634bfcSVikram Hegde 	}
3893a634bfcSVikram Hegde 
390e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS
391e03dceedSVikram Hegde 
392e03dceedSVikram Hegde 	flags |= (IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
393e03dceedSVikram Hegde 
394e03dceedSVikram Hegde #else
3953a634bfcSVikram Hegde 	/*
3963a634bfcSVikram Hegde 	 * Read and write flags need to be reversed.
3973a634bfcSVikram Hegde 	 * DMA_READ means read from device and write
3983a634bfcSVikram Hegde 	 * to memory. So DMA read means DVMA write.
3993a634bfcSVikram Hegde 	 */
4003a634bfcSVikram Hegde 	if (dmareq->dmar_flags & DDI_DMA_READ)
4013a634bfcSVikram Hegde 		flags |= IMMU_FLAGS_WRITE;
4023a634bfcSVikram Hegde 
4033a634bfcSVikram Hegde 	if (dmareq->dmar_flags & DDI_DMA_WRITE)
4043a634bfcSVikram Hegde 		flags |= IMMU_FLAGS_READ;
4053a634bfcSVikram Hegde 
4063a634bfcSVikram Hegde 	/*
4073a634bfcSVikram Hegde 	 * Some buggy drivers specify neither READ or WRITE
4083a634bfcSVikram Hegde 	 * For such drivers set both read and write permissions
4093a634bfcSVikram Hegde 	 */
4103a634bfcSVikram Hegde 	if ((dmareq->dmar_flags & (DDI_DMA_READ | DDI_DMA_WRITE)) == 0) {
4113a634bfcSVikram Hegde 		flags |= (IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
4123a634bfcSVikram Hegde 	}
4133a634bfcSVikram Hegde #endif
4143a634bfcSVikram Hegde 
4153a634bfcSVikram Hegde 	return (flags);
4163a634bfcSVikram Hegde }
4173a634bfcSVikram Hegde 
41850200e77SFrank Van Der Linden /*ARGSUSED*/
419e03dceedSVikram Hegde int
pgtable_ctor(void * buf,void * arg,int kmflag)420e03dceedSVikram Hegde pgtable_ctor(void *buf, void *arg, int kmflag)
4213a634bfcSVikram Hegde {
4223a634bfcSVikram Hegde 	size_t actual_size = 0;
4233a634bfcSVikram Hegde 	pgtable_t *pgtable;
4243a634bfcSVikram Hegde 	int (*dmafp)(caddr_t);
4253a634bfcSVikram Hegde 	caddr_t vaddr;
426e03dceedSVikram Hegde 	void *next;
42750200e77SFrank Van Der Linden 	uint_t flags;
42850200e77SFrank Van Der Linden 	immu_t *immu = arg;
4293a634bfcSVikram Hegde 
430e03dceedSVikram Hegde 	pgtable = (pgtable_t *)buf;
4313a634bfcSVikram Hegde 
432e03dceedSVikram Hegde 	dmafp = (kmflag & KM_NOSLEEP) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP;
4333a634bfcSVikram Hegde 
434e03dceedSVikram Hegde 	next = kmem_zalloc(IMMU_PAGESIZE, kmflag);
435e03dceedSVikram Hegde 	if (next == NULL) {
436e03dceedSVikram Hegde 		return (-1);
4373a634bfcSVikram Hegde 	}
4383a634bfcSVikram Hegde 
4393a634bfcSVikram Hegde 	if (ddi_dma_alloc_handle(root_devinfo, &immu_dma_attr,
4403a634bfcSVikram Hegde 	    dmafp, NULL, &pgtable->hwpg_dmahdl) != DDI_SUCCESS) {
441e03dceedSVikram Hegde 		kmem_free(next, IMMU_PAGESIZE);
442e03dceedSVikram Hegde 		return (-1);
4433a634bfcSVikram Hegde 	}
4443a634bfcSVikram Hegde 
44550200e77SFrank Van Der Linden 	flags = DDI_DMA_CONSISTENT;
44650200e77SFrank Van Der Linden 	if (!immu->immu_dvma_coherent)
44750200e77SFrank Van Der Linden 		flags |= IOMEM_DATA_UC_WR_COMBINE;
44850200e77SFrank Van Der Linden 
4493a634bfcSVikram Hegde 	if (ddi_dma_mem_alloc(pgtable->hwpg_dmahdl, IMMU_PAGESIZE,
45050200e77SFrank Van Der Linden 	    &immu_acc_attr, flags,
4513a634bfcSVikram Hegde 	    dmafp, NULL, &vaddr, &actual_size,
4523a634bfcSVikram Hegde 	    &pgtable->hwpg_memhdl) != DDI_SUCCESS) {
4533a634bfcSVikram Hegde 		ddi_dma_free_handle(&pgtable->hwpg_dmahdl);
454e03dceedSVikram Hegde 		kmem_free(next, IMMU_PAGESIZE);
455e03dceedSVikram Hegde 		return (-1);
4563a634bfcSVikram Hegde 	}
4573a634bfcSVikram Hegde 
4583a634bfcSVikram Hegde 	/*
4593a634bfcSVikram Hegde 	 * Memory allocation failure. Maybe a temporary condition
4603a634bfcSVikram Hegde 	 * so return error rather than panic, so we can try again
4613a634bfcSVikram Hegde 	 */
4623a634bfcSVikram Hegde 	if (actual_size < IMMU_PAGESIZE) {
4633a634bfcSVikram Hegde 		ddi_dma_mem_free(&pgtable->hwpg_memhdl);
4643a634bfcSVikram Hegde 		ddi_dma_free_handle(&pgtable->hwpg_dmahdl);
465e03dceedSVikram Hegde 		kmem_free(next, IMMU_PAGESIZE);
466e03dceedSVikram Hegde 		return (-1);
4673a634bfcSVikram Hegde 	}
4683a634bfcSVikram Hegde 
4693a634bfcSVikram Hegde 	pgtable->hwpg_paddr = pfn_to_pa(hat_getpfnum(kas.a_hat, vaddr));
4703a634bfcSVikram Hegde 	pgtable->hwpg_vaddr = vaddr;
471e03dceedSVikram Hegde 	pgtable->swpg_next_array = next;
4723a634bfcSVikram Hegde 
473e03dceedSVikram Hegde 	rw_init(&(pgtable->swpg_rwlock), NULL, RW_DEFAULT, NULL);
4743a634bfcSVikram Hegde 
475e03dceedSVikram Hegde 	return (0);
476e03dceedSVikram Hegde }
4773a634bfcSVikram Hegde 
47850200e77SFrank Van Der Linden /*ARGSUSED*/
479e03dceedSVikram Hegde void
pgtable_dtor(void * buf,void * arg)480e03dceedSVikram Hegde pgtable_dtor(void *buf, void *arg)
481e03dceedSVikram Hegde {
482e03dceedSVikram Hegde 	pgtable_t *pgtable;
4833a634bfcSVikram Hegde 
484e03dceedSVikram Hegde 	pgtable = (pgtable_t *)buf;
485e03dceedSVikram Hegde 
486e03dceedSVikram Hegde 	/* destroy will panic if lock is held. */
487e03dceedSVikram Hegde 	rw_destroy(&(pgtable->swpg_rwlock));
488e03dceedSVikram Hegde 
489e03dceedSVikram Hegde 	ddi_dma_mem_free(&pgtable->hwpg_memhdl);
490e03dceedSVikram Hegde 	ddi_dma_free_handle(&pgtable->hwpg_dmahdl);
491e03dceedSVikram Hegde 	kmem_free(pgtable->swpg_next_array, IMMU_PAGESIZE);
4923a634bfcSVikram Hegde }
4933a634bfcSVikram Hegde 
494e03dceedSVikram Hegde /*
495e03dceedSVikram Hegde  * pgtable_alloc()
496e03dceedSVikram Hegde  *	alloc a IOMMU pgtable structure.
497e03dceedSVikram Hegde  *	This same struct is used for root and context tables as well.
498e03dceedSVikram Hegde  *	This routine allocs the f/ollowing:
499e03dceedSVikram Hegde  *	- a pgtable_t struct
500e03dceedSVikram Hegde  *	- a HW page which holds PTEs/entries which is accesssed by HW
501e03dceedSVikram Hegde  *        so we set up DMA for this page
502e03dceedSVikram Hegde  *	- a SW page which is only for our bookeeping
503e03dceedSVikram Hegde  *        (for example to  hold pointers to the next level pgtable).
504e03dceedSVikram Hegde  *        So a simple kmem_alloc suffices
505e03dceedSVikram Hegde  */
506e03dceedSVikram Hegde static pgtable_t *
pgtable_alloc(immu_t * immu,immu_flags_t immu_flags)507e03dceedSVikram Hegde pgtable_alloc(immu_t *immu, immu_flags_t immu_flags)
5083a634bfcSVikram Hegde {
509e03dceedSVikram Hegde 	pgtable_t *pgtable;
510e03dceedSVikram Hegde 	int kmflags;
511e03dceedSVikram Hegde 
512e03dceedSVikram Hegde 	kmflags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;
5133a634bfcSVikram Hegde 
51450200e77SFrank Van Der Linden 	pgtable = kmem_cache_alloc(immu->immu_pgtable_cache, kmflags);
515e03dceedSVikram Hegde 	if (pgtable == NULL) {
516e03dceedSVikram Hegde 		return (NULL);
517e03dceedSVikram Hegde 	}
518e03dceedSVikram Hegde 	return (pgtable);
519e03dceedSVikram Hegde }
5203a634bfcSVikram Hegde 
521e03dceedSVikram Hegde static void
pgtable_zero(pgtable_t * pgtable)52250200e77SFrank Van Der Linden pgtable_zero(pgtable_t *pgtable)
523e03dceedSVikram Hegde {
5243a634bfcSVikram Hegde 	bzero(pgtable->hwpg_vaddr, IMMU_PAGESIZE);
525e03dceedSVikram Hegde 	bzero(pgtable->swpg_next_array, IMMU_PAGESIZE);
526e03dceedSVikram Hegde }
527e03dceedSVikram Hegde 
528e03dceedSVikram Hegde static void
pgtable_free(immu_t * immu,pgtable_t * pgtable)529e03dceedSVikram Hegde pgtable_free(immu_t *immu, pgtable_t *pgtable)
530e03dceedSVikram Hegde {
53150200e77SFrank Van Der Linden 	kmem_cache_free(immu->immu_pgtable_cache, pgtable);
5323a634bfcSVikram Hegde }
5333a634bfcSVikram Hegde 
5343a634bfcSVikram Hegde /*
5353a634bfcSVikram Hegde  * Function to identify a display device from the PCI class code
5363a634bfcSVikram Hegde  */
5373a634bfcSVikram Hegde static boolean_t
device_is_display(uint_t classcode)5383a634bfcSVikram Hegde device_is_display(uint_t classcode)
5393a634bfcSVikram Hegde {
5403a634bfcSVikram Hegde 	static uint_t disp_classes[] = {
5413a634bfcSVikram Hegde 		0x000100,
5423a634bfcSVikram Hegde 		0x030000,
5433a634bfcSVikram Hegde 		0x030001
5443a634bfcSVikram Hegde 	};
5453a634bfcSVikram Hegde 	int i, nclasses = sizeof (disp_classes) / sizeof (uint_t);
5463a634bfcSVikram Hegde 
5473a634bfcSVikram Hegde 	for (i = 0; i < nclasses; i++) {
5483a634bfcSVikram Hegde 		if (classcode == disp_classes[i])
5493a634bfcSVikram Hegde 			return (B_TRUE);
5503a634bfcSVikram Hegde 	}
5513a634bfcSVikram Hegde 	return (B_FALSE);
5523a634bfcSVikram Hegde }
5533a634bfcSVikram Hegde 
5543a634bfcSVikram Hegde /*
5553a634bfcSVikram Hegde  * Function that determines if device is PCIEX and/or PCIEX bridge
5563a634bfcSVikram Hegde  */
5573a634bfcSVikram Hegde static boolean_t
device_is_pciex(uchar_t bus,uchar_t dev,uchar_t func,boolean_t * is_pcib)5583a634bfcSVikram Hegde device_is_pciex(
5593a634bfcSVikram Hegde 	uchar_t bus, uchar_t dev, uchar_t func, boolean_t *is_pcib)
5603a634bfcSVikram Hegde {
5613a634bfcSVikram Hegde 	ushort_t cap;
5623a634bfcSVikram Hegde 	ushort_t capsp;
5633a634bfcSVikram Hegde 	ushort_t cap_count = PCI_CAP_MAX_PTR;
5643a634bfcSVikram Hegde 	ushort_t status;
5653a634bfcSVikram Hegde 	boolean_t is_pciex = B_FALSE;
5663a634bfcSVikram Hegde 
5673a634bfcSVikram Hegde 	*is_pcib = B_FALSE;
5683a634bfcSVikram Hegde 
5693a634bfcSVikram Hegde 	status = pci_getw_func(bus, dev, func, PCI_CONF_STAT);
5703a634bfcSVikram Hegde 	if (!(status & PCI_STAT_CAP))
5713a634bfcSVikram Hegde 		return (B_FALSE);
5723a634bfcSVikram Hegde 
5733a634bfcSVikram Hegde 	capsp = pci_getb_func(bus, dev, func, PCI_CONF_CAP_PTR);
5743a634bfcSVikram Hegde 	while (cap_count-- && capsp >= PCI_CAP_PTR_OFF) {
5753a634bfcSVikram Hegde 		capsp &= PCI_CAP_PTR_MASK;
5763a634bfcSVikram Hegde 		cap = pci_getb_func(bus, dev, func, capsp);
5773a634bfcSVikram Hegde 
5783a634bfcSVikram Hegde 		if (cap == PCI_CAP_ID_PCI_E) {
5793a634bfcSVikram Hegde 			status = pci_getw_func(bus, dev, func, capsp + 2);
5803a634bfcSVikram Hegde 			/*
5813a634bfcSVikram Hegde 			 * See section 7.8.2 of PCI-Express Base Spec v1.0a
5823a634bfcSVikram Hegde 			 * for Device/Port Type.
5833a634bfcSVikram Hegde 			 * PCIE_PCIECAP_DEV_TYPE_PCIE2PCI implies that the
5843a634bfcSVikram Hegde 			 * device is a PCIE2PCI bridge
5853a634bfcSVikram Hegde 			 */
5863a634bfcSVikram Hegde 			*is_pcib =
5873a634bfcSVikram Hegde 			    ((status & PCIE_PCIECAP_DEV_TYPE_MASK) ==
5883a634bfcSVikram Hegde 			    PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ? B_TRUE : B_FALSE;
5893a634bfcSVikram Hegde 			is_pciex = B_TRUE;
5903a634bfcSVikram Hegde 		}
5913a634bfcSVikram Hegde 
5923a634bfcSVikram Hegde 		capsp = (*pci_getb_func)(bus, dev, func,
5933a634bfcSVikram Hegde 		    capsp + PCI_CAP_NEXT_PTR);
5943a634bfcSVikram Hegde 	}
5953a634bfcSVikram Hegde 
5963a634bfcSVikram Hegde 	return (is_pciex);
5973a634bfcSVikram Hegde }
5983a634bfcSVikram Hegde 
59950200e77SFrank Van Der Linden static boolean_t
device_use_premap(uint_t classcode)60050200e77SFrank Van Der Linden device_use_premap(uint_t classcode)
60150200e77SFrank Van Der Linden {
60250200e77SFrank Van Der Linden 	if (IMMU_PCI_CLASS2BASE(classcode) == PCI_CLASS_NET)
60350200e77SFrank Van Der Linden 		return (B_TRUE);
60450200e77SFrank Van Der Linden 	return (B_FALSE);
60550200e77SFrank Van Der Linden }
60650200e77SFrank Van Der Linden 
6073a634bfcSVikram Hegde 
6083a634bfcSVikram Hegde /*
6093a634bfcSVikram Hegde  * immu_dvma_get_immu()
6103a634bfcSVikram Hegde  *   get the immu unit structure for a dev_info node
6113a634bfcSVikram Hegde  */
6123a634bfcSVikram Hegde immu_t *
immu_dvma_get_immu(dev_info_t * dip,immu_flags_t immu_flags)6133a634bfcSVikram Hegde immu_dvma_get_immu(dev_info_t *dip, immu_flags_t immu_flags)
6143a634bfcSVikram Hegde {
6153a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
6163a634bfcSVikram Hegde 	immu_t *immu;
6173a634bfcSVikram Hegde 
6183a634bfcSVikram Hegde 	/*
6193a634bfcSVikram Hegde 	 * check if immu unit was already found earlier.
6203a634bfcSVikram Hegde 	 * If yes, then it will be stashed in immu_devi struct.
6213a634bfcSVikram Hegde 	 */
6223a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
6233a634bfcSVikram Hegde 	if (immu_devi == NULL) {
6243a634bfcSVikram Hegde 		if (immu_devi_set(dip, immu_flags) != DDI_SUCCESS) {
6253a634bfcSVikram Hegde 			/*
6263a634bfcSVikram Hegde 			 * May fail because of low memory. Return error rather
6273a634bfcSVikram Hegde 			 * than panic as we want driver to rey again later
6283a634bfcSVikram Hegde 			 */
6293a634bfcSVikram Hegde 			ddi_err(DER_PANIC, dip, "immu_dvma_get_immu: "
6303a634bfcSVikram Hegde 			    "No immu_devi structure");
6313a634bfcSVikram Hegde 			/*NOTREACHED*/
6323a634bfcSVikram Hegde 		}
6333a634bfcSVikram Hegde 		immu_devi = immu_devi_get(dip);
6343a634bfcSVikram Hegde 	}
6353a634bfcSVikram Hegde 
6363a634bfcSVikram Hegde 	mutex_enter(&(DEVI(dip)->devi_lock));
6373a634bfcSVikram Hegde 	if (immu_devi->imd_immu) {
6383a634bfcSVikram Hegde 		immu = immu_devi->imd_immu;
6393a634bfcSVikram Hegde 		mutex_exit(&(DEVI(dip)->devi_lock));
6403a634bfcSVikram Hegde 		return (immu);
6413a634bfcSVikram Hegde 	}
6423a634bfcSVikram Hegde 	mutex_exit(&(DEVI(dip)->devi_lock));
6433a634bfcSVikram Hegde 
6443a634bfcSVikram Hegde 	immu = immu_dmar_get_immu(dip);
6453a634bfcSVikram Hegde 	if (immu == NULL) {
6463a634bfcSVikram Hegde 		ddi_err(DER_PANIC, dip, "immu_dvma_get_immu: "
6473a634bfcSVikram Hegde 		    "Cannot find immu_t for device");
6483a634bfcSVikram Hegde 		/*NOTREACHED*/
6493a634bfcSVikram Hegde 	}
6503a634bfcSVikram Hegde 
6513a634bfcSVikram Hegde 	/*
6523a634bfcSVikram Hegde 	 * Check if some other thread found immu
6533a634bfcSVikram Hegde 	 * while lock was not held
6543a634bfcSVikram Hegde 	 */
6553a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
6563a634bfcSVikram Hegde 	/* immu_devi should be present as we found it earlier */
6573a634bfcSVikram Hegde 	if (immu_devi == NULL) {
6583a634bfcSVikram Hegde 		ddi_err(DER_PANIC, dip,
6593a634bfcSVikram Hegde 		    "immu_dvma_get_immu: No immu_devi structure");
6603a634bfcSVikram Hegde 		/*NOTREACHED*/
6613a634bfcSVikram Hegde 	}
6623a634bfcSVikram Hegde 
6633a634bfcSVikram Hegde 	mutex_enter(&(DEVI(dip)->devi_lock));
6643a634bfcSVikram Hegde 	if (immu_devi->imd_immu == NULL) {
6653a634bfcSVikram Hegde 		/* nobody else set it, so we should do it */
6663a634bfcSVikram Hegde 		immu_devi->imd_immu = immu;
6673a634bfcSVikram Hegde 		immu_devi_set_spclist(dip, immu);
6683a634bfcSVikram Hegde 	} else {
6693a634bfcSVikram Hegde 		/*
6703a634bfcSVikram Hegde 		 * if some other thread got immu before
6713a634bfcSVikram Hegde 		 * us, it should get the same results
6723a634bfcSVikram Hegde 		 */
6733a634bfcSVikram Hegde 		if (immu_devi->imd_immu != immu) {
6743a634bfcSVikram Hegde 			ddi_err(DER_PANIC, dip, "Multiple "
6753a634bfcSVikram Hegde 			    "immu units found for device. Expected (%p), "
6763a634bfcSVikram Hegde 			    "actual (%p)", (void *)immu,
6773a634bfcSVikram Hegde 			    (void *)immu_devi->imd_immu);
6783a634bfcSVikram Hegde 			mutex_exit(&(DEVI(dip)->devi_lock));
6793a634bfcSVikram Hegde 			/*NOTREACHED*/
6803a634bfcSVikram Hegde 		}
6813a634bfcSVikram Hegde 	}
6823a634bfcSVikram Hegde 	mutex_exit(&(DEVI(dip)->devi_lock));
6833a634bfcSVikram Hegde 
6843a634bfcSVikram Hegde 	return (immu);
6853a634bfcSVikram Hegde }
6863a634bfcSVikram Hegde 
6873a634bfcSVikram Hegde 
6883a634bfcSVikram Hegde /* ############################# IMMU_DEVI code ############################ */
6893a634bfcSVikram Hegde 
6903a634bfcSVikram Hegde /*
6913a634bfcSVikram Hegde  * Allocate a immu_devi structure and initialize it
6923a634bfcSVikram Hegde  */
6933a634bfcSVikram Hegde static immu_devi_t *
create_immu_devi(dev_info_t * rdip,int bus,int dev,int func,immu_flags_t immu_flags)6943a634bfcSVikram Hegde create_immu_devi(dev_info_t *rdip, int bus, int dev, int func,
6953a634bfcSVikram Hegde     immu_flags_t immu_flags)
6963a634bfcSVikram Hegde {
6973a634bfcSVikram Hegde 	uchar_t baseclass, subclass;
6983a634bfcSVikram Hegde 	uint_t classcode, revclass;
6993a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
7003a634bfcSVikram Hegde 	boolean_t pciex = B_FALSE;
7013a634bfcSVikram Hegde 	int kmflags;
7023a634bfcSVikram Hegde 	boolean_t is_pcib = B_FALSE;
7033a634bfcSVikram Hegde 
7043a634bfcSVikram Hegde 	/* bus ==  -1 indicate non-PCI device (no BDF) */
7053a634bfcSVikram Hegde 	ASSERT(bus == -1 || bus >= 0);
7063a634bfcSVikram Hegde 	ASSERT(dev >= 0);
7073a634bfcSVikram Hegde 	ASSERT(func >= 0);
7083a634bfcSVikram Hegde 
7093a634bfcSVikram Hegde 	kmflags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;
7103a634bfcSVikram Hegde 	immu_devi = kmem_zalloc(sizeof (immu_devi_t), kmflags);
7113a634bfcSVikram Hegde 	if (immu_devi == NULL) {
7123a634bfcSVikram Hegde 		ddi_err(DER_WARN, rdip, "Failed to allocate memory for "
7133a634bfcSVikram Hegde 		    "Intel IOMMU immu_devi structure");
7143a634bfcSVikram Hegde 		return (NULL);
7153a634bfcSVikram Hegde 	}
7163a634bfcSVikram Hegde 	immu_devi->imd_dip = rdip;
7173a634bfcSVikram Hegde 	immu_devi->imd_seg = 0; /* Currently seg can only be 0 */
7183a634bfcSVikram Hegde 	immu_devi->imd_bus = bus;
7193a634bfcSVikram Hegde 	immu_devi->imd_pcib_type = IMMU_PCIB_BAD;
7203a634bfcSVikram Hegde 
7213a634bfcSVikram Hegde 	if (bus == -1) {
7223a634bfcSVikram Hegde 		immu_devi->imd_pcib_type = IMMU_PCIB_NOBDF;
7233a634bfcSVikram Hegde 		return (immu_devi);
7243a634bfcSVikram Hegde 	}
7253a634bfcSVikram Hegde 
7263a634bfcSVikram Hegde 	immu_devi->imd_devfunc = IMMU_PCI_DEVFUNC(dev, func);
7273a634bfcSVikram Hegde 	immu_devi->imd_sec = 0;
7283a634bfcSVikram Hegde 	immu_devi->imd_sub = 0;
7293a634bfcSVikram Hegde 
7303a634bfcSVikram Hegde 	revclass = pci_getl_func(bus, dev, func, PCI_CONF_REVID);
7313a634bfcSVikram Hegde 
7323a634bfcSVikram Hegde 	classcode = IMMU_PCI_REV2CLASS(revclass);
7333a634bfcSVikram Hegde 	baseclass = IMMU_PCI_CLASS2BASE(classcode);
7343a634bfcSVikram Hegde 	subclass = IMMU_PCI_CLASS2SUB(classcode);
7353a634bfcSVikram Hegde 
7363a634bfcSVikram Hegde 	if (baseclass == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
7373a634bfcSVikram Hegde 
7383a634bfcSVikram Hegde 		immu_devi->imd_sec = pci_getb_func(bus, dev, func,
7393a634bfcSVikram Hegde 		    PCI_BCNF_SECBUS);
7403a634bfcSVikram Hegde 		immu_devi->imd_sub = pci_getb_func(bus, dev, func,
7413a634bfcSVikram Hegde 		    PCI_BCNF_SUBBUS);
7423a634bfcSVikram Hegde 
7433a634bfcSVikram Hegde 		pciex = device_is_pciex(bus, dev, func, &is_pcib);
7443a634bfcSVikram Hegde 		if (pciex  == B_TRUE && is_pcib == B_TRUE) {
7453a634bfcSVikram Hegde 			immu_devi->imd_pcib_type = IMMU_PCIB_PCIE_PCI;
7463a634bfcSVikram Hegde 		} else if (pciex == B_TRUE) {
7473a634bfcSVikram Hegde 			immu_devi->imd_pcib_type = IMMU_PCIB_PCIE_PCIE;
7483a634bfcSVikram Hegde 		} else {
7493a634bfcSVikram Hegde 			immu_devi->imd_pcib_type = IMMU_PCIB_PCI_PCI;
7503a634bfcSVikram Hegde 		}
7513a634bfcSVikram Hegde 	} else {
7523a634bfcSVikram Hegde 		immu_devi->imd_pcib_type = IMMU_PCIB_ENDPOINT;
7533a634bfcSVikram Hegde 	}
7543a634bfcSVikram Hegde 
7553a634bfcSVikram Hegde 	/* check for certain special devices */
7563a634bfcSVikram Hegde 	immu_devi->imd_display = device_is_display(classcode);
7573a634bfcSVikram Hegde 	immu_devi->imd_lpc = ((baseclass == PCI_CLASS_BRIDGE) &&
7583a634bfcSVikram Hegde 	    (subclass == PCI_BRIDGE_ISA)) ? B_TRUE : B_FALSE;
75950200e77SFrank Van Der Linden 	immu_devi->imd_use_premap = device_use_premap(classcode);
7603a634bfcSVikram Hegde 
7613a634bfcSVikram Hegde 	immu_devi->imd_domain = NULL;
7623a634bfcSVikram Hegde 
7639e986f0eSFrank Van Der Linden 	immu_devi->imd_dvma_flags = immu_global_dvma_flags;
7649e986f0eSFrank Van Der Linden 
7653a634bfcSVikram Hegde 	return (immu_devi);
7663a634bfcSVikram Hegde }
7673a634bfcSVikram Hegde 
7683a634bfcSVikram Hegde static void
destroy_immu_devi(immu_devi_t * immu_devi)7693a634bfcSVikram Hegde destroy_immu_devi(immu_devi_t *immu_devi)
7703a634bfcSVikram Hegde {
7713a634bfcSVikram Hegde 	kmem_free(immu_devi, sizeof (immu_devi_t));
7723a634bfcSVikram Hegde }
7733a634bfcSVikram Hegde 
7743a634bfcSVikram Hegde static domain_t *
immu_devi_domain(dev_info_t * rdip,dev_info_t ** ddipp)7753a634bfcSVikram Hegde immu_devi_domain(dev_info_t *rdip, dev_info_t **ddipp)
7763a634bfcSVikram Hegde {
7773a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
7783a634bfcSVikram Hegde 	domain_t *domain;
7793a634bfcSVikram Hegde 	dev_info_t *ddip;
7803a634bfcSVikram Hegde 
7813a634bfcSVikram Hegde 	*ddipp = NULL;
7823a634bfcSVikram Hegde 
7833a634bfcSVikram Hegde 	immu_devi = immu_devi_get(rdip);
7843a634bfcSVikram Hegde 	if (immu_devi == NULL) {
7853a634bfcSVikram Hegde 		return (NULL);
7863a634bfcSVikram Hegde 	}
7873a634bfcSVikram Hegde 
7883a634bfcSVikram Hegde 	mutex_enter(&(DEVI(rdip)->devi_lock));
7893a634bfcSVikram Hegde 	domain = immu_devi->imd_domain;
7903a634bfcSVikram Hegde 	ddip = immu_devi->imd_ddip;
7913a634bfcSVikram Hegde 	mutex_exit(&(DEVI(rdip)->devi_lock));
7923a634bfcSVikram Hegde 
79350200e77SFrank Van Der Linden 	if (domain)
7943a634bfcSVikram Hegde 		*ddipp = ddip;
7953a634bfcSVikram Hegde 
7963a634bfcSVikram Hegde 	return (domain);
7973a634bfcSVikram Hegde 
7983a634bfcSVikram Hegde }
7993a634bfcSVikram Hegde 
8003a634bfcSVikram Hegde /* ############################# END IMMU_DEVI code ######################## */
8013a634bfcSVikram Hegde /* ############################# DOMAIN code ############################### */
8023a634bfcSVikram Hegde 
8033a634bfcSVikram Hegde /*
8043a634bfcSVikram Hegde  * This routine always succeeds
8053a634bfcSVikram Hegde  */
8063a634bfcSVikram Hegde static int
did_alloc(immu_t * immu,dev_info_t * rdip,dev_info_t * ddip,immu_flags_t immu_flags)8073a634bfcSVikram Hegde did_alloc(immu_t *immu, dev_info_t *rdip,
8083a634bfcSVikram Hegde     dev_info_t *ddip, immu_flags_t immu_flags)
8093a634bfcSVikram Hegde {
8103a634bfcSVikram Hegde 	int did;
8113a634bfcSVikram Hegde 
8123a634bfcSVikram Hegde 	did = (uintptr_t)vmem_alloc(immu->immu_did_arena, 1,
8133a634bfcSVikram Hegde 	    (immu_flags & IMMU_FLAGS_NOSLEEP) ? VM_NOSLEEP : VM_SLEEP);
8143a634bfcSVikram Hegde 
8153a634bfcSVikram Hegde 	if (did == 0) {
8163a634bfcSVikram Hegde 		ddi_err(DER_WARN, rdip, "device domain-id alloc error"
8173a634bfcSVikram Hegde 		    " domain-device: %s%d. immu unit is %s. Using "
8183a634bfcSVikram Hegde 		    "unity domain with domain-id (%d)",
8193a634bfcSVikram Hegde 		    ddi_driver_name(ddip), ddi_get_instance(ddip),
8203a634bfcSVikram Hegde 		    immu->immu_name, immu->immu_unity_domain->dom_did);
8213a634bfcSVikram Hegde 		did = immu->immu_unity_domain->dom_did;
8223a634bfcSVikram Hegde 	}
8233a634bfcSVikram Hegde 
8243a634bfcSVikram Hegde 	return (did);
8253a634bfcSVikram Hegde }
8263a634bfcSVikram Hegde 
8273a634bfcSVikram Hegde static int
get_branch_domain(dev_info_t * pdip,void * arg)8283a634bfcSVikram Hegde get_branch_domain(dev_info_t *pdip, void *arg)
8293a634bfcSVikram Hegde {
8303a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
8313a634bfcSVikram Hegde 	domain_t *domain;
8323a634bfcSVikram Hegde 	dev_info_t *ddip;
8333a634bfcSVikram Hegde 	immu_t *immu;
8343a634bfcSVikram Hegde 	dvma_arg_t *dvp = (dvma_arg_t *)arg;
8353a634bfcSVikram Hegde 
8363a634bfcSVikram Hegde 	/*
8373a634bfcSVikram Hegde 	 * The field dvp->dva_rdip is a work-in-progress
8383a634bfcSVikram Hegde 	 * and gets updated as we walk up the ancestor
8393a634bfcSVikram Hegde 	 * tree. The final ddip is set only when we reach
8403a634bfcSVikram Hegde 	 * the top of the tree. So the dvp->dva_ddip field cannot
8413a634bfcSVikram Hegde 	 * be relied on until we reach the top of the field.
8423a634bfcSVikram Hegde 	 */
8433a634bfcSVikram Hegde 
8443a634bfcSVikram Hegde 	/* immu_devi may not be set. */
8453a634bfcSVikram Hegde 	immu_devi = immu_devi_get(pdip);
8463a634bfcSVikram Hegde 	if (immu_devi == NULL) {
8473a634bfcSVikram Hegde 		if (immu_devi_set(pdip, dvp->dva_flags) != DDI_SUCCESS) {
8483a634bfcSVikram Hegde 			dvp->dva_error = DDI_FAILURE;
8493a634bfcSVikram Hegde 			return (DDI_WALK_TERMINATE);
8503a634bfcSVikram Hegde 		}
8513a634bfcSVikram Hegde 	}
8523a634bfcSVikram Hegde 
8533a634bfcSVikram Hegde 	immu_devi = immu_devi_get(pdip);
8543a634bfcSVikram Hegde 	immu = immu_devi->imd_immu;
85550200e77SFrank Van Der Linden 	if (immu == NULL)
8563a634bfcSVikram Hegde 		immu = immu_dvma_get_immu(pdip, dvp->dva_flags);
8573a634bfcSVikram Hegde 
8583a634bfcSVikram Hegde 	/*
8593a634bfcSVikram Hegde 	 * If we encounter a PCIE_PCIE bridge *ANCESTOR* we need to
8603a634bfcSVikram Hegde 	 * terminate the walk (since the device under the PCIE bridge
8613a634bfcSVikram Hegde 	 * is a PCIE device and has an independent entry in the
8623a634bfcSVikram Hegde 	 * root/context table)
8633a634bfcSVikram Hegde 	 */
8643a634bfcSVikram Hegde 	if (dvp->dva_rdip != pdip &&
8653a634bfcSVikram Hegde 	    immu_devi->imd_pcib_type == IMMU_PCIB_PCIE_PCIE) {
8663a634bfcSVikram Hegde 		return (DDI_WALK_TERMINATE);
8673a634bfcSVikram Hegde 	}
8683a634bfcSVikram Hegde 
8693a634bfcSVikram Hegde 	/*
8703a634bfcSVikram Hegde 	 * In order to be a domain-dim, it must be a PCI device i.e.
8713a634bfcSVikram Hegde 	 * must have valid BDF. This also eliminates the root complex.
8723a634bfcSVikram Hegde 	 */
8733a634bfcSVikram Hegde 	if (immu_devi->imd_pcib_type != IMMU_PCIB_BAD &&
8743a634bfcSVikram Hegde 	    immu_devi->imd_pcib_type != IMMU_PCIB_NOBDF) {
8753a634bfcSVikram Hegde 		ASSERT(immu_devi->imd_bus >= 0);
8763a634bfcSVikram Hegde 		ASSERT(immu_devi->imd_devfunc >= 0);
8773a634bfcSVikram Hegde 		dvp->dva_ddip = pdip;
8783a634bfcSVikram Hegde 	}
8793a634bfcSVikram Hegde 
8803a634bfcSVikram Hegde 	if (immu_devi->imd_display == B_TRUE ||
8813a634bfcSVikram Hegde 	    (dvp->dva_flags & IMMU_FLAGS_UNITY)) {
8823a634bfcSVikram Hegde 		dvp->dva_domain = immu->immu_unity_domain;
8833a634bfcSVikram Hegde 		/* continue walking to find ddip */
8843a634bfcSVikram Hegde 		return (DDI_WALK_CONTINUE);
8853a634bfcSVikram Hegde 	}
8863a634bfcSVikram Hegde 
8873a634bfcSVikram Hegde 	mutex_enter(&(DEVI(pdip)->devi_lock));
8883a634bfcSVikram Hegde 	domain = immu_devi->imd_domain;
8893a634bfcSVikram Hegde 	ddip = immu_devi->imd_ddip;
8903a634bfcSVikram Hegde 	mutex_exit(&(DEVI(pdip)->devi_lock));
8913a634bfcSVikram Hegde 
8923a634bfcSVikram Hegde 	if (domain && ddip) {
8933a634bfcSVikram Hegde 		/* if domain is set, it must be the same */
8943a634bfcSVikram Hegde 		if (dvp->dva_domain) {
8953a634bfcSVikram Hegde 			ASSERT(domain == dvp->dva_domain);
8963a634bfcSVikram Hegde 		}
8973a634bfcSVikram Hegde 		dvp->dva_domain = domain;
8983a634bfcSVikram Hegde 		dvp->dva_ddip = ddip;
8993a634bfcSVikram Hegde 		return (DDI_WALK_TERMINATE);
9003a634bfcSVikram Hegde 	}
9013a634bfcSVikram Hegde 
9023a634bfcSVikram Hegde 	/* Domain may already be set, continue walking so that ddip gets set */
9033a634bfcSVikram Hegde 	if (dvp->dva_domain) {
9043a634bfcSVikram Hegde 		return (DDI_WALK_CONTINUE);
9053a634bfcSVikram Hegde 	}
9063a634bfcSVikram Hegde 
9073a634bfcSVikram Hegde 	/* domain is not set in either immu_devi or dvp */
9083a634bfcSVikram Hegde 	domain = bdf_domain_lookup(immu_devi);
9093a634bfcSVikram Hegde 	if (domain == NULL) {
9103a634bfcSVikram Hegde 		return (DDI_WALK_CONTINUE);
9113a634bfcSVikram Hegde 	}
9123a634bfcSVikram Hegde 
9133a634bfcSVikram Hegde 	/* ok, the BDF hash had a domain for this BDF. */
9143a634bfcSVikram Hegde 
9153a634bfcSVikram Hegde 	/* Grab lock again to check if something else set immu_devi fields */
9163a634bfcSVikram Hegde 	mutex_enter(&(DEVI(pdip)->devi_lock));
9173a634bfcSVikram Hegde 	if (immu_devi->imd_domain != NULL) {
9183a634bfcSVikram Hegde 		dvp->dva_domain = domain;
9193a634bfcSVikram Hegde 	} else {
9203a634bfcSVikram Hegde 		dvp->dva_domain = domain;
9213a634bfcSVikram Hegde 	}
9223a634bfcSVikram Hegde 	mutex_exit(&(DEVI(pdip)->devi_lock));
9233a634bfcSVikram Hegde 
9243a634bfcSVikram Hegde 	/*
9253a634bfcSVikram Hegde 	 * walk upwards until the topmost PCI bridge is found
9263a634bfcSVikram Hegde 	 */
9273a634bfcSVikram Hegde 	return (DDI_WALK_CONTINUE);
928e03dceedSVikram Hegde 
9293a634bfcSVikram Hegde }
9303a634bfcSVikram Hegde 
9313a634bfcSVikram Hegde static void
map_unity_domain(domain_t * domain)9323a634bfcSVikram Hegde map_unity_domain(domain_t *domain)
9333a634bfcSVikram Hegde {
9343a634bfcSVikram Hegde 	struct memlist *mp;
9353a634bfcSVikram Hegde 	uint64_t start;
9363a634bfcSVikram Hegde 	uint64_t npages;
93750200e77SFrank Van Der Linden 	immu_dcookie_t dcookies[1] = {0};
938e03dceedSVikram Hegde 	int dcount = 0;
9393a634bfcSVikram Hegde 
9403a634bfcSVikram Hegde 	/*
9413a634bfcSVikram Hegde 	 * UNITY arenas are a mirror of the physical memory
9423a634bfcSVikram Hegde 	 * installed on the system.
9433a634bfcSVikram Hegde 	 */
9443a634bfcSVikram Hegde 
9453a634bfcSVikram Hegde #ifdef BUGGY_DRIVERS
9463a634bfcSVikram Hegde 	/*
9473a634bfcSVikram Hegde 	 * Dont skip page0. Some broken HW/FW access it.
9483a634bfcSVikram Hegde 	 */
949e03dceedSVikram Hegde 	dcookies[0].dck_paddr = 0;
950e03dceedSVikram Hegde 	dcookies[0].dck_npages = 1;
951e03dceedSVikram Hegde 	dcount = 1;
95250200e77SFrank Van Der Linden 	(void) dvma_map(domain, 0, 1, dcookies, dcount, NULL,
9533a634bfcSVikram Hegde 	    IMMU_FLAGS_READ | IMMU_FLAGS_WRITE | IMMU_FLAGS_PAGE1);
9543a634bfcSVikram Hegde #endif
9553a634bfcSVikram Hegde 
9563a634bfcSVikram Hegde 	memlist_read_lock();
9573a634bfcSVikram Hegde 
9583a634bfcSVikram Hegde 	mp = phys_install;
9593a634bfcSVikram Hegde 
9603a634bfcSVikram Hegde 	if (mp->ml_address == 0) {
9613a634bfcSVikram Hegde 		/* since we already mapped page1 above */
9623a634bfcSVikram Hegde 		start = IMMU_PAGESIZE;
9633a634bfcSVikram Hegde 	} else {
9643a634bfcSVikram Hegde 		start = mp->ml_address;
9653a634bfcSVikram Hegde 	}
9663a634bfcSVikram Hegde 	npages = mp->ml_size/IMMU_PAGESIZE + 1;
9673a634bfcSVikram Hegde 
968e03dceedSVikram Hegde 	dcookies[0].dck_paddr = start;
969e03dceedSVikram Hegde 	dcookies[0].dck_npages = npages;
970e03dceedSVikram Hegde 	dcount = 1;
97150200e77SFrank Van Der Linden 	(void) dvma_map(domain, start, npages, dcookies,
972e03dceedSVikram Hegde 	    dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
9733a634bfcSVikram Hegde 
97450200e77SFrank Van Der Linden 	ddi_err(DER_LOG, domain->dom_dip, "iommu: mapping PHYS span [0x%" PRIx64
9753a634bfcSVikram Hegde 	    " - 0x%" PRIx64 "]", start, start + mp->ml_size);
9763a634bfcSVikram Hegde 
9773a634bfcSVikram Hegde 	mp = mp->ml_next;
9783a634bfcSVikram Hegde 	while (mp) {
97950200e77SFrank Van Der Linden 		ddi_err(DER_LOG, domain->dom_dip,
98050200e77SFrank Van Der Linden 		    "iommu: mapping PHYS span [0x%" PRIx64 " - 0x%" PRIx64 "]",
98150200e77SFrank Van Der Linden 		    mp->ml_address, mp->ml_address + mp->ml_size);
9823a634bfcSVikram Hegde 
9833a634bfcSVikram Hegde 		start = mp->ml_address;
9843a634bfcSVikram Hegde 		npages = mp->ml_size/IMMU_PAGESIZE + 1;
9853a634bfcSVikram Hegde 
986e03dceedSVikram Hegde 		dcookies[0].dck_paddr = start;
987e03dceedSVikram Hegde 		dcookies[0].dck_npages = npages;
988e03dceedSVikram Hegde 		dcount = 1;
98950200e77SFrank Van Der Linden 		(void) dvma_map(domain, start, npages,
990e03dceedSVikram Hegde 		    dcookies, dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
9913a634bfcSVikram Hegde 		mp = mp->ml_next;
9923a634bfcSVikram Hegde 	}
9933a634bfcSVikram Hegde 
9943a634bfcSVikram Hegde 	mp = bios_rsvd;
9953a634bfcSVikram Hegde 	while (mp) {
99650200e77SFrank Van Der Linden 		ddi_err(DER_LOG, domain->dom_dip,
99750200e77SFrank Van Der Linden 		    "iommu: mapping PHYS span [0x%" PRIx64 " - 0x%" PRIx64 "]",
99850200e77SFrank Van Der Linden 		    mp->ml_address, mp->ml_address + mp->ml_size);
9993a634bfcSVikram Hegde 
10003a634bfcSVikram Hegde 		start = mp->ml_address;
10013a634bfcSVikram Hegde 		npages = mp->ml_size/IMMU_PAGESIZE + 1;
10023a634bfcSVikram Hegde 
1003e03dceedSVikram Hegde 		dcookies[0].dck_paddr = start;
1004e03dceedSVikram Hegde 		dcookies[0].dck_npages = npages;
1005e03dceedSVikram Hegde 		dcount = 1;
100650200e77SFrank Van Der Linden 		(void) dvma_map(domain, start, npages,
1007e03dceedSVikram Hegde 		    dcookies, dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
10083a634bfcSVikram Hegde 
10093a634bfcSVikram Hegde 		mp = mp->ml_next;
10103a634bfcSVikram Hegde 	}
10113a634bfcSVikram Hegde 
10123a634bfcSVikram Hegde 	memlist_read_unlock();
10133a634bfcSVikram Hegde }
10143a634bfcSVikram Hegde 
10153a634bfcSVikram Hegde /*
10163a634bfcSVikram Hegde  * create_xlate_arena()
1017*584b574aSToomas Soome  *	Create the dvma arena for a domain with translation
10183a634bfcSVikram Hegde  *	mapping
10193a634bfcSVikram Hegde  */
10203a634bfcSVikram Hegde static void
create_xlate_arena(immu_t * immu,domain_t * domain,dev_info_t * rdip,immu_flags_t immu_flags)10213a634bfcSVikram Hegde create_xlate_arena(immu_t *immu, domain_t *domain,
10223a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags)
10233a634bfcSVikram Hegde {
10243a634bfcSVikram Hegde 	char *arena_name;
10253a634bfcSVikram Hegde 	struct memlist *mp;
10263a634bfcSVikram Hegde 	int vmem_flags;
10273a634bfcSVikram Hegde 	uint64_t start;
10283a634bfcSVikram Hegde 	uint_t mgaw;
10293a634bfcSVikram Hegde 	uint64_t size;
10303a634bfcSVikram Hegde 	uint64_t maxaddr;
10313a634bfcSVikram Hegde 	void *vmem_ret;
10323a634bfcSVikram Hegde 
10333a634bfcSVikram Hegde 	arena_name = domain->dom_dvma_arena_name;
10343a634bfcSVikram Hegde 
10353a634bfcSVikram Hegde 	/* Note, don't do sizeof (arena_name) - it is just a pointer */
10363a634bfcSVikram Hegde 	(void) snprintf(arena_name,
10373a634bfcSVikram Hegde 	    sizeof (domain->dom_dvma_arena_name),
10383a634bfcSVikram Hegde 	    "%s-domain-%d-xlate-DVMA-arena", immu->immu_name,
10393a634bfcSVikram Hegde 	    domain->dom_did);
10403a634bfcSVikram Hegde 
10413a634bfcSVikram Hegde 	vmem_flags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? VM_NOSLEEP : VM_SLEEP;
10423a634bfcSVikram Hegde 
10433a634bfcSVikram Hegde 	/* Restrict mgaddr (max guest addr) to MGAW */
10443a634bfcSVikram Hegde 	mgaw = IMMU_CAP_MGAW(immu->immu_regs_cap);
10453a634bfcSVikram Hegde 
10463a634bfcSVikram Hegde 	/*
10473a634bfcSVikram Hegde 	 * To ensure we avoid ioapic and PCI MMIO ranges we just
10483a634bfcSVikram Hegde 	 * use the physical memory address range of the system as the
10493a634bfcSVikram Hegde 	 * range
10503a634bfcSVikram Hegde 	 */
10513a634bfcSVikram Hegde 	maxaddr = ((uint64_t)1 << mgaw);
10523a634bfcSVikram Hegde 
1053e03dceedSVikram Hegde 	memlist_read_lock();
1054e03dceedSVikram Hegde 
1055e03dceedSVikram Hegde 	mp = phys_install;
10563a634bfcSVikram Hegde 
1057e03dceedSVikram Hegde 	if (mp->ml_address == 0)
10583a634bfcSVikram Hegde 		start = MMU_PAGESIZE;
1059e03dceedSVikram Hegde 	else
1060e03dceedSVikram Hegde 		start = mp->ml_address;
1061e03dceedSVikram Hegde 
1062e03dceedSVikram Hegde 	if (start + mp->ml_size > maxaddr)
10633a634bfcSVikram Hegde 		size = maxaddr - start;
1064e03dceedSVikram Hegde 	else
1065e03dceedSVikram Hegde 		size = mp->ml_size;
10663a634bfcSVikram Hegde 
1067e03dceedSVikram Hegde 	ddi_err(DER_VERB, rdip,
106850200e77SFrank Van Der Linden 	    "iommu: %s: Creating dvma vmem arena [0x%" PRIx64
1069e03dceedSVikram Hegde 	    " - 0x%" PRIx64 "]", arena_name, start, start + size);
10703a634bfcSVikram Hegde 
1071e03dceedSVikram Hegde 	/*
1072e03dceedSVikram Hegde 	 * We always allocate in quanta of IMMU_PAGESIZE
1073e03dceedSVikram Hegde 	 */
1074e03dceedSVikram Hegde 	domain->dom_dvma_arena = vmem_create(arena_name,
1075e03dceedSVikram Hegde 	    (void *)(uintptr_t)start,	/* start addr */
1076e03dceedSVikram Hegde 	    size,			/* size */
1077e03dceedSVikram Hegde 	    IMMU_PAGESIZE,		/* quantum */
1078e03dceedSVikram Hegde 	    NULL,			/* afunc */
1079e03dceedSVikram Hegde 	    NULL,			/* ffunc */
1080e03dceedSVikram Hegde 	    NULL,			/* source */
1081e03dceedSVikram Hegde 	    0,				/* qcache_max */
1082e03dceedSVikram Hegde 	    vmem_flags);
10833a634bfcSVikram Hegde 
1084e03dceedSVikram Hegde 	if (domain->dom_dvma_arena == NULL) {
1085e03dceedSVikram Hegde 		ddi_err(DER_PANIC, rdip,
1086e03dceedSVikram Hegde 		    "Failed to allocate DVMA arena(%s) "
1087e03dceedSVikram Hegde 		    "for domain ID (%d)", arena_name, domain->dom_did);
1088e03dceedSVikram Hegde 		/*NOTREACHED*/
1089e03dceedSVikram Hegde 	}
10903a634bfcSVikram Hegde 
1091e03dceedSVikram Hegde 	mp = mp->ml_next;
1092e03dceedSVikram Hegde 	while (mp) {
10933a634bfcSVikram Hegde 
10943a634bfcSVikram Hegde 		if (mp->ml_address == 0)
10953a634bfcSVikram Hegde 			start = MMU_PAGESIZE;
10963a634bfcSVikram Hegde 		else
10973a634bfcSVikram Hegde 			start = mp->ml_address;
10983a634bfcSVikram Hegde 
10993a634bfcSVikram Hegde 		if (start + mp->ml_size > maxaddr)
11003a634bfcSVikram Hegde 			size = maxaddr - start;
11013a634bfcSVikram Hegde 		else
11023a634bfcSVikram Hegde 			size = mp->ml_size;
11033a634bfcSVikram Hegde 
11043a634bfcSVikram Hegde 		ddi_err(DER_VERB, rdip,
110550200e77SFrank Van Der Linden 		    "iommu: %s: Adding dvma vmem span [0x%" PRIx64
1106e03dceedSVikram Hegde 		    " - 0x%" PRIx64 "]", arena_name, start,
1107e03dceedSVikram Hegde 		    start + size);
11083a634bfcSVikram Hegde 
1109e03dceedSVikram Hegde 		vmem_ret = vmem_add(domain->dom_dvma_arena,
1110e03dceedSVikram Hegde 		    (void *)(uintptr_t)start, size,  vmem_flags);
11113a634bfcSVikram Hegde 
1112e03dceedSVikram Hegde 		if (vmem_ret == NULL) {
11133a634bfcSVikram Hegde 			ddi_err(DER_PANIC, rdip,
11143a634bfcSVikram Hegde 			    "Failed to allocate DVMA arena(%s) "
1115e03dceedSVikram Hegde 			    "for domain ID (%d)",
1116e03dceedSVikram Hegde 			    arena_name, domain->dom_did);
11173a634bfcSVikram Hegde 			/*NOTREACHED*/
11183a634bfcSVikram Hegde 		}
11193a634bfcSVikram Hegde 		mp = mp->ml_next;
11203a634bfcSVikram Hegde 	}
1121e03dceedSVikram Hegde 	memlist_read_unlock();
11223a634bfcSVikram Hegde }
11233a634bfcSVikram Hegde 
11243a634bfcSVikram Hegde /* ################################### DOMAIN CODE ######################### */
11253a634bfcSVikram Hegde 
11263a634bfcSVikram Hegde /*
11273a634bfcSVikram Hegde  * Set the domain and domain-dip for a dip
11283a634bfcSVikram Hegde  */
11293a634bfcSVikram Hegde static void
set_domain(dev_info_t * dip,dev_info_t * ddip,domain_t * domain)11303a634bfcSVikram Hegde set_domain(
11313a634bfcSVikram Hegde 	dev_info_t *dip,
11323a634bfcSVikram Hegde 	dev_info_t *ddip,
11333a634bfcSVikram Hegde 	domain_t *domain)
11343a634bfcSVikram Hegde {
11353a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
11363a634bfcSVikram Hegde 	domain_t *fdomain;
11373a634bfcSVikram Hegde 	dev_info_t *fddip;
11383a634bfcSVikram Hegde 
11393a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
11403a634bfcSVikram Hegde 
11413a634bfcSVikram Hegde 	mutex_enter(&(DEVI(dip)->devi_lock));
11423a634bfcSVikram Hegde 	fddip = immu_devi->imd_ddip;
11433a634bfcSVikram Hegde 	fdomain = immu_devi->imd_domain;
11443a634bfcSVikram Hegde 
11453a634bfcSVikram Hegde 	if (fddip) {
11463a634bfcSVikram Hegde 		ASSERT(fddip == ddip);
11473a634bfcSVikram Hegde 	} else {
11483a634bfcSVikram Hegde 		immu_devi->imd_ddip = ddip;
11493a634bfcSVikram Hegde 	}
11503a634bfcSVikram Hegde 
11513a634bfcSVikram Hegde 	if (fdomain) {
11523a634bfcSVikram Hegde 		ASSERT(fdomain == domain);
11533a634bfcSVikram Hegde 	} else {
11543a634bfcSVikram Hegde 		immu_devi->imd_domain = domain;
11553a634bfcSVikram Hegde 	}
11563a634bfcSVikram Hegde 	mutex_exit(&(DEVI(dip)->devi_lock));
11573a634bfcSVikram Hegde }
11583a634bfcSVikram Hegde 
11593a634bfcSVikram Hegde /*
11603a634bfcSVikram Hegde  * device_domain()
1161*584b574aSToomas Soome  *	Get domain for a device. The domain may be global in which case it
11623a634bfcSVikram Hegde  *	is shared between all IOMMU units. Due to potential AGAW differences
11633a634bfcSVikram Hegde  *      between IOMMU units, such global domains *have to be* UNITY mapping
11643a634bfcSVikram Hegde  *      domains. Alternatively, the domain may be local to a IOMMU unit.
11653a634bfcSVikram Hegde  *	Local domains may be shared or immu_devi, although the
11663a634bfcSVikram Hegde  *      scope of sharing
11673a634bfcSVikram Hegde  *	is restricted to devices controlled by the IOMMU unit to
11683a634bfcSVikram Hegde  *      which the domain
11693a634bfcSVikram Hegde  *	belongs. If shared, they (currently) have to be UNITY domains. If
11703a634bfcSVikram Hegde  *      immu_devi a domain may be either UNITY or translation (XLATE) domain.
11713a634bfcSVikram Hegde  */
11723a634bfcSVikram Hegde static domain_t *
device_domain(dev_info_t * rdip,dev_info_t ** ddipp,immu_flags_t immu_flags)11733a634bfcSVikram Hegde device_domain(dev_info_t *rdip, dev_info_t **ddipp, immu_flags_t immu_flags)
11743a634bfcSVikram Hegde {
11753a634bfcSVikram Hegde 	dev_info_t *ddip; /* topmost dip in domain i.e. domain owner */
11763a634bfcSVikram Hegde 	immu_t *immu;
11773a634bfcSVikram Hegde 	domain_t *domain;
11783a634bfcSVikram Hegde 	dvma_arg_t dvarg = {0};
11793a634bfcSVikram Hegde 	int level;
11803a634bfcSVikram Hegde 
11813a634bfcSVikram Hegde 	*ddipp = NULL;
11823a634bfcSVikram Hegde 
11833a634bfcSVikram Hegde 	/*
11843a634bfcSVikram Hegde 	 * Check if the domain is already set. This is usually true
11853a634bfcSVikram Hegde 	 * if this is not the first DVMA transaction.
11863a634bfcSVikram Hegde 	 */
11873a634bfcSVikram Hegde 	ddip = NULL;
11883a634bfcSVikram Hegde 	domain = immu_devi_domain(rdip, &ddip);
11893a634bfcSVikram Hegde 	if (domain) {
11903a634bfcSVikram Hegde 		*ddipp = ddip;
11913a634bfcSVikram Hegde 		return (domain);
11923a634bfcSVikram Hegde 	}
11933a634bfcSVikram Hegde 
11943a634bfcSVikram Hegde 	immu = immu_dvma_get_immu(rdip, immu_flags);
11953a634bfcSVikram Hegde 	if (immu == NULL) {
11963a634bfcSVikram Hegde 		/*
11973a634bfcSVikram Hegde 		 * possible that there is no IOMMU unit for this device
11983a634bfcSVikram Hegde 		 * - BIOS bugs are one example.
11993a634bfcSVikram Hegde 		 */
120050200e77SFrank Van Der Linden 		ddi_err(DER_WARN, rdip, "No iommu unit found for device");
12013a634bfcSVikram Hegde 		return (NULL);
12023a634bfcSVikram Hegde 	}
12033a634bfcSVikram Hegde 
12049e986f0eSFrank Van Der Linden 	immu_flags |= immu_devi_get(rdip)->imd_dvma_flags;
12059e986f0eSFrank Van Der Linden 
1206e03dceedSVikram Hegde 	dvarg.dva_rdip = rdip;
12073a634bfcSVikram Hegde 	dvarg.dva_ddip = NULL;
12083a634bfcSVikram Hegde 	dvarg.dva_domain = NULL;
12093a634bfcSVikram Hegde 	dvarg.dva_flags = immu_flags;
12103a634bfcSVikram Hegde 	level = 0;
1211e03dceedSVikram Hegde 	if (immu_walk_ancestor(rdip, NULL, get_branch_domain,
12123a634bfcSVikram Hegde 	    &dvarg, &level, immu_flags) != DDI_SUCCESS) {
12133a634bfcSVikram Hegde 		/*
12143a634bfcSVikram Hegde 		 * maybe low memory. return error,
12153a634bfcSVikram Hegde 		 * so driver tries again later
12163a634bfcSVikram Hegde 		 */
12173a634bfcSVikram Hegde 		return (NULL);
12183a634bfcSVikram Hegde 	}
12193a634bfcSVikram Hegde 
12203a634bfcSVikram Hegde 	/* should have walked at least 1 dip (i.e. edip) */
12213a634bfcSVikram Hegde 	ASSERT(level > 0);
12223a634bfcSVikram Hegde 
12233a634bfcSVikram Hegde 	ddip = dvarg.dva_ddip;	/* must be present */
12243a634bfcSVikram Hegde 	domain = dvarg.dva_domain;	/* may be NULL */
12253a634bfcSVikram Hegde 
12263a634bfcSVikram Hegde 	/*
12273a634bfcSVikram Hegde 	 * We may find the domain during our ancestor walk on any one of our
12283a634bfcSVikram Hegde 	 * ancestor dips, If the domain is found then the domain-dip
12293a634bfcSVikram Hegde 	 * (i.e. ddip) will also be found in the same immu_devi struct.
12303a634bfcSVikram Hegde 	 * The domain-dip is the highest ancestor dip which shares the
12313a634bfcSVikram Hegde 	 * same domain with edip.
12323a634bfcSVikram Hegde 	 * The domain may or may not be found, but the domain dip must
12333a634bfcSVikram Hegde 	 * be found.
12343a634bfcSVikram Hegde 	 */
12353a634bfcSVikram Hegde 	if (ddip == NULL) {
1236e03dceedSVikram Hegde 		ddi_err(DER_MODE, rdip, "Cannot find domain dip for device.");
12373a634bfcSVikram Hegde 		return (NULL);
12383a634bfcSVikram Hegde 	}
12393a634bfcSVikram Hegde 
12403a634bfcSVikram Hegde 	/*
12413a634bfcSVikram Hegde 	 * Did we find a domain ?
12423a634bfcSVikram Hegde 	 */
12433a634bfcSVikram Hegde 	if (domain) {
12443a634bfcSVikram Hegde 		goto found;
12453a634bfcSVikram Hegde 	}
12463a634bfcSVikram Hegde 
12473a634bfcSVikram Hegde 	/* nope, so allocate */
12483a634bfcSVikram Hegde 	domain = domain_create(immu, ddip, rdip, immu_flags);
12493a634bfcSVikram Hegde 	if (domain == NULL) {
12503a634bfcSVikram Hegde 		return (NULL);
12513a634bfcSVikram Hegde 	}
12523a634bfcSVikram Hegde 
12533a634bfcSVikram Hegde 	/*FALLTHROUGH*/
12543a634bfcSVikram Hegde found:
12553a634bfcSVikram Hegde 	/*
12563a634bfcSVikram Hegde 	 * We know *domain *is* the right domain, so panic if
12573a634bfcSVikram Hegde 	 * another domain is set for either the request-dip or
12583a634bfcSVikram Hegde 	 * effective dip.
12593a634bfcSVikram Hegde 	 */
12603a634bfcSVikram Hegde 	set_domain(ddip, ddip, domain);
12613a634bfcSVikram Hegde 	set_domain(rdip, ddip, domain);
12623a634bfcSVikram Hegde 
12633a634bfcSVikram Hegde 	*ddipp = ddip;
12643a634bfcSVikram Hegde 	return (domain);
12653a634bfcSVikram Hegde }
12663a634bfcSVikram Hegde 
12673a634bfcSVikram Hegde static void
create_unity_domain(immu_t * immu)12683a634bfcSVikram Hegde create_unity_domain(immu_t *immu)
12693a634bfcSVikram Hegde {
12703a634bfcSVikram Hegde 	domain_t *domain;
12713a634bfcSVikram Hegde 
12723a634bfcSVikram Hegde 	/* domain created during boot and always use sleep flag */
12733a634bfcSVikram Hegde 	domain = kmem_zalloc(sizeof (domain_t), KM_SLEEP);
12743a634bfcSVikram Hegde 
12753a634bfcSVikram Hegde 	rw_init(&(domain->dom_pgtable_rwlock), NULL, RW_DEFAULT, NULL);
12763a634bfcSVikram Hegde 
12773a634bfcSVikram Hegde 	domain->dom_did = IMMU_UNITY_DID;
12783a634bfcSVikram Hegde 	domain->dom_maptype = IMMU_MAPTYPE_UNITY;
12793a634bfcSVikram Hegde 
12803a634bfcSVikram Hegde 	domain->dom_immu = immu;
12813a634bfcSVikram Hegde 	immu->immu_unity_domain = domain;
12823a634bfcSVikram Hegde 
12833a634bfcSVikram Hegde 	/*
12843a634bfcSVikram Hegde 	 * Setup the domain's initial page table
12853a634bfcSVikram Hegde 	 * should never fail.
12863a634bfcSVikram Hegde 	 */
1287e03dceedSVikram Hegde 	domain->dom_pgtable_root = pgtable_alloc(immu, IMMU_FLAGS_SLEEP);
128850200e77SFrank Van Der Linden 	pgtable_zero(domain->dom_pgtable_root);
12893a634bfcSVikram Hegde 
1290be56ae36SFrank Van Der Linden 	/*
1291be56ae36SFrank Van Der Linden 	 * Only map all physical memory in to the unity domain
1292be56ae36SFrank Van Der Linden 	 * if passthrough is not supported. If it is supported,
1293be56ae36SFrank Van Der Linden 	 * passthrough is set in the context entry instead.
1294be56ae36SFrank Van Der Linden 	 */
1295be56ae36SFrank Van Der Linden 	if (!IMMU_ECAP_GET_PT(immu->immu_regs_excap))
1296be56ae36SFrank Van Der Linden 		map_unity_domain(domain);
1297be56ae36SFrank Van Der Linden 
12983a634bfcSVikram Hegde 
12993a634bfcSVikram Hegde 	/*
13003a634bfcSVikram Hegde 	 * put it on the system-wide UNITY domain list
13013a634bfcSVikram Hegde 	 */
13023a634bfcSVikram Hegde 	mutex_enter(&(immu_domain_lock));
13033a634bfcSVikram Hegde 	list_insert_tail(&immu_unity_domain_list, domain);
13043a634bfcSVikram Hegde 	mutex_exit(&(immu_domain_lock));
13053a634bfcSVikram Hegde }
13063a634bfcSVikram Hegde 
13073a634bfcSVikram Hegde /*
13083a634bfcSVikram Hegde  * ddip is the domain-dip - the topmost dip in a domain
13093a634bfcSVikram Hegde  * rdip is the requesting-dip - the device which is
13103a634bfcSVikram Hegde  * requesting DVMA setup
13113a634bfcSVikram Hegde  * if domain is a non-shared domain rdip == ddip
13123a634bfcSVikram Hegde  */
13133a634bfcSVikram Hegde static domain_t *
domain_create(immu_t * immu,dev_info_t * ddip,dev_info_t * rdip,immu_flags_t immu_flags)13143a634bfcSVikram Hegde domain_create(immu_t *immu, dev_info_t *ddip, dev_info_t *rdip,
13153a634bfcSVikram Hegde     immu_flags_t immu_flags)
13163a634bfcSVikram Hegde {
13173a634bfcSVikram Hegde 	int kmflags;
13183a634bfcSVikram Hegde 	domain_t *domain;
13193a634bfcSVikram Hegde 	char mod_hash_name[128];
13203a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
13213a634bfcSVikram Hegde 	int did;
132250200e77SFrank Van Der Linden 	immu_dcookie_t dcookies[1] = {0};
1323e03dceedSVikram Hegde 	int dcount = 0;
13243a634bfcSVikram Hegde 
13253a634bfcSVikram Hegde 	immu_devi = immu_devi_get(rdip);
13263a634bfcSVikram Hegde 
13273a634bfcSVikram Hegde 	/*
13283a634bfcSVikram Hegde 	 * First allocate a domainid.
13293a634bfcSVikram Hegde 	 * This routine will never fail, since if we run out
13303a634bfcSVikram Hegde 	 * of domains the unity domain will be allocated.
13313a634bfcSVikram Hegde 	 */
13323a634bfcSVikram Hegde 	did = did_alloc(immu, rdip, ddip, immu_flags);
13333a634bfcSVikram Hegde 	if (did == IMMU_UNITY_DID) {
13343a634bfcSVikram Hegde 		/* domain overflow */
13353a634bfcSVikram Hegde 		ASSERT(immu->immu_unity_domain);
13363a634bfcSVikram Hegde 		return (immu->immu_unity_domain);
13373a634bfcSVikram Hegde 	}
13383a634bfcSVikram Hegde 
13393a634bfcSVikram Hegde 	kmflags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;
13403a634bfcSVikram Hegde 	domain = kmem_zalloc(sizeof (domain_t), kmflags);
13413a634bfcSVikram Hegde 	if (domain == NULL) {
13423a634bfcSVikram Hegde 		ddi_err(DER_PANIC, rdip, "Failed to alloc DVMA domain "
13433a634bfcSVikram Hegde 		    "structure for device. IOMMU unit: %s", immu->immu_name);
13443a634bfcSVikram Hegde 		/*NOTREACHED*/
13453a634bfcSVikram Hegde 	}
13463a634bfcSVikram Hegde 
13473a634bfcSVikram Hegde 	rw_init(&(domain->dom_pgtable_rwlock), NULL, RW_DEFAULT, NULL);
13483a634bfcSVikram Hegde 
13493a634bfcSVikram Hegde 	(void) snprintf(mod_hash_name, sizeof (mod_hash_name),
13503a634bfcSVikram Hegde 	    "immu%s-domain%d-pava-hash", immu->immu_name, did);
13513a634bfcSVikram Hegde 
13523a634bfcSVikram Hegde 	domain->dom_did = did;
13533a634bfcSVikram Hegde 	domain->dom_immu = immu;
13543a634bfcSVikram Hegde 	domain->dom_maptype = IMMU_MAPTYPE_XLATE;
135550200e77SFrank Van Der Linden 	domain->dom_dip = ddip;
13563a634bfcSVikram Hegde 
13573a634bfcSVikram Hegde 	/*
13583a634bfcSVikram Hegde 	 * Create xlate DVMA arena for this domain.
13593a634bfcSVikram Hegde 	 */
13603a634bfcSVikram Hegde 	create_xlate_arena(immu, domain, rdip, immu_flags);
13613a634bfcSVikram Hegde 
13623a634bfcSVikram Hegde 	/*
13633a634bfcSVikram Hegde 	 * Setup the domain's initial page table
13643a634bfcSVikram Hegde 	 */
1365e03dceedSVikram Hegde 	domain->dom_pgtable_root = pgtable_alloc(immu, immu_flags);
13663a634bfcSVikram Hegde 	if (domain->dom_pgtable_root == NULL) {
13673a634bfcSVikram Hegde 		ddi_err(DER_PANIC, rdip, "Failed to alloc root "
13683a634bfcSVikram Hegde 		    "pgtable for domain (%d). IOMMU unit: %s",
13693a634bfcSVikram Hegde 		    domain->dom_did, immu->immu_name);
13703a634bfcSVikram Hegde 		/*NOTREACHED*/
13713a634bfcSVikram Hegde 	}
137250200e77SFrank Van Der Linden 	pgtable_zero(domain->dom_pgtable_root);
13733a634bfcSVikram Hegde 
13743a634bfcSVikram Hegde 	/*
13753a634bfcSVikram Hegde 	 * Since this is a immu unit-specific domain, put it on
13763a634bfcSVikram Hegde 	 * the per-immu domain list.
13773a634bfcSVikram Hegde 	 */
13783a634bfcSVikram Hegde 	mutex_enter(&(immu->immu_lock));
13793a634bfcSVikram Hegde 	list_insert_head(&immu->immu_domain_list, domain);
13803a634bfcSVikram Hegde 	mutex_exit(&(immu->immu_lock));
13813a634bfcSVikram Hegde 
13823a634bfcSVikram Hegde 	/*
13833a634bfcSVikram Hegde 	 * Also put it on the system-wide xlate domain list
13843a634bfcSVikram Hegde 	 */
13853a634bfcSVikram Hegde 	mutex_enter(&(immu_domain_lock));
13863a634bfcSVikram Hegde 	list_insert_head(&immu_xlate_domain_list, domain);
13873a634bfcSVikram Hegde 	mutex_exit(&(immu_domain_lock));
13883a634bfcSVikram Hegde 
13893a634bfcSVikram Hegde 	bdf_domain_insert(immu_devi, domain);
13903a634bfcSVikram Hegde 
13913a634bfcSVikram Hegde #ifdef BUGGY_DRIVERS
13923a634bfcSVikram Hegde 	/*
13933a634bfcSVikram Hegde 	 * Map page0. Some broken HW/FW access it.
13943a634bfcSVikram Hegde 	 */
1395e03dceedSVikram Hegde 	dcookies[0].dck_paddr = 0;
1396e03dceedSVikram Hegde 	dcookies[0].dck_npages = 1;
1397e03dceedSVikram Hegde 	dcount = 1;
139850200e77SFrank Van Der Linden 	(void) dvma_map(domain, 0, 1, dcookies, dcount, NULL,
13993a634bfcSVikram Hegde 	    IMMU_FLAGS_READ | IMMU_FLAGS_WRITE | IMMU_FLAGS_PAGE1);
14003a634bfcSVikram Hegde #endif
14013a634bfcSVikram Hegde 	return (domain);
14023a634bfcSVikram Hegde }
14033a634bfcSVikram Hegde 
14043a634bfcSVikram Hegde /*
14053a634bfcSVikram Hegde  * Create domainid arena.
14063a634bfcSVikram Hegde  * Domainid 0 is reserved by Vt-d spec and cannot be used by
14073a634bfcSVikram Hegde  * system software.
14083a634bfcSVikram Hegde  * Domainid 1 is reserved by solaris and used for *all* of the following:
14093a634bfcSVikram Hegde  *	as the "uninitialized" domain - For devices not yet controlled
14103a634bfcSVikram Hegde  *	by Solaris
14113a634bfcSVikram Hegde  *	as the "unity" domain - For devices that will always belong
14123a634bfcSVikram Hegde  *	to the unity domain
14133a634bfcSVikram Hegde  *	as the "overflow" domain - Used for any new device after we
14143a634bfcSVikram Hegde  *	run out of domains
14153a634bfcSVikram Hegde  * All of the above domains map into a single domain with
14163a634bfcSVikram Hegde  * domainid 1 and UNITY DVMA mapping
14173a634bfcSVikram Hegde  * Each IMMU unity has its own unity/uninit/overflow domain
14183a634bfcSVikram Hegde  */
14193a634bfcSVikram Hegde static void
did_init(immu_t * immu)14203a634bfcSVikram Hegde did_init(immu_t *immu)
14213a634bfcSVikram Hegde {
14223a634bfcSVikram Hegde 	(void) snprintf(immu->immu_did_arena_name,
14233a634bfcSVikram Hegde 	    sizeof (immu->immu_did_arena_name),
14243a634bfcSVikram Hegde 	    "%s_domainid_arena", immu->immu_name);
14253a634bfcSVikram Hegde 
142650200e77SFrank Van Der Linden 	ddi_err(DER_VERB, immu->immu_dip, "creating domainid arena %s",
142750200e77SFrank Van Der Linden 	    immu->immu_did_arena_name);
14283a634bfcSVikram Hegde 
14293a634bfcSVikram Hegde 	immu->immu_did_arena = vmem_create(
14303a634bfcSVikram Hegde 	    immu->immu_did_arena_name,
14313a634bfcSVikram Hegde 	    (void *)(uintptr_t)(IMMU_UNITY_DID + 1),   /* start addr */
14323a634bfcSVikram Hegde 	    immu->immu_max_domains - IMMU_UNITY_DID,
14333a634bfcSVikram Hegde 	    1,				/* quantum */
14343a634bfcSVikram Hegde 	    NULL,			/* afunc */
14353a634bfcSVikram Hegde 	    NULL,			/* ffunc */
14363a634bfcSVikram Hegde 	    NULL,			/* source */
14373a634bfcSVikram Hegde 	    0,				/* qcache_max */
14383a634bfcSVikram Hegde 	    VM_SLEEP);
14393a634bfcSVikram Hegde 
14403a634bfcSVikram Hegde 	/* Even with SLEEP flag, vmem_create() can fail */
14413a634bfcSVikram Hegde 	if (immu->immu_did_arena == NULL) {
14423a634bfcSVikram Hegde 		ddi_err(DER_PANIC, NULL, "%s: Failed to create Intel "
14433a634bfcSVikram Hegde 		    "IOMMU domainid allocator: %s", immu->immu_name,
14443a634bfcSVikram Hegde 		    immu->immu_did_arena_name);
14453a634bfcSVikram Hegde 	}
14463a634bfcSVikram Hegde }
14473a634bfcSVikram Hegde 
14483a634bfcSVikram Hegde /* #########################  CONTEXT CODE ################################# */
14493a634bfcSVikram Hegde 
14503a634bfcSVikram Hegde static void
context_set(immu_t * immu,domain_t * domain,pgtable_t * root_table,int bus,int devfunc)14513a634bfcSVikram Hegde context_set(immu_t *immu, domain_t *domain, pgtable_t *root_table,
14523a634bfcSVikram Hegde     int bus, int devfunc)
14533a634bfcSVikram Hegde {
14543a634bfcSVikram Hegde 	pgtable_t *context;
14553a634bfcSVikram Hegde 	pgtable_t *pgtable_root;
14563a634bfcSVikram Hegde 	hw_rce_t *hw_rent;
14573a634bfcSVikram Hegde 	hw_rce_t *hw_cent;
14583a634bfcSVikram Hegde 	hw_rce_t *ctxp;
1459e03dceedSVikram Hegde 	int sid;
1460e03dceedSVikram Hegde 	krw_t rwtype;
1461e03dceedSVikram Hegde 	boolean_t fill_root;
1462e03dceedSVikram Hegde 	boolean_t fill_ctx;
14633a634bfcSVikram Hegde 
1464e03dceedSVikram Hegde 	pgtable_root = domain->dom_pgtable_root;
1465e03dceedSVikram Hegde 
14663a634bfcSVikram Hegde 	ctxp = (hw_rce_t *)(root_table->swpg_next_array);
14673a634bfcSVikram Hegde 	context = *(pgtable_t **)(ctxp + bus);
14683a634bfcSVikram Hegde 	hw_rent = (hw_rce_t *)(root_table->hwpg_vaddr) + bus;
1469e03dceedSVikram Hegde 
1470e03dceedSVikram Hegde 	fill_root = B_FALSE;
1471e03dceedSVikram Hegde 	fill_ctx = B_FALSE;
1472e03dceedSVikram Hegde 
1473e03dceedSVikram Hegde 	/* Check the most common case first with reader lock */
1474e03dceedSVikram Hegde 	rw_enter(&(immu->immu_ctx_rwlock), RW_READER);
1475e03dceedSVikram Hegde 	rwtype = RW_READER;
1476e03dceedSVikram Hegde again:
14773a634bfcSVikram Hegde 	if (ROOT_GET_P(hw_rent)) {
1478e03dceedSVikram Hegde 		hw_cent = (hw_rce_t *)(context->hwpg_vaddr) + devfunc;
1479e03dceedSVikram Hegde 		if (CONT_GET_AVAIL(hw_cent) == IMMU_CONT_INITED) {
1480e03dceedSVikram Hegde 			rw_exit(&(immu->immu_ctx_rwlock));
1481e03dceedSVikram Hegde 			return;
1482e03dceedSVikram Hegde 		} else {
1483e03dceedSVikram Hegde 			fill_ctx = B_TRUE;
1484e03dceedSVikram Hegde 		}
14853a634bfcSVikram Hegde 	} else {
1486e03dceedSVikram Hegde 		fill_root = B_TRUE;
1487e03dceedSVikram Hegde 		fill_ctx = B_TRUE;
1488e03dceedSVikram Hegde 	}
1489e03dceedSVikram Hegde 
1490e03dceedSVikram Hegde 	if (rwtype == RW_READER &&
1491e03dceedSVikram Hegde 	    rw_tryupgrade(&(immu->immu_ctx_rwlock)) == 0) {
1492e03dceedSVikram Hegde 		rw_exit(&(immu->immu_ctx_rwlock));
1493e03dceedSVikram Hegde 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
1494e03dceedSVikram Hegde 		rwtype = RW_WRITER;
1495e03dceedSVikram Hegde 		goto again;
1496e03dceedSVikram Hegde 	}
1497e03dceedSVikram Hegde 	rwtype = RW_WRITER;
1498e03dceedSVikram Hegde 
1499e03dceedSVikram Hegde 	if (fill_root == B_TRUE) {
15003a634bfcSVikram Hegde 		ROOT_SET_CONT(hw_rent, context->hwpg_paddr);
15013a634bfcSVikram Hegde 		ROOT_SET_P(hw_rent);
15023a634bfcSVikram Hegde 		immu_regs_cpu_flush(immu, (caddr_t)hw_rent, sizeof (hw_rce_t));
15033a634bfcSVikram Hegde 	}
15043a634bfcSVikram Hegde 
1505e03dceedSVikram Hegde 	if (fill_ctx == B_TRUE) {
1506e03dceedSVikram Hegde 		hw_cent = (hw_rce_t *)(context->hwpg_vaddr) + devfunc;
15073a634bfcSVikram Hegde 		/* need to disable context entry before reprogramming it */
15083a634bfcSVikram Hegde 		bzero(hw_cent, sizeof (hw_rce_t));
15093a634bfcSVikram Hegde 
15103a634bfcSVikram Hegde 		/* flush caches */
15113a634bfcSVikram Hegde 		immu_regs_cpu_flush(immu, (caddr_t)hw_cent, sizeof (hw_rce_t));
1512e03dceedSVikram Hegde 
1513e03dceedSVikram Hegde 		sid = ((bus << 8) | devfunc);
151450200e77SFrank Van Der Linden 		immu_flush_context_fsi(immu, 0, sid, domain->dom_did,
151550200e77SFrank Van Der Linden 		    &immu->immu_ctx_inv_wait);
15163a634bfcSVikram Hegde 
15173a634bfcSVikram Hegde 		CONT_SET_AVAIL(hw_cent, IMMU_CONT_INITED);
15183a634bfcSVikram Hegde 		CONT_SET_DID(hw_cent, domain->dom_did);
15193a634bfcSVikram Hegde 		CONT_SET_AW(hw_cent, immu->immu_dvma_agaw);
15203a634bfcSVikram Hegde 		CONT_SET_ASR(hw_cent, pgtable_root->hwpg_paddr);
1521be56ae36SFrank Van Der Linden 		if (domain->dom_did == IMMU_UNITY_DID &&
1522be56ae36SFrank Van Der Linden 		    IMMU_ECAP_GET_PT(immu->immu_regs_excap))
1523be56ae36SFrank Van Der Linden 			CONT_SET_TTYPE(hw_cent, TTYPE_PASSTHRU);
1524be56ae36SFrank Van Der Linden 		else
1525be56ae36SFrank Van Der Linden 			/*LINTED*/
1526be56ae36SFrank Van Der Linden 			CONT_SET_TTYPE(hw_cent, TTYPE_XLATE_ONLY);
15273a634bfcSVikram Hegde 		CONT_SET_P(hw_cent);
152850200e77SFrank Van Der Linden 		if (IMMU_ECAP_GET_CH(immu->immu_regs_excap)) {
152950200e77SFrank Van Der Linden 			CONT_SET_EH(hw_cent);
153050200e77SFrank Van Der Linden 			if (immu_use_alh)
153150200e77SFrank Van Der Linden 				CONT_SET_ALH(hw_cent);
153250200e77SFrank Van Der Linden 		}
15333a634bfcSVikram Hegde 		immu_regs_cpu_flush(immu, (caddr_t)hw_cent, sizeof (hw_rce_t));
15343a634bfcSVikram Hegde 	}
1535e03dceedSVikram Hegde 	rw_exit(&(immu->immu_ctx_rwlock));
15363a634bfcSVikram Hegde }
15373a634bfcSVikram Hegde 
15383a634bfcSVikram Hegde static pgtable_t *
context_create(immu_t * immu)15393a634bfcSVikram Hegde context_create(immu_t *immu)
15403a634bfcSVikram Hegde {
15413a634bfcSVikram Hegde 	int	bus;
15423a634bfcSVikram Hegde 	int	devfunc;
15433a634bfcSVikram Hegde 	pgtable_t *root_table;
15443a634bfcSVikram Hegde 	pgtable_t *context;
15453a634bfcSVikram Hegde 	pgtable_t *pgtable_root;
15463a634bfcSVikram Hegde 	hw_rce_t *ctxp;
15473a634bfcSVikram Hegde 	hw_rce_t *hw_rent;
15483a634bfcSVikram Hegde 	hw_rce_t *hw_cent;
15493a634bfcSVikram Hegde 
15503a634bfcSVikram Hegde 	/* Allocate a zeroed root table (4K 256b entries) */
1551e03dceedSVikram Hegde 	root_table = pgtable_alloc(immu, IMMU_FLAGS_SLEEP);
155250200e77SFrank Van Der Linden 	pgtable_zero(root_table);
15533a634bfcSVikram Hegde 
15543a634bfcSVikram Hegde 	/*
15553a634bfcSVikram Hegde 	 * Setup context tables for all possible root table entries.
15563a634bfcSVikram Hegde 	 * Start out with unity domains for all entries.
15573a634bfcSVikram Hegde 	 */
15583a634bfcSVikram Hegde 	ctxp = (hw_rce_t *)(root_table->swpg_next_array);
15593a634bfcSVikram Hegde 	hw_rent = (hw_rce_t *)(root_table->hwpg_vaddr);
15603a634bfcSVikram Hegde 	for (bus = 0; bus < IMMU_ROOT_NUM; bus++, ctxp++, hw_rent++) {
1561e03dceedSVikram Hegde 		context = pgtable_alloc(immu, IMMU_FLAGS_SLEEP);
156250200e77SFrank Van Der Linden 		pgtable_zero(context);
15633a634bfcSVikram Hegde 		ROOT_SET_P(hw_rent);
15643a634bfcSVikram Hegde 		ROOT_SET_CONT(hw_rent, context->hwpg_paddr);
15653a634bfcSVikram Hegde 		hw_cent = (hw_rce_t *)(context->hwpg_vaddr);
15663a634bfcSVikram Hegde 		for (devfunc = 0; devfunc < IMMU_CONT_NUM;
15673a634bfcSVikram Hegde 		    devfunc++, hw_cent++) {
15683a634bfcSVikram Hegde 			pgtable_root =
15693a634bfcSVikram Hegde 			    immu->immu_unity_domain->dom_pgtable_root;
15703a634bfcSVikram Hegde 			CONT_SET_DID(hw_cent,
15713a634bfcSVikram Hegde 			    immu->immu_unity_domain->dom_did);
15723a634bfcSVikram Hegde 			CONT_SET_AW(hw_cent, immu->immu_dvma_agaw);
15733a634bfcSVikram Hegde 			CONT_SET_ASR(hw_cent, pgtable_root->hwpg_paddr);
1574be56ae36SFrank Van Der Linden 			if (IMMU_ECAP_GET_PT(immu->immu_regs_excap))
1575be56ae36SFrank Van Der Linden 				CONT_SET_TTYPE(hw_cent, TTYPE_PASSTHRU);
1576be56ae36SFrank Van Der Linden 			else
1577be56ae36SFrank Van Der Linden 				/*LINTED*/
1578be56ae36SFrank Van Der Linden 				CONT_SET_TTYPE(hw_cent, TTYPE_XLATE_ONLY);
15793a634bfcSVikram Hegde 			CONT_SET_AVAIL(hw_cent, IMMU_CONT_UNINITED);
15803a634bfcSVikram Hegde 			CONT_SET_P(hw_cent);
15813a634bfcSVikram Hegde 		}
15823a634bfcSVikram Hegde 		immu_regs_cpu_flush(immu, context->hwpg_vaddr, IMMU_PAGESIZE);
15833a634bfcSVikram Hegde 		*((pgtable_t **)ctxp) = context;
15843a634bfcSVikram Hegde 	}
15853a634bfcSVikram Hegde 
15863a634bfcSVikram Hegde 	return (root_table);
15873a634bfcSVikram Hegde }
15883a634bfcSVikram Hegde 
15893a634bfcSVikram Hegde /*
15903a634bfcSVikram Hegde  * Called during rootnex attach, so no locks needed
15913a634bfcSVikram Hegde  */
15923a634bfcSVikram Hegde static void
context_init(immu_t * immu)15933a634bfcSVikram Hegde context_init(immu_t *immu)
15943a634bfcSVikram Hegde {
15953a634bfcSVikram Hegde 	rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL);
15963a634bfcSVikram Hegde 
159750200e77SFrank Van Der Linden 	immu_init_inv_wait(&immu->immu_ctx_inv_wait, "ctxglobal", B_TRUE);
159850200e77SFrank Van Der Linden 
15993a634bfcSVikram Hegde 	immu_regs_wbf_flush(immu);
16003a634bfcSVikram Hegde 
16013a634bfcSVikram Hegde 	immu->immu_ctx_root = context_create(immu);
16023a634bfcSVikram Hegde 
16033a634bfcSVikram Hegde 	immu_regs_set_root_table(immu);
16043a634bfcSVikram Hegde 
16053a634bfcSVikram Hegde 	rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
160650200e77SFrank Van Der Linden 	immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait);
160750200e77SFrank Van Der Linden 	immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait);
16083a634bfcSVikram Hegde 	rw_exit(&(immu->immu_ctx_rwlock));
16093a634bfcSVikram Hegde }
16103a634bfcSVikram Hegde 
16113a634bfcSVikram Hegde 
16123a634bfcSVikram Hegde /*
16133a634bfcSVikram Hegde  * Find top pcib
16143a634bfcSVikram Hegde  */
16153a634bfcSVikram Hegde static int
find_top_pcib(dev_info_t * dip,void * arg)16163a634bfcSVikram Hegde find_top_pcib(dev_info_t *dip, void *arg)
16173a634bfcSVikram Hegde {
16183a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
16193a634bfcSVikram Hegde 	dev_info_t **pcibdipp = (dev_info_t **)arg;
16203a634bfcSVikram Hegde 
16213a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
16223a634bfcSVikram Hegde 
16233a634bfcSVikram Hegde 	if (immu_devi->imd_pcib_type == IMMU_PCIB_PCI_PCI) {
16243a634bfcSVikram Hegde 		*pcibdipp = dip;
16253a634bfcSVikram Hegde 	}
16263a634bfcSVikram Hegde 
16273a634bfcSVikram Hegde 	return (DDI_WALK_CONTINUE);
16283a634bfcSVikram Hegde }
16293a634bfcSVikram Hegde 
16303a634bfcSVikram Hegde static int
immu_context_update(immu_t * immu,domain_t * domain,dev_info_t * ddip,dev_info_t * rdip,immu_flags_t immu_flags)16313a634bfcSVikram Hegde immu_context_update(immu_t *immu, domain_t *domain, dev_info_t *ddip,
16323a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags)
16333a634bfcSVikram Hegde {
16343a634bfcSVikram Hegde 	immu_devi_t *r_immu_devi;
16353a634bfcSVikram Hegde 	immu_devi_t *d_immu_devi;
16363a634bfcSVikram Hegde 	int r_bus;
16373a634bfcSVikram Hegde 	int d_bus;
16383a634bfcSVikram Hegde 	int r_devfunc;
16393a634bfcSVikram Hegde 	int d_devfunc;
16403a634bfcSVikram Hegde 	immu_pcib_t d_pcib_type;
16413a634bfcSVikram Hegde 	dev_info_t *pcibdip;
16423a634bfcSVikram Hegde 
16433a634bfcSVikram Hegde 	if (ddip == NULL || rdip == NULL ||
16443a634bfcSVikram Hegde 	    ddip == root_devinfo || rdip == root_devinfo) {
16453a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "immu_contexts_update: domain-dip or "
16463a634bfcSVikram Hegde 		    "request-dip are NULL or are root devinfo");
16473a634bfcSVikram Hegde 		return (DDI_FAILURE);
16483a634bfcSVikram Hegde 	}
16493a634bfcSVikram Hegde 
16503a634bfcSVikram Hegde 	/*
16513a634bfcSVikram Hegde 	 * We need to set the context fields
16523a634bfcSVikram Hegde 	 * based on what type of device rdip and ddip are.
16533a634bfcSVikram Hegde 	 * To do that we need the immu_devi field.
16543a634bfcSVikram Hegde 	 * Set the immu_devi field (if not already set)
16553a634bfcSVikram Hegde 	 */
16563a634bfcSVikram Hegde 	if (immu_devi_set(ddip, immu_flags) == DDI_FAILURE) {
16573a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip,
16583a634bfcSVikram Hegde 		    "immu_context_update: failed to set immu_devi for ddip");
16593a634bfcSVikram Hegde 		return (DDI_FAILURE);
16603a634bfcSVikram Hegde 	}
16613a634bfcSVikram Hegde 
16623a634bfcSVikram Hegde 	if (immu_devi_set(rdip, immu_flags) == DDI_FAILURE) {
16633a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip,
16643a634bfcSVikram Hegde 		    "immu_context_update: failed to set immu_devi for rdip");
16653a634bfcSVikram Hegde 		return (DDI_FAILURE);
16663a634bfcSVikram Hegde 	}
16673a634bfcSVikram Hegde 
16683a634bfcSVikram Hegde 	d_immu_devi = immu_devi_get(ddip);
16693a634bfcSVikram Hegde 	r_immu_devi = immu_devi_get(rdip);
16703a634bfcSVikram Hegde 
16713a634bfcSVikram Hegde 	d_bus = d_immu_devi->imd_bus;
16723a634bfcSVikram Hegde 	d_devfunc = d_immu_devi->imd_devfunc;
16733a634bfcSVikram Hegde 	d_pcib_type = d_immu_devi->imd_pcib_type;
16743a634bfcSVikram Hegde 	r_bus = r_immu_devi->imd_bus;
16753a634bfcSVikram Hegde 	r_devfunc = r_immu_devi->imd_devfunc;
16763a634bfcSVikram Hegde 
16773a634bfcSVikram Hegde 	if (rdip == ddip) {
16783a634bfcSVikram Hegde 		/* rdip is a PCIE device. set context for it only */
16793a634bfcSVikram Hegde 		context_set(immu, domain, immu->immu_ctx_root, r_bus,
16803a634bfcSVikram Hegde 		    r_devfunc);
16813a634bfcSVikram Hegde #ifdef BUGGY_DRIVERS
16823a634bfcSVikram Hegde 	} else if (r_immu_devi == d_immu_devi) {
16833a634bfcSVikram Hegde #ifdef TEST
16843a634bfcSVikram Hegde 		ddi_err(DER_WARN, rdip, "Driver bug: Devices 0x%lx and "
16853a634bfcSVikram Hegde 		    "0x%lx are identical", rdip, ddip);
16863a634bfcSVikram Hegde #endif
16873a634bfcSVikram Hegde 		/* rdip is a PCIE device. set context for it only */
16883a634bfcSVikram Hegde 		context_set(immu, domain, immu->immu_ctx_root, r_bus,
16893a634bfcSVikram Hegde 		    r_devfunc);
16903a634bfcSVikram Hegde #endif
16913a634bfcSVikram Hegde 	} else if (d_pcib_type == IMMU_PCIB_PCIE_PCI) {
16923a634bfcSVikram Hegde 		/*
16933a634bfcSVikram Hegde 		 * ddip is a PCIE_PCI bridge. Set context for ddip's
16943a634bfcSVikram Hegde 		 * secondary bus. If rdip is on ddip's secondary
16953a634bfcSVikram Hegde 		 * bus, set context for rdip. Else, set context
16963a634bfcSVikram Hegde 		 * for rdip's PCI bridge on ddip's secondary bus.
16973a634bfcSVikram Hegde 		 */
16983a634bfcSVikram Hegde 		context_set(immu, domain, immu->immu_ctx_root,
16993a634bfcSVikram Hegde 		    d_immu_devi->imd_sec, 0);
17003a634bfcSVikram Hegde 		if (d_immu_devi->imd_sec == r_bus) {
17013a634bfcSVikram Hegde 			context_set(immu, domain, immu->immu_ctx_root,
17023a634bfcSVikram Hegde 			    r_bus, r_devfunc);
17033a634bfcSVikram Hegde 		} else {
17043a634bfcSVikram Hegde 			pcibdip = NULL;
17053a634bfcSVikram Hegde 			if (immu_walk_ancestor(rdip, ddip, find_top_pcib,
17063a634bfcSVikram Hegde 			    &pcibdip, NULL, immu_flags) == DDI_SUCCESS &&
17073a634bfcSVikram Hegde 			    pcibdip != NULL) {
17083a634bfcSVikram Hegde 				r_immu_devi = immu_devi_get(pcibdip);
17093a634bfcSVikram Hegde 				r_bus = r_immu_devi->imd_bus;
17103a634bfcSVikram Hegde 				r_devfunc = r_immu_devi->imd_devfunc;
17113a634bfcSVikram Hegde 				context_set(immu, domain, immu->immu_ctx_root,
17123a634bfcSVikram Hegde 				    r_bus, r_devfunc);
17133a634bfcSVikram Hegde 			} else {
17143a634bfcSVikram Hegde 				ddi_err(DER_PANIC, rdip, "Failed to find PCI "
17153a634bfcSVikram Hegde 				    " bridge for PCI device");
17163a634bfcSVikram Hegde 				/*NOTREACHED*/
17173a634bfcSVikram Hegde 			}
17183a634bfcSVikram Hegde 		}
17193a634bfcSVikram Hegde 	} else if (d_pcib_type == IMMU_PCIB_PCI_PCI) {
17203a634bfcSVikram Hegde 		context_set(immu, domain, immu->immu_ctx_root, d_bus,
17213a634bfcSVikram Hegde 		    d_devfunc);
17223a634bfcSVikram Hegde 	} else if (d_pcib_type == IMMU_PCIB_ENDPOINT) {
17233a634bfcSVikram Hegde 		/*
17243a634bfcSVikram Hegde 		 * ddip is a PCIE device which has a non-PCI device under it
17253a634bfcSVikram Hegde 		 * i.e. it is a PCI-nonPCI bridge. Example: pciicde-ata
17263a634bfcSVikram Hegde 		 */
17273a634bfcSVikram Hegde 		context_set(immu, domain, immu->immu_ctx_root, d_bus,
17283a634bfcSVikram Hegde 		    d_devfunc);
17293a634bfcSVikram Hegde 	} else {
17303a634bfcSVikram Hegde 		ddi_err(DER_PANIC, rdip, "unknown device type. Cannot "
173150200e77SFrank Van Der Linden 		    "set iommu context.");
17323a634bfcSVikram Hegde 		/*NOTREACHED*/
17333a634bfcSVikram Hegde 	}
17343a634bfcSVikram Hegde 
17353a634bfcSVikram Hegde 	/* XXX do we need a membar_producer() here */
17363a634bfcSVikram Hegde 	return (DDI_SUCCESS);
17373a634bfcSVikram Hegde }
17383a634bfcSVikram Hegde 
17393a634bfcSVikram Hegde /* ##################### END CONTEXT CODE ################################## */
17403a634bfcSVikram Hegde /* ##################### MAPPING CODE ################################## */
17413a634bfcSVikram Hegde 
17423a634bfcSVikram Hegde 
174350200e77SFrank Van Der Linden #ifdef DEBUG
17443a634bfcSVikram Hegde static boolean_t
PDTE_check(immu_t * immu,hw_pdte_t pdte,pgtable_t * next,paddr_t paddr,dev_info_t * rdip,immu_flags_t immu_flags)17453a634bfcSVikram Hegde PDTE_check(immu_t *immu, hw_pdte_t pdte, pgtable_t *next, paddr_t paddr,
17463a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags)
17473a634bfcSVikram Hegde {
17483a634bfcSVikram Hegde 	/* The PDTE must be set i.e. present bit is set */
17493a634bfcSVikram Hegde 	if (!PDTE_P(pdte)) {
17503a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "No present flag");
17513a634bfcSVikram Hegde 		return (B_FALSE);
17523a634bfcSVikram Hegde 	}
17533a634bfcSVikram Hegde 
17543a634bfcSVikram Hegde 	/*
17553a634bfcSVikram Hegde 	 * Just assert to check most significant system software field
17563a634bfcSVikram Hegde 	 * (PDTE_SW4) as it is same as present bit and we
17573a634bfcSVikram Hegde 	 * checked that above
17583a634bfcSVikram Hegde 	 */
17593a634bfcSVikram Hegde 	ASSERT(PDTE_SW4(pdte));
17603a634bfcSVikram Hegde 
17613a634bfcSVikram Hegde 	/*
17623a634bfcSVikram Hegde 	 * TM field should be clear if not reserved.
17633a634bfcSVikram Hegde 	 * non-leaf is always reserved
17643a634bfcSVikram Hegde 	 */
1765e03dceedSVikram Hegde 	if (next == NULL && immu->immu_TM_reserved == B_FALSE) {
17663a634bfcSVikram Hegde 		if (PDTE_TM(pdte)) {
17673a634bfcSVikram Hegde 			ddi_err(DER_MODE, rdip, "TM flag set");
17683a634bfcSVikram Hegde 			return (B_FALSE);
17693a634bfcSVikram Hegde 		}
17703a634bfcSVikram Hegde 	}
17713a634bfcSVikram Hegde 
17723a634bfcSVikram Hegde 	/*
17733a634bfcSVikram Hegde 	 * The SW3 field is not used and must be clear
17743a634bfcSVikram Hegde 	 */
17753a634bfcSVikram Hegde 	if (PDTE_SW3(pdte)) {
17763a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "SW3 set");
17773a634bfcSVikram Hegde 		return (B_FALSE);
17783a634bfcSVikram Hegde 	}
17793a634bfcSVikram Hegde 
17803a634bfcSVikram Hegde 	/*
17813a634bfcSVikram Hegde 	 * PFN (for PTE) or next level pgtable-paddr (for PDE) must be set
17823a634bfcSVikram Hegde 	 */
17833a634bfcSVikram Hegde 	if (next == NULL) {
17843a634bfcSVikram Hegde 		ASSERT(paddr % IMMU_PAGESIZE == 0);
17853a634bfcSVikram Hegde 		if (PDTE_PADDR(pdte) != paddr) {
17863a634bfcSVikram Hegde 			ddi_err(DER_MODE, rdip,
17873a634bfcSVikram Hegde 			    "PTE paddr mismatch: %lx != %lx",
17883a634bfcSVikram Hegde 			    PDTE_PADDR(pdte), paddr);
17893a634bfcSVikram Hegde 			return (B_FALSE);
17903a634bfcSVikram Hegde 		}
17913a634bfcSVikram Hegde 	} else {
17923a634bfcSVikram Hegde 		if (PDTE_PADDR(pdte) != next->hwpg_paddr) {
17933a634bfcSVikram Hegde 			ddi_err(DER_MODE, rdip,
17943a634bfcSVikram Hegde 			    "PDE paddr mismatch: %lx != %lx",
17953a634bfcSVikram Hegde 			    PDTE_PADDR(pdte), next->hwpg_paddr);
17963a634bfcSVikram Hegde 			return (B_FALSE);
17973a634bfcSVikram Hegde 		}
17983a634bfcSVikram Hegde 	}
17993a634bfcSVikram Hegde 
18003a634bfcSVikram Hegde 	/*
18013a634bfcSVikram Hegde 	 * SNP field should be clear if not reserved.
18023a634bfcSVikram Hegde 	 * non-leaf is always reserved
18033a634bfcSVikram Hegde 	 */
1804e03dceedSVikram Hegde 	if (next == NULL && immu->immu_SNP_reserved == B_FALSE) {
18053a634bfcSVikram Hegde 		if (PDTE_SNP(pdte)) {
18063a634bfcSVikram Hegde 			ddi_err(DER_MODE, rdip, "SNP set");
18073a634bfcSVikram Hegde 			return (B_FALSE);
18083a634bfcSVikram Hegde 		}
18093a634bfcSVikram Hegde 	}
18103a634bfcSVikram Hegde 
18113a634bfcSVikram Hegde 	/* second field available for system software should be clear */
18123a634bfcSVikram Hegde 	if (PDTE_SW2(pdte)) {
18133a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "SW2 set");
18143a634bfcSVikram Hegde 		return (B_FALSE);
18153a634bfcSVikram Hegde 	}
18163a634bfcSVikram Hegde 
18173a634bfcSVikram Hegde 	/* Super pages field should be clear */
18183a634bfcSVikram Hegde 	if (PDTE_SP(pdte)) {
18193a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "SP set");
18203a634bfcSVikram Hegde 		return (B_FALSE);
18213a634bfcSVikram Hegde 	}
18223a634bfcSVikram Hegde 
18233a634bfcSVikram Hegde 	/*
18243a634bfcSVikram Hegde 	 * least significant field available for
18253a634bfcSVikram Hegde 	 * system software should be clear
18263a634bfcSVikram Hegde 	 */
18273a634bfcSVikram Hegde 	if (PDTE_SW1(pdte)) {
18283a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "SW1 set");
18293a634bfcSVikram Hegde 		return (B_FALSE);
18303a634bfcSVikram Hegde 	}
18313a634bfcSVikram Hegde 
18323a634bfcSVikram Hegde 	if ((immu_flags & IMMU_FLAGS_READ) && !PDTE_READ(pdte)) {
18333a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "READ not set");
18343a634bfcSVikram Hegde 		return (B_FALSE);
18353a634bfcSVikram Hegde 	}
18363a634bfcSVikram Hegde 
18373a634bfcSVikram Hegde 	if ((immu_flags & IMMU_FLAGS_WRITE) && !PDTE_WRITE(pdte)) {
18383a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "WRITE not set");
18393a634bfcSVikram Hegde 		return (B_FALSE);
18403a634bfcSVikram Hegde 	}
18413a634bfcSVikram Hegde 
18423a634bfcSVikram Hegde 	return (B_TRUE);
18433a634bfcSVikram Hegde }
184450200e77SFrank Van Der Linden #endif
184550200e77SFrank Van Der Linden 
18463a634bfcSVikram Hegde /*ARGSUSED*/
18473a634bfcSVikram Hegde static void
PTE_clear_all(immu_t * immu,domain_t * domain,xlate_t * xlate,uint64_t * dvma_ptr,uint64_t * npages_ptr,dev_info_t * rdip)1848e03dceedSVikram Hegde PTE_clear_all(immu_t *immu, domain_t *domain, xlate_t *xlate,
1849e03dceedSVikram Hegde     uint64_t *dvma_ptr, uint64_t *npages_ptr, dev_info_t *rdip)
18503a634bfcSVikram Hegde {
1851e03dceedSVikram Hegde 	uint64_t npages;
1852e03dceedSVikram Hegde 	uint64_t dvma;
18533a634bfcSVikram Hegde 	pgtable_t *pgtable;
1854e03dceedSVikram Hegde 	hw_pdte_t *hwp;
1855e03dceedSVikram Hegde 	hw_pdte_t *shwp;
18563a634bfcSVikram Hegde 	int idx;
18573a634bfcSVikram Hegde 
18583a634bfcSVikram Hegde 	pgtable = xlate->xlt_pgtable;
1859e03dceedSVikram Hegde 	idx = xlate->xlt_idx;
18603a634bfcSVikram Hegde 
1861e03dceedSVikram Hegde 	dvma = *dvma_ptr;
1862e03dceedSVikram Hegde 	npages = *npages_ptr;
1863e03dceedSVikram Hegde 
18643a634bfcSVikram Hegde 	/*
1865e03dceedSVikram Hegde 	 * since a caller gets a unique dvma for a physical address,
1866e03dceedSVikram Hegde 	 * no other concurrent thread will be writing to the same
1867e03dceedSVikram Hegde 	 * PTE even if it has the same paddr. So no locks needed.
18683a634bfcSVikram Hegde 	 */
1869e03dceedSVikram Hegde 	shwp = (hw_pdte_t *)(pgtable->hwpg_vaddr) + idx;
1870e03dceedSVikram Hegde 
1871e03dceedSVikram Hegde 	hwp = shwp;
1872e03dceedSVikram Hegde 	for (; npages > 0 && idx <= IMMU_PGTABLE_MAXIDX; idx++, hwp++) {
187350200e77SFrank Van Der Linden 		PDTE_CLEAR_P(*hwp);
1874e03dceedSVikram Hegde 		dvma += IMMU_PAGESIZE;
1875e03dceedSVikram Hegde 		npages--;
1876e03dceedSVikram Hegde 	}
1877e03dceedSVikram Hegde 
1878e03dceedSVikram Hegde 	*dvma_ptr = dvma;
1879e03dceedSVikram Hegde 	*npages_ptr = npages;
1880e03dceedSVikram Hegde 
1881e03dceedSVikram Hegde 	xlate->xlt_idx = idx;
18823a634bfcSVikram Hegde }
18833a634bfcSVikram Hegde 
18843a634bfcSVikram Hegde static void
xlate_setup(uint64_t dvma,xlate_t * xlate,int nlevels)188550200e77SFrank Van Der Linden xlate_setup(uint64_t dvma, xlate_t *xlate, int nlevels)
18863a634bfcSVikram Hegde {
18873a634bfcSVikram Hegde 	int level;
18883a634bfcSVikram Hegde 	uint64_t offbits;
18893a634bfcSVikram Hegde 
18903a634bfcSVikram Hegde 	/*
18913a634bfcSVikram Hegde 	 * Skip the first 12 bits which is the offset into
18923a634bfcSVikram Hegde 	 * 4K PFN (phys page frame based on IMMU_PAGESIZE)
18933a634bfcSVikram Hegde 	 */
18943a634bfcSVikram Hegde 	offbits = dvma >> IMMU_PAGESHIFT;
18953a634bfcSVikram Hegde 
18963a634bfcSVikram Hegde 	/* skip to level 1 i.e. leaf PTE */
18973a634bfcSVikram Hegde 	for (level = 1, xlate++; level <= nlevels; level++, xlate++) {
18983a634bfcSVikram Hegde 		xlate->xlt_level = level;
18993a634bfcSVikram Hegde 		xlate->xlt_idx = (offbits & IMMU_PGTABLE_LEVEL_MASK);
19003a634bfcSVikram Hegde 		ASSERT(xlate->xlt_idx <= IMMU_PGTABLE_MAXIDX);
19013a634bfcSVikram Hegde 		xlate->xlt_pgtable = NULL;
19023a634bfcSVikram Hegde 		offbits >>= IMMU_PGTABLE_LEVEL_STRIDE;
19033a634bfcSVikram Hegde 	}
19043a634bfcSVikram Hegde }
19053a634bfcSVikram Hegde 
19063a634bfcSVikram Hegde /*
19073a634bfcSVikram Hegde  * Read the pgtables
19083a634bfcSVikram Hegde  */
190950200e77SFrank Van Der Linden static boolean_t
PDE_lookup(domain_t * domain,xlate_t * xlate,int nlevels)191050200e77SFrank Van Der Linden PDE_lookup(domain_t *domain, xlate_t *xlate, int nlevels)
19113a634bfcSVikram Hegde {
19123a634bfcSVikram Hegde 	pgtable_t *pgtable;
19133a634bfcSVikram Hegde 	pgtable_t *next;
19143a634bfcSVikram Hegde 	uint_t idx;
19153a634bfcSVikram Hegde 
19163a634bfcSVikram Hegde 	/* start with highest level pgtable i.e. root */
19173a634bfcSVikram Hegde 	xlate += nlevels;
19183a634bfcSVikram Hegde 
19193a634bfcSVikram Hegde 	if (xlate->xlt_pgtable == NULL) {
19203a634bfcSVikram Hegde 		xlate->xlt_pgtable = domain->dom_pgtable_root;
19213a634bfcSVikram Hegde 	}
19223a634bfcSVikram Hegde 
19233a634bfcSVikram Hegde 	for (; xlate->xlt_level > 1; xlate--) {
19243a634bfcSVikram Hegde 		idx = xlate->xlt_idx;
19253a634bfcSVikram Hegde 		pgtable = xlate->xlt_pgtable;
19263a634bfcSVikram Hegde 
19273a634bfcSVikram Hegde 		if ((xlate - 1)->xlt_pgtable) {
19283a634bfcSVikram Hegde 			continue;
19293a634bfcSVikram Hegde 		}
19303a634bfcSVikram Hegde 
19313a634bfcSVikram Hegde 		/* Lock the pgtable in read mode */
19323a634bfcSVikram Hegde 		rw_enter(&(pgtable->swpg_rwlock), RW_READER);
19333a634bfcSVikram Hegde 
19343a634bfcSVikram Hegde 		/*
19353a634bfcSVikram Hegde 		 * since we are unmapping, the pgtable should
19363a634bfcSVikram Hegde 		 * already point to a leafier pgtable.
19373a634bfcSVikram Hegde 		 */
19383a634bfcSVikram Hegde 		next = *(pgtable->swpg_next_array + idx);
193950200e77SFrank Van Der Linden 		(xlate - 1)->xlt_pgtable = next;
194050200e77SFrank Van Der Linden 		rw_exit(&(pgtable->swpg_rwlock));
194150200e77SFrank Van Der Linden 		if (next == NULL)
194250200e77SFrank Van Der Linden 			return (B_FALSE);
194350200e77SFrank Van Der Linden 	}
19443a634bfcSVikram Hegde 
194550200e77SFrank Van Der Linden 	return (B_TRUE);
194650200e77SFrank Van Der Linden }
19473a634bfcSVikram Hegde 
194850200e77SFrank Van Der Linden static void
immu_fault_walk(void * arg,void * base,size_t len)194950200e77SFrank Van Der Linden immu_fault_walk(void *arg, void *base, size_t len)
195050200e77SFrank Van Der Linden {
195150200e77SFrank Van Der Linden 	uint64_t dvma, start;
19523a634bfcSVikram Hegde 
195350200e77SFrank Van Der Linden 	dvma = *(uint64_t *)arg;
195450200e77SFrank Van Der Linden 	start = (uint64_t)(uintptr_t)base;
19553a634bfcSVikram Hegde 
195650200e77SFrank Van Der Linden 	if (dvma >= start && dvma < (start + len)) {
195750200e77SFrank Van Der Linden 		ddi_err(DER_WARN, NULL,
195850200e77SFrank Van Der Linden 		    "faulting DVMA address is in vmem arena "
195950200e77SFrank Van Der Linden 		    "(%" PRIx64 "-%" PRIx64 ")",
196050200e77SFrank Van Der Linden 		    start, start + len);
196150200e77SFrank Van Der Linden 		*(uint64_t *)arg = ~0ULL;
196250200e77SFrank Van Der Linden 	}
196350200e77SFrank Van Der Linden }
196450200e77SFrank Van Der Linden 
196550200e77SFrank Van Der Linden void
immu_print_fault_info(uint_t sid,uint64_t dvma)196650200e77SFrank Van Der Linden immu_print_fault_info(uint_t sid, uint64_t dvma)
196750200e77SFrank Van Der Linden {
196850200e77SFrank Van Der Linden 	int nlevels;
196950200e77SFrank Van Der Linden 	xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0};
197050200e77SFrank Van Der Linden 	xlate_t *xlatep;
197150200e77SFrank Van Der Linden 	hw_pdte_t pte;
197250200e77SFrank Van Der Linden 	domain_t *domain;
197350200e77SFrank Van Der Linden 	immu_t *immu;
197450200e77SFrank Van Der Linden 	uint64_t dvma_arg;
197550200e77SFrank Van Der Linden 
197650200e77SFrank Van Der Linden 	if (mod_hash_find(bdf_domain_hash,
197750200e77SFrank Van Der Linden 	    (void *)(uintptr_t)sid, (void *)&domain) != 0) {
197850200e77SFrank Van Der Linden 		ddi_err(DER_WARN, NULL,
197950200e77SFrank Van Der Linden 		    "no domain for faulting SID %08x", sid);
198050200e77SFrank Van Der Linden 		return;
198150200e77SFrank Van Der Linden 	}
198250200e77SFrank Van Der Linden 
198350200e77SFrank Van Der Linden 	immu = domain->dom_immu;
198450200e77SFrank Van Der Linden 
198550200e77SFrank Van Der Linden 	dvma_arg = dvma;
198650200e77SFrank Van Der Linden 	vmem_walk(domain->dom_dvma_arena, VMEM_ALLOC, immu_fault_walk,
198750200e77SFrank Van Der Linden 	    (void *)&dvma_arg);
198850200e77SFrank Van Der Linden 	if (dvma_arg != ~0ULL)
198950200e77SFrank Van Der Linden 		ddi_err(DER_WARN, domain->dom_dip,
199050200e77SFrank Van Der Linden 		    "faulting DVMA address is not in vmem arena");
199150200e77SFrank Van Der Linden 
199250200e77SFrank Van Der Linden 	nlevels = immu->immu_dvma_nlevels;
199350200e77SFrank Van Der Linden 	xlate_setup(dvma, xlate, nlevels);
199450200e77SFrank Van Der Linden 
199550200e77SFrank Van Der Linden 	if (!PDE_lookup(domain, xlate, nlevels)) {
199650200e77SFrank Van Der Linden 		ddi_err(DER_WARN, domain->dom_dip,
199750200e77SFrank Van Der Linden 		    "pte not found in domid %d for faulting addr %" PRIx64,
199850200e77SFrank Van Der Linden 		    domain->dom_did, dvma);
199950200e77SFrank Van Der Linden 		return;
20003a634bfcSVikram Hegde 	}
200150200e77SFrank Van Der Linden 
200250200e77SFrank Van Der Linden 	xlatep = &xlate[1];
200350200e77SFrank Van Der Linden 	pte = *((hw_pdte_t *)
200450200e77SFrank Van Der Linden 	    (xlatep->xlt_pgtable->hwpg_vaddr) + xlatep->xlt_idx);
200550200e77SFrank Van Der Linden 
200650200e77SFrank Van Der Linden 	ddi_err(DER_WARN, domain->dom_dip,
200750200e77SFrank Van Der Linden 	    "domid %d pte: %" PRIx64 "(paddr %" PRIx64 ")", domain->dom_did,
200850200e77SFrank Van Der Linden 	    (unsigned long long)pte, (unsigned long long)PDTE_PADDR(pte));
20093a634bfcSVikram Hegde }
20103a634bfcSVikram Hegde 
2011e03dceedSVikram Hegde /*ARGSUSED*/
20123a634bfcSVikram Hegde static void
PTE_set_one(immu_t * immu,hw_pdte_t * hwp,paddr_t paddr,dev_info_t * rdip,immu_flags_t immu_flags)20133a634bfcSVikram Hegde PTE_set_one(immu_t *immu, hw_pdte_t *hwp, paddr_t paddr,
20143a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags)
20153a634bfcSVikram Hegde {
20163a634bfcSVikram Hegde 	hw_pdte_t pte;
20173a634bfcSVikram Hegde 
2018e03dceedSVikram Hegde #ifndef DEBUG
201950200e77SFrank Van Der Linden 	pte = immu->immu_ptemask;
2020e03dceedSVikram Hegde 	PDTE_SET_PADDR(pte, paddr);
2021e03dceedSVikram Hegde #else
202250200e77SFrank Van Der Linden 	pte = *hwp;
2023e03dceedSVikram Hegde 
20243a634bfcSVikram Hegde 	if (PDTE_P(pte)) {
20253a634bfcSVikram Hegde 		if (PDTE_PADDR(pte) != paddr) {
20263a634bfcSVikram Hegde 			ddi_err(DER_MODE, rdip, "PTE paddr %lx != paddr %lx",
20273a634bfcSVikram Hegde 			    PDTE_PADDR(pte), paddr);
20283a634bfcSVikram Hegde 		}
2029e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS
2030e03dceedSVikram Hegde 		return;
2031e03dceedSVikram Hegde #else
20323a634bfcSVikram Hegde 		goto out;
2033e03dceedSVikram Hegde #endif
20343a634bfcSVikram Hegde 	}
20353a634bfcSVikram Hegde 
20363a634bfcSVikram Hegde 	/* clear TM field if not reserved */
2037e03dceedSVikram Hegde 	if (immu->immu_TM_reserved == B_FALSE) {
20383a634bfcSVikram Hegde 		PDTE_CLEAR_TM(pte);
20393a634bfcSVikram Hegde 	}
20403a634bfcSVikram Hegde 
20413a634bfcSVikram Hegde 	/* Clear 3rd field for system software  - not used */
20423a634bfcSVikram Hegde 	PDTE_CLEAR_SW3(pte);
20433a634bfcSVikram Hegde 
20443a634bfcSVikram Hegde 	/* Set paddr */
20453a634bfcSVikram Hegde 	ASSERT(paddr % IMMU_PAGESIZE == 0);
20463a634bfcSVikram Hegde 	PDTE_CLEAR_PADDR(pte);
20473a634bfcSVikram Hegde 	PDTE_SET_PADDR(pte, paddr);
20483a634bfcSVikram Hegde 
20493a634bfcSVikram Hegde 	/*  clear SNP field if not reserved. */
2050e03dceedSVikram Hegde 	if (immu->immu_SNP_reserved == B_FALSE) {
20513a634bfcSVikram Hegde 		PDTE_CLEAR_SNP(pte);
20523a634bfcSVikram Hegde 	}
20533a634bfcSVikram Hegde 
20543a634bfcSVikram Hegde 	/* Clear SW2 field available for software */
20553a634bfcSVikram Hegde 	PDTE_CLEAR_SW2(pte);
2056e03dceedSVikram Hegde 
20573a634bfcSVikram Hegde 
20583a634bfcSVikram Hegde 	/* SP is don't care for PTEs. Clear it for cleanliness */
20593a634bfcSVikram Hegde 	PDTE_CLEAR_SP(pte);
20603a634bfcSVikram Hegde 
20613a634bfcSVikram Hegde 	/* Clear SW1 field available for software */
20623a634bfcSVikram Hegde 	PDTE_CLEAR_SW1(pte);
20633a634bfcSVikram Hegde 
20643a634bfcSVikram Hegde 	/*
20653a634bfcSVikram Hegde 	 * Now that we are done writing the PTE
20663a634bfcSVikram Hegde 	 * set the "present" flag. Note this present
20673a634bfcSVikram Hegde 	 * flag is a bit in the PDE/PTE that the
20683a634bfcSVikram Hegde 	 * spec says is available for system software.
20693a634bfcSVikram Hegde 	 * This is an implementation detail of Solaris
20703a634bfcSVikram Hegde 	 * bare-metal Intel IOMMU.
20713a634bfcSVikram Hegde 	 * The present field in a PDE/PTE is not defined
20723a634bfcSVikram Hegde 	 * by the Vt-d spec
20733a634bfcSVikram Hegde 	 */
20743a634bfcSVikram Hegde 
20753a634bfcSVikram Hegde 	PDTE_SET_P(pte);
20763a634bfcSVikram Hegde 
207750200e77SFrank Van Der Linden 	pte |= immu->immu_ptemask;
207850200e77SFrank Van Der Linden 
20793a634bfcSVikram Hegde out:
208050200e77SFrank Van Der Linden #endif /* DEBUG */
2081e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS
2082e03dceedSVikram Hegde 	PDTE_SET_READ(pte);
2083e03dceedSVikram Hegde 	PDTE_SET_WRITE(pte);
2084e03dceedSVikram Hegde #else
20853a634bfcSVikram Hegde 	if (immu_flags & IMMU_FLAGS_READ)
20863a634bfcSVikram Hegde 		PDTE_SET_READ(pte);
20873a634bfcSVikram Hegde 	if (immu_flags & IMMU_FLAGS_WRITE)
20883a634bfcSVikram Hegde 		PDTE_SET_WRITE(pte);
208950200e77SFrank Van Der Linden #endif /* BUGGY_DRIVERS */
20903a634bfcSVikram Hegde 
20913a634bfcSVikram Hegde 	*hwp = pte;
20923a634bfcSVikram Hegde }
20933a634bfcSVikram Hegde 
20943a634bfcSVikram Hegde /*ARGSUSED*/
20953a634bfcSVikram Hegde static void
PTE_set_all(immu_t * immu,domain_t * domain,xlate_t * xlate,uint64_t * dvma_ptr,uint64_t * nvpages_ptr,immu_dcookie_t * dcookies,int dcount,dev_info_t * rdip,immu_flags_t immu_flags)20963a634bfcSVikram Hegde PTE_set_all(immu_t *immu, domain_t *domain, xlate_t *xlate,
209750200e77SFrank Van Der Linden     uint64_t *dvma_ptr, uint64_t *nvpages_ptr, immu_dcookie_t *dcookies,
2098e03dceedSVikram Hegde     int dcount, dev_info_t *rdip, immu_flags_t immu_flags)
20993a634bfcSVikram Hegde {
21003a634bfcSVikram Hegde 	paddr_t paddr;
2101e03dceedSVikram Hegde 	uint64_t nvpages;
2102e03dceedSVikram Hegde 	uint64_t nppages;
21033a634bfcSVikram Hegde 	uint64_t dvma;
21043a634bfcSVikram Hegde 	pgtable_t *pgtable;
21053a634bfcSVikram Hegde 	hw_pdte_t *hwp;
21063a634bfcSVikram Hegde 	hw_pdte_t *shwp;
210750200e77SFrank Van Der Linden 	int idx, nset;
2108e03dceedSVikram Hegde 	int j;
21093a634bfcSVikram Hegde 
21103a634bfcSVikram Hegde 	pgtable = xlate->xlt_pgtable;
21113a634bfcSVikram Hegde 	idx = xlate->xlt_idx;
21123a634bfcSVikram Hegde 
21133a634bfcSVikram Hegde 	dvma = *dvma_ptr;
2114e03dceedSVikram Hegde 	nvpages = *nvpages_ptr;
21153a634bfcSVikram Hegde 
21163a634bfcSVikram Hegde 	/*
2117e03dceedSVikram Hegde 	 * since a caller gets a unique dvma for a physical address,
2118e03dceedSVikram Hegde 	 * no other concurrent thread will be writing to the same
2119e03dceedSVikram Hegde 	 * PTE even if it has the same paddr. So no locks needed.
21203a634bfcSVikram Hegde 	 */
21213a634bfcSVikram Hegde 	shwp = (hw_pdte_t *)(pgtable->hwpg_vaddr) + idx;
21223a634bfcSVikram Hegde 
21233a634bfcSVikram Hegde 	hwp = shwp;
2124e03dceedSVikram Hegde 	for (j = dcount - 1; j >= 0; j--) {
2125e03dceedSVikram Hegde 		if (nvpages <= dcookies[j].dck_npages)
2126e03dceedSVikram Hegde 			break;
2127e03dceedSVikram Hegde 		nvpages -= dcookies[j].dck_npages;
2128e03dceedSVikram Hegde 	}
2129e03dceedSVikram Hegde 
21302cedd8f0SHans Rosenfeld 	VERIFY(j >= 0);
2131e03dceedSVikram Hegde 	nppages = nvpages;
2132e03dceedSVikram Hegde 	paddr = dcookies[j].dck_paddr +
2133e03dceedSVikram Hegde 	    (dcookies[j].dck_npages - nppages) * IMMU_PAGESIZE;
2134e03dceedSVikram Hegde 
2135e03dceedSVikram Hegde 	nvpages = *nvpages_ptr;
213650200e77SFrank Van Der Linden 	nset = 0;
2137e03dceedSVikram Hegde 	for (; nvpages > 0 && idx <= IMMU_PGTABLE_MAXIDX; idx++, hwp++) {
21383a634bfcSVikram Hegde 		PTE_set_one(immu, hwp, paddr, rdip, immu_flags);
213950200e77SFrank Van Der Linden 		nset++;
21403a634bfcSVikram Hegde 
21413a634bfcSVikram Hegde 		ASSERT(PDTE_check(immu, *hwp, NULL, paddr, rdip, immu_flags)
21423a634bfcSVikram Hegde 		    == B_TRUE);
2143e03dceedSVikram Hegde 		nppages--;
2144e03dceedSVikram Hegde 		nvpages--;
21453a634bfcSVikram Hegde 		paddr += IMMU_PAGESIZE;
21463a634bfcSVikram Hegde 		dvma += IMMU_PAGESIZE;
2147e03dceedSVikram Hegde 
2148e03dceedSVikram Hegde 		if (nppages == 0) {
2149e03dceedSVikram Hegde 			j++;
2150e03dceedSVikram Hegde 		}
2151e03dceedSVikram Hegde 
215250200e77SFrank Van Der Linden 		if (j == dcount)
2153e03dceedSVikram Hegde 			break;
2154e03dceedSVikram Hegde 
2155e03dceedSVikram Hegde 		if (nppages == 0) {
2156e03dceedSVikram Hegde 			nppages = dcookies[j].dck_npages;
2157e03dceedSVikram Hegde 			paddr = dcookies[j].dck_paddr;
2158e03dceedSVikram Hegde 		}
21593a634bfcSVikram Hegde 	}
21603a634bfcSVikram Hegde 
2161e03dceedSVikram Hegde 	if (nvpages) {
2162e03dceedSVikram Hegde 		*dvma_ptr = dvma;
2163e03dceedSVikram Hegde 		*nvpages_ptr = nvpages;
2164e03dceedSVikram Hegde 	} else {
2165e03dceedSVikram Hegde 		*dvma_ptr = 0;
2166e03dceedSVikram Hegde 		*nvpages_ptr = 0;
2167e03dceedSVikram Hegde 	}
21683a634bfcSVikram Hegde 
2169e03dceedSVikram Hegde 	xlate->xlt_idx = idx;
21703a634bfcSVikram Hegde }
21713a634bfcSVikram Hegde 
21723a634bfcSVikram Hegde /*ARGSUSED*/
21733a634bfcSVikram Hegde static void
PDE_set_one(immu_t * immu,hw_pdte_t * hwp,pgtable_t * next,dev_info_t * rdip,immu_flags_t immu_flags)21743a634bfcSVikram Hegde PDE_set_one(immu_t *immu, hw_pdte_t *hwp, pgtable_t *next,
21753a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags)
21763a634bfcSVikram Hegde {
21773a634bfcSVikram Hegde 	hw_pdte_t pde;
21783a634bfcSVikram Hegde 
21793a634bfcSVikram Hegde 	pde = *hwp;
21803a634bfcSVikram Hegde 
21813a634bfcSVikram Hegde 	/* if PDE is already set, make sure it is correct */
21823a634bfcSVikram Hegde 	if (PDTE_P(pde)) {
21833a634bfcSVikram Hegde 		ASSERT(PDTE_PADDR(pde) == next->hwpg_paddr);
2184e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS
2185e03dceedSVikram Hegde 		return;
2186e03dceedSVikram Hegde #else
21873a634bfcSVikram Hegde 		goto out;
2188e03dceedSVikram Hegde #endif
21893a634bfcSVikram Hegde 	}
21903a634bfcSVikram Hegde 
21913a634bfcSVikram Hegde 	/* Dont touch SW4, it is the present bit */
21923a634bfcSVikram Hegde 
21933a634bfcSVikram Hegde 	/* don't touch TM field it is reserved for PDEs */
21943a634bfcSVikram Hegde 
21953a634bfcSVikram Hegde 	/* 3rd field available for system software is not used */
21963a634bfcSVikram Hegde 	PDTE_CLEAR_SW3(pde);
21973a634bfcSVikram Hegde 
21983a634bfcSVikram Hegde 	/* Set next level pgtable-paddr for PDE */
21993a634bfcSVikram Hegde 	PDTE_CLEAR_PADDR(pde);
22003a634bfcSVikram Hegde 	PDTE_SET_PADDR(pde, next->hwpg_paddr);
22013a634bfcSVikram Hegde 
22023a634bfcSVikram Hegde 	/* don't touch SNP field it is reserved for PDEs */
22033a634bfcSVikram Hegde 
22043a634bfcSVikram Hegde 	/* Clear second field available for system software */
22053a634bfcSVikram Hegde 	PDTE_CLEAR_SW2(pde);
22063a634bfcSVikram Hegde 
22073a634bfcSVikram Hegde 	/* No super pages for PDEs */
22083a634bfcSVikram Hegde 	PDTE_CLEAR_SP(pde);
22093a634bfcSVikram Hegde 
22103a634bfcSVikram Hegde 	/* Clear SW1 for software */
22113a634bfcSVikram Hegde 	PDTE_CLEAR_SW1(pde);
22123a634bfcSVikram Hegde 
22133a634bfcSVikram Hegde 	/*
22143a634bfcSVikram Hegde 	 * Now that we are done writing the PDE
22153a634bfcSVikram Hegde 	 * set the "present" flag. Note this present
22163a634bfcSVikram Hegde 	 * flag is a bit in the PDE/PTE that the
22173a634bfcSVikram Hegde 	 * spec says is available for system software.
22183a634bfcSVikram Hegde 	 * This is an implementation detail of Solaris
22193a634bfcSVikram Hegde 	 * base-metal Intel IOMMU.
22203a634bfcSVikram Hegde 	 * The present field in a PDE/PTE is not defined
22213a634bfcSVikram Hegde 	 * by the Vt-d spec
22223a634bfcSVikram Hegde 	 */
22233a634bfcSVikram Hegde 
2224e03dceedSVikram Hegde out:
2225e03dceedSVikram Hegde #ifdef  BUGGY_DRIVERS
2226e03dceedSVikram Hegde 	PDTE_SET_READ(pde);
2227e03dceedSVikram Hegde 	PDTE_SET_WRITE(pde);
2228e03dceedSVikram Hegde #else
22293a634bfcSVikram Hegde 	if (immu_flags & IMMU_FLAGS_READ)
22303a634bfcSVikram Hegde 		PDTE_SET_READ(pde);
22313a634bfcSVikram Hegde 	if (immu_flags & IMMU_FLAGS_WRITE)
22323a634bfcSVikram Hegde 		PDTE_SET_WRITE(pde);
22333a634bfcSVikram Hegde #endif
22343a634bfcSVikram Hegde 
22353a634bfcSVikram Hegde 	PDTE_SET_P(pde);
22363a634bfcSVikram Hegde 
22373a634bfcSVikram Hegde 	*hwp = pde;
22383a634bfcSVikram Hegde }
22393a634bfcSVikram Hegde 
22403a634bfcSVikram Hegde /*
22413a634bfcSVikram Hegde  * Used to set PDEs
22423a634bfcSVikram Hegde  */
2243e03dceedSVikram Hegde static boolean_t
PDE_set_all(immu_t * immu,domain_t * domain,xlate_t * xlate,int nlevels,dev_info_t * rdip,immu_flags_t immu_flags)22443a634bfcSVikram Hegde PDE_set_all(immu_t *immu, domain_t *domain, xlate_t *xlate, int nlevels,
22453a634bfcSVikram Hegde     dev_info_t *rdip, immu_flags_t immu_flags)
22463a634bfcSVikram Hegde {
22473a634bfcSVikram Hegde 	pgtable_t *pgtable;
22483a634bfcSVikram Hegde 	pgtable_t *new;
22493a634bfcSVikram Hegde 	pgtable_t *next;
22503a634bfcSVikram Hegde 	hw_pdte_t *hwp;
22513a634bfcSVikram Hegde 	int level;
22523a634bfcSVikram Hegde 	uint_t idx;
2253e03dceedSVikram Hegde 	krw_t rwtype;
2254e03dceedSVikram Hegde 	boolean_t set = B_FALSE;
22553a634bfcSVikram Hegde 
22563a634bfcSVikram Hegde 	/* start with highest level pgtable i.e. root */
22573a634bfcSVikram Hegde 	xlate += nlevels;
22583a634bfcSVikram Hegde 
22593a634bfcSVikram Hegde 	new = NULL;
22603a634bfcSVikram Hegde 	xlate->xlt_pgtable = domain->dom_pgtable_root;
22613a634bfcSVikram Hegde 	for (level = nlevels; level > 1; level--, xlate--) {
22623a634bfcSVikram Hegde 		idx = xlate->xlt_idx;
22633a634bfcSVikram Hegde 		pgtable = xlate->xlt_pgtable;
22643a634bfcSVikram Hegde 
2265e03dceedSVikram Hegde 		/* Lock the pgtable in READ mode first */
2266e03dceedSVikram Hegde 		rw_enter(&(pgtable->swpg_rwlock), RW_READER);
2267e03dceedSVikram Hegde 		rwtype = RW_READER;
2268e03dceedSVikram Hegde again:
22693a634bfcSVikram Hegde 		hwp = (hw_pdte_t *)(pgtable->hwpg_vaddr) + idx;
22703a634bfcSVikram Hegde 		next = (pgtable->swpg_next_array)[idx];
22713a634bfcSVikram Hegde 
22723a634bfcSVikram Hegde 		/*
22733a634bfcSVikram Hegde 		 * check if leafier level already has a pgtable
22743a634bfcSVikram Hegde 		 * if yes, verify
22753a634bfcSVikram Hegde 		 */
22763a634bfcSVikram Hegde 		if (next == NULL) {
227750200e77SFrank Van Der Linden 			if (new == NULL) {
227850200e77SFrank Van Der Linden 
227950200e77SFrank Van Der Linden 				IMMU_DPROBE2(immu__pdp__alloc, dev_info_t *,
228050200e77SFrank Van Der Linden 				    rdip, int, level);
228150200e77SFrank Van Der Linden 
228250200e77SFrank Van Der Linden 				new = pgtable_alloc(immu, immu_flags);
228350200e77SFrank Van Der Linden 				if (new == NULL) {
228450200e77SFrank Van Der Linden 					ddi_err(DER_PANIC, rdip,
228550200e77SFrank Van Der Linden 					    "pgtable alloc err");
228650200e77SFrank Van Der Linden 				}
228750200e77SFrank Van Der Linden 				pgtable_zero(new);
228850200e77SFrank Van Der Linden 			}
228950200e77SFrank Van Der Linden 
2290e03dceedSVikram Hegde 			/* Change to a write lock */
2291e03dceedSVikram Hegde 			if (rwtype == RW_READER &&
2292e03dceedSVikram Hegde 			    rw_tryupgrade(&(pgtable->swpg_rwlock)) == 0) {
2293e03dceedSVikram Hegde 				rw_exit(&(pgtable->swpg_rwlock));
2294e03dceedSVikram Hegde 				rw_enter(&(pgtable->swpg_rwlock), RW_WRITER);
2295e03dceedSVikram Hegde 				rwtype = RW_WRITER;
2296e03dceedSVikram Hegde 				goto again;
2297e03dceedSVikram Hegde 			}
2298e03dceedSVikram Hegde 			rwtype = RW_WRITER;
22993a634bfcSVikram Hegde 			next = new;
23003a634bfcSVikram Hegde 			(pgtable->swpg_next_array)[idx] = next;
230150200e77SFrank Van Der Linden 			new = NULL;
23023a634bfcSVikram Hegde 			PDE_set_one(immu, hwp, next, rdip, immu_flags);
2303e03dceedSVikram Hegde 			set = B_TRUE;
2304e03dceedSVikram Hegde 			rw_downgrade(&(pgtable->swpg_rwlock));
2305e03dceedSVikram Hegde 			rwtype = RW_READER;
230650200e77SFrank Van Der Linden 		}
230750200e77SFrank Van Der Linden #ifndef  BUGGY_DRIVERS
230850200e77SFrank Van Der Linden 		else {
23093a634bfcSVikram Hegde 			hw_pdte_t pde = *hwp;
23103a634bfcSVikram Hegde 
2311e03dceedSVikram Hegde 			/*
2312e03dceedSVikram Hegde 			 * If buggy driver we already set permission
2313e03dceedSVikram Hegde 			 * READ+WRITE so nothing to do for that case
2314e03dceedSVikram Hegde 			 * XXX Check that read writer perms change before
2315e03dceedSVikram Hegde 			 * actually setting perms. Also need to hold lock
2316e03dceedSVikram Hegde 			 */
23173a634bfcSVikram Hegde 			if (immu_flags & IMMU_FLAGS_READ)
23183a634bfcSVikram Hegde 				PDTE_SET_READ(pde);
23193a634bfcSVikram Hegde 			if (immu_flags & IMMU_FLAGS_WRITE)
23203a634bfcSVikram Hegde 				PDTE_SET_WRITE(pde);
23213a634bfcSVikram Hegde 
23223a634bfcSVikram Hegde 			*hwp = pde;
23233a634bfcSVikram Hegde 		}
232450200e77SFrank Van Der Linden #endif
23253a634bfcSVikram Hegde 
23263a634bfcSVikram Hegde 		ASSERT(PDTE_check(immu, *hwp, next, 0, rdip, immu_flags)
23273a634bfcSVikram Hegde 		    == B_TRUE);
23283a634bfcSVikram Hegde 
23293a634bfcSVikram Hegde 		(xlate - 1)->xlt_pgtable = next;
23303a634bfcSVikram Hegde 		rw_exit(&(pgtable->swpg_rwlock));
23313a634bfcSVikram Hegde 	}
23323a634bfcSVikram Hegde 
23333a634bfcSVikram Hegde 	if (new) {
2334e03dceedSVikram Hegde 		pgtable_free(immu, new);
23353a634bfcSVikram Hegde 	}
2336e03dceedSVikram Hegde 
2337e03dceedSVikram Hegde 	return (set);
23383a634bfcSVikram Hegde }
23393a634bfcSVikram Hegde 
23403a634bfcSVikram Hegde /*
23413a634bfcSVikram Hegde  * dvma_map()
23423a634bfcSVikram Hegde  *     map a contiguous range of DVMA pages
23433a634bfcSVikram Hegde  *
23443a634bfcSVikram Hegde  *     immu: IOMMU unit for which we are generating DVMA cookies
23453a634bfcSVikram Hegde  *   domain: domain
23463a634bfcSVikram Hegde  *    sdvma: Starting dvma
23473a634bfcSVikram Hegde  *   spaddr: Starting paddr
23483a634bfcSVikram Hegde  *   npages: Number of pages
23493a634bfcSVikram Hegde  *     rdip: requesting device
23503a634bfcSVikram Hegde  *     immu_flags: flags
23513a634bfcSVikram Hegde  */
2352e03dceedSVikram Hegde static boolean_t
dvma_map(domain_t * domain,uint64_t sdvma,uint64_t snvpages,immu_dcookie_t * dcookies,int dcount,dev_info_t * rdip,immu_flags_t immu_flags)235350200e77SFrank Van Der Linden dvma_map(domain_t *domain, uint64_t sdvma, uint64_t snvpages,
235450200e77SFrank Van Der Linden     immu_dcookie_t *dcookies, int dcount, dev_info_t *rdip,
235550200e77SFrank Van Der Linden     immu_flags_t immu_flags)
23563a634bfcSVikram Hegde {
23573a634bfcSVikram Hegde 	uint64_t dvma;
23583a634bfcSVikram Hegde 	uint64_t n;
235950200e77SFrank Van Der Linden 	immu_t *immu = domain->dom_immu;
23603a634bfcSVikram Hegde 	int nlevels = immu->immu_dvma_nlevels;
23613a634bfcSVikram Hegde 	xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0};
2362e03dceedSVikram Hegde 	boolean_t pde_set = B_FALSE;
23633a634bfcSVikram Hegde 
2364e03dceedSVikram Hegde 	n = snvpages;
23653a634bfcSVikram Hegde 	dvma = sdvma;
23663a634bfcSVikram Hegde 
23673a634bfcSVikram Hegde 	while (n > 0) {
236850200e77SFrank Van Der Linden 		xlate_setup(dvma, xlate, nlevels);
23693a634bfcSVikram Hegde 
23703a634bfcSVikram Hegde 		/* Lookup or allocate PGDIRs and PGTABLEs if necessary */
2371e03dceedSVikram Hegde 		if (PDE_set_all(immu, domain, xlate, nlevels, rdip, immu_flags)
2372e03dceedSVikram Hegde 		    == B_TRUE) {
2373e03dceedSVikram Hegde 			pde_set = B_TRUE;
2374e03dceedSVikram Hegde 		}
23753a634bfcSVikram Hegde 
23763a634bfcSVikram Hegde 		/* set all matching ptes that fit into this leaf pgtable */
2377e03dceedSVikram Hegde 		PTE_set_all(immu, domain, &xlate[1], &dvma, &n, dcookies,
2378e03dceedSVikram Hegde 		    dcount, rdip, immu_flags);
23793a634bfcSVikram Hegde 	}
2380e03dceedSVikram Hegde 
2381e03dceedSVikram Hegde 	return (pde_set);
23823a634bfcSVikram Hegde }
23833a634bfcSVikram Hegde 
23843a634bfcSVikram Hegde /*
23853a634bfcSVikram Hegde  * dvma_unmap()
23863a634bfcSVikram Hegde  *   unmap a range of DVMAs
23873a634bfcSVikram Hegde  *
23883a634bfcSVikram Hegde  * immu: IOMMU unit state
23893a634bfcSVikram Hegde  * domain: domain for requesting device
23903a634bfcSVikram Hegde  * ddip: domain-dip
23913a634bfcSVikram Hegde  * dvma: starting DVMA
23923a634bfcSVikram Hegde  * npages: Number of IMMU pages to be unmapped
23933a634bfcSVikram Hegde  * rdip: requesting device
23943a634bfcSVikram Hegde  */
23953a634bfcSVikram Hegde static void
dvma_unmap(domain_t * domain,uint64_t sdvma,uint64_t snpages,dev_info_t * rdip)239650200e77SFrank Van Der Linden dvma_unmap(domain_t *domain, uint64_t sdvma, uint64_t snpages,
23973a634bfcSVikram Hegde     dev_info_t *rdip)
23983a634bfcSVikram Hegde {
239950200e77SFrank Van Der Linden 	immu_t *immu = domain->dom_immu;
24003a634bfcSVikram Hegde 	int nlevels = immu->immu_dvma_nlevels;
24013a634bfcSVikram Hegde 	xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0};
2402e03dceedSVikram Hegde 	uint64_t n;
2403e03dceedSVikram Hegde 	uint64_t dvma;
24043a634bfcSVikram Hegde 
2405e03dceedSVikram Hegde 	dvma = sdvma;
2406e03dceedSVikram Hegde 	n = snpages;
2407e03dceedSVikram Hegde 
2408e03dceedSVikram Hegde 	while (n > 0) {
24093a634bfcSVikram Hegde 		/* setup the xlate array */
241050200e77SFrank Van Der Linden 		xlate_setup(dvma, xlate, nlevels);
24113a634bfcSVikram Hegde 
24123a634bfcSVikram Hegde 		/* just lookup existing pgtables. Should never fail */
241350200e77SFrank Van Der Linden 		if (!PDE_lookup(domain, xlate, nlevels))
241450200e77SFrank Van Der Linden 			ddi_err(DER_PANIC, rdip,
241550200e77SFrank Van Der Linden 			    "PTE not found for addr %" PRIx64,
241650200e77SFrank Van Der Linden 			    (unsigned long long)dvma);
24173a634bfcSVikram Hegde 
2418e03dceedSVikram Hegde 		/* clear all matching ptes that fit into this leaf pgtable */
2419e03dceedSVikram Hegde 		PTE_clear_all(immu, domain, &xlate[1], &dvma, &n, rdip);
24203a634bfcSVikram Hegde 	}
2421e03dceedSVikram Hegde 
2422e03dceedSVikram Hegde 	/* No need to flush IOTLB after unmap */
24233a634bfcSVikram Hegde }
24243a634bfcSVikram Hegde 
24253a634bfcSVikram Hegde static uint64_t
dvma_alloc(domain_t * domain,ddi_dma_attr_t * dma_attr,uint_t npages,int kmf)242650200e77SFrank Van Der Linden dvma_alloc(domain_t *domain, ddi_dma_attr_t *dma_attr, uint_t npages, int kmf)
24273a634bfcSVikram Hegde {
24283a634bfcSVikram Hegde 	uint64_t dvma;
2429e03dceedSVikram Hegde 	size_t xsize, align;
24303a634bfcSVikram Hegde 	uint64_t minaddr, maxaddr;
24313a634bfcSVikram Hegde 
24323a634bfcSVikram Hegde 	/* parameters */
24333a634bfcSVikram Hegde 	xsize = npages * IMMU_PAGESIZE;
24343a634bfcSVikram Hegde 	align = MAX((size_t)(dma_attr->dma_attr_align), IMMU_PAGESIZE);
24353a634bfcSVikram Hegde 	minaddr = dma_attr->dma_attr_addr_lo;
24363a634bfcSVikram Hegde 	maxaddr = dma_attr->dma_attr_addr_hi + 1;
24373a634bfcSVikram Hegde 
24383a634bfcSVikram Hegde 	/* handle the rollover cases */
24393a634bfcSVikram Hegde 	if (maxaddr < dma_attr->dma_attr_addr_hi) {
24403a634bfcSVikram Hegde 		maxaddr = dma_attr->dma_attr_addr_hi;
24413a634bfcSVikram Hegde 	}
24423a634bfcSVikram Hegde 
24433a634bfcSVikram Hegde 	/*
24443a634bfcSVikram Hegde 	 * allocate from vmem arena.
24453a634bfcSVikram Hegde 	 */
24463a634bfcSVikram Hegde 	dvma = (uint64_t)(uintptr_t)vmem_xalloc(domain->dom_dvma_arena,
2447e03dceedSVikram Hegde 	    xsize, align, 0, 0, (void *)(uintptr_t)minaddr,
244850200e77SFrank Van Der Linden 	    (void *)(uintptr_t)maxaddr, kmf);
24493a634bfcSVikram Hegde 
24503a634bfcSVikram Hegde 	return (dvma);
24513a634bfcSVikram Hegde }
24523a634bfcSVikram Hegde 
24533a634bfcSVikram Hegde static void
dvma_prealloc(dev_info_t * rdip,immu_hdl_priv_t * ihp,ddi_dma_attr_t * dma_attr)245450200e77SFrank Van Der Linden dvma_prealloc(dev_info_t *rdip, immu_hdl_priv_t *ihp, ddi_dma_attr_t *dma_attr)
24553a634bfcSVikram Hegde {
245650200e77SFrank Van Der Linden 	int nlevels;
245750200e77SFrank Van Der Linden 	xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0}, *xlp;
245850200e77SFrank Van Der Linden 	uint64_t dvma, n;
245950200e77SFrank Van Der Linden 	size_t xsize, align;
246050200e77SFrank Van Der Linden 	uint64_t minaddr, maxaddr, dmamax;
246150200e77SFrank Van Der Linden 	int on, npte, pindex;
246250200e77SFrank Van Der Linden 	hw_pdte_t *shwp;
246350200e77SFrank Van Der Linden 	immu_t *immu;
246450200e77SFrank Van Der Linden 	domain_t *domain;
24653a634bfcSVikram Hegde 
246650200e77SFrank Van Der Linden 	/* parameters */
246750200e77SFrank Van Der Linden 	domain = IMMU_DEVI(rdip)->imd_domain;
246850200e77SFrank Van Der Linden 	immu = domain->dom_immu;
246950200e77SFrank Van Der Linden 	nlevels = immu->immu_dvma_nlevels;
247050200e77SFrank Van Der Linden 	xsize = IMMU_NPREPTES * IMMU_PAGESIZE;
247150200e77SFrank Van Der Linden 	align = MAX((size_t)(dma_attr->dma_attr_align), IMMU_PAGESIZE);
247250200e77SFrank Van Der Linden 	minaddr = dma_attr->dma_attr_addr_lo;
247350200e77SFrank Van Der Linden 	if (dma_attr->dma_attr_flags & _DDI_DMA_BOUNCE_ON_SEG)
247450200e77SFrank Van Der Linden 		dmamax = dma_attr->dma_attr_seg;
247550200e77SFrank Van Der Linden 	else
247650200e77SFrank Van Der Linden 		dmamax = dma_attr->dma_attr_addr_hi;
247750200e77SFrank Van Der Linden 	maxaddr = dmamax + 1;
24783a634bfcSVikram Hegde 
247950200e77SFrank Van Der Linden 	if (maxaddr < dmamax)
248050200e77SFrank Van Der Linden 		maxaddr = dmamax;
24813a634bfcSVikram Hegde 
248250200e77SFrank Van Der Linden 	dvma = (uint64_t)(uintptr_t)vmem_xalloc(domain->dom_dvma_arena,
248350200e77SFrank Van Der Linden 	    xsize, align, 0, dma_attr->dma_attr_seg + 1,
248450200e77SFrank Van Der Linden 	    (void *)(uintptr_t)minaddr, (void *)(uintptr_t)maxaddr, VM_NOSLEEP);
24853a634bfcSVikram Hegde 
248650200e77SFrank Van Der Linden 	ihp->ihp_predvma = dvma;
248750200e77SFrank Van Der Linden 	ihp->ihp_npremapped = 0;
248850200e77SFrank Van Der Linden 	if (dvma == 0)
248950200e77SFrank Van Der Linden 		return;
249050200e77SFrank Van Der Linden 
249150200e77SFrank Van Der Linden 	n = IMMU_NPREPTES;
249250200e77SFrank Van Der Linden 	pindex = 0;
24933a634bfcSVikram Hegde 
2494e03dceedSVikram Hegde 	/*
249550200e77SFrank Van Der Linden 	 * Set up a mapping at address 0, just so that all PDPs get allocated
249650200e77SFrank Van Der Linden 	 * now. Although this initial mapping should never be used,
249750200e77SFrank Van Der Linden 	 * explicitly set it to read-only, just to be safe.
2498e03dceedSVikram Hegde 	 */
249950200e77SFrank Van Der Linden 	while (n > 0) {
250050200e77SFrank Van Der Linden 		xlate_setup(dvma, xlate, nlevels);
25013a634bfcSVikram Hegde 
250250200e77SFrank Van Der Linden 		(void) PDE_set_all(immu, domain, xlate, nlevels, rdip,
250350200e77SFrank Van Der Linden 		    IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
25043a634bfcSVikram Hegde 
250550200e77SFrank Van Der Linden 		xlp = &xlate[1];
250650200e77SFrank Van Der Linden 		shwp = (hw_pdte_t *)(xlp->xlt_pgtable->hwpg_vaddr)
250750200e77SFrank Van Der Linden 		    + xlp->xlt_idx;
250850200e77SFrank Van Der Linden 		on = n;
25093a634bfcSVikram Hegde 
251050200e77SFrank Van Der Linden 		PTE_set_all(immu, domain, xlp, &dvma, &n, &immu_precookie,
251150200e77SFrank Van Der Linden 		    1, rdip, IMMU_FLAGS_READ);
25123a634bfcSVikram Hegde 
251350200e77SFrank Van Der Linden 		npte = on - n;
25143a634bfcSVikram Hegde 
251550200e77SFrank Van Der Linden 		while (npte > 0) {
251650200e77SFrank Van Der Linden 			ihp->ihp_preptes[pindex++] = shwp;
251750200e77SFrank Van Der Linden #ifdef BUGGY_DRIVERS
251850200e77SFrank Van Der Linden 			PDTE_CLEAR_WRITE(*shwp);
251950200e77SFrank Van Der Linden #endif
252050200e77SFrank Van Der Linden 			shwp++;
252150200e77SFrank Van Der Linden 			npte--;
25223a634bfcSVikram Hegde 		}
252350200e77SFrank Van Der Linden 	}
252450200e77SFrank Van Der Linden }
25253a634bfcSVikram Hegde 
252650200e77SFrank Van Der Linden static void
dvma_prefree(dev_info_t * rdip,immu_hdl_priv_t * ihp)252750200e77SFrank Van Der Linden dvma_prefree(dev_info_t *rdip, immu_hdl_priv_t *ihp)
252850200e77SFrank Van Der Linden {
252950200e77SFrank Van Der Linden 	domain_t *domain;
25303a634bfcSVikram Hegde 
253150200e77SFrank Van Der Linden 	domain = IMMU_DEVI(rdip)->imd_domain;
2532e03dceedSVikram Hegde 
253350200e77SFrank Van Der Linden 	if (ihp->ihp_predvma != 0) {
253450200e77SFrank Van Der Linden 		dvma_unmap(domain, ihp->ihp_predvma, IMMU_NPREPTES, rdip);
253550200e77SFrank Van Der Linden 		vmem_free(domain->dom_dvma_arena,
253650200e77SFrank Van Der Linden 		    (void *)(uintptr_t)ihp->ihp_predvma,
253750200e77SFrank Van Der Linden 		    IMMU_NPREPTES * IMMU_PAGESIZE);
253850200e77SFrank Van Der Linden 	}
25393a634bfcSVikram Hegde }
25403a634bfcSVikram Hegde 
25413a634bfcSVikram Hegde static void
dvma_free(domain_t * domain,uint64_t dvma,uint64_t npages)254250200e77SFrank Van Der Linden dvma_free(domain_t *domain, uint64_t dvma, uint64_t npages)
25433a634bfcSVikram Hegde {
254450200e77SFrank Van Der Linden 	uint64_t size = npages * IMMU_PAGESIZE;
25453a634bfcSVikram Hegde 
254650200e77SFrank Van Der Linden 	if (domain->dom_maptype != IMMU_MAPTYPE_XLATE)
254750200e77SFrank Van Der Linden 		return;
25483a634bfcSVikram Hegde 
254950200e77SFrank Van Der Linden 	vmem_free(domain->dom_dvma_arena, (void *)(uintptr_t)dvma, size);
255050200e77SFrank Van Der Linden }
25513a634bfcSVikram Hegde 
255250200e77SFrank Van Der Linden static int
immu_map_dvmaseg(dev_info_t * rdip,ddi_dma_handle_t handle,immu_hdl_priv_t * ihp,struct ddi_dma_req * dmareq,ddi_dma_obj_t * dma_out)255350200e77SFrank Van Der Linden immu_map_dvmaseg(dev_info_t *rdip, ddi_dma_handle_t handle,
255450200e77SFrank Van Der Linden     immu_hdl_priv_t *ihp, struct ddi_dma_req *dmareq,
255550200e77SFrank Van Der Linden     ddi_dma_obj_t *dma_out)
255650200e77SFrank Van Der Linden {
255750200e77SFrank Van Der Linden 	domain_t *domain;
255850200e77SFrank Van Der Linden 	immu_t *immu;
255950200e77SFrank Van Der Linden 	immu_flags_t immu_flags;
256050200e77SFrank Van Der Linden 	ddi_dma_atyp_t buftype;
256150200e77SFrank Van Der Linden 	ddi_dma_obj_t *dmar_object;
256250200e77SFrank Van Der Linden 	ddi_dma_attr_t *attrp;
256350200e77SFrank Van Der Linden 	uint64_t offset, paddr, dvma, sdvma, rwmask;
256450200e77SFrank Van Der Linden 	size_t npages, npgalloc;
256550200e77SFrank Van Der Linden 	uint_t psize, size, pcnt, dmax;
256650200e77SFrank Van Der Linden 	page_t **pparray;
256750200e77SFrank Van Der Linden 	caddr_t vaddr;
256850200e77SFrank Van Der Linden 	page_t *page;
256950200e77SFrank Van Der Linden 	struct as *vas;
257050200e77SFrank Van Der Linden 	immu_dcookie_t *dcookies;
257150200e77SFrank Van Der Linden 	int pde_set;
25723a634bfcSVikram Hegde 
2573*584b574aSToomas Soome 	rwmask = 0;
2574*584b574aSToomas Soome 	page = NULL;
257550200e77SFrank Van Der Linden 	domain = IMMU_DEVI(rdip)->imd_domain;
257650200e77SFrank Van Der Linden 	immu = domain->dom_immu;
257750200e77SFrank Van Der Linden 	immu_flags = dma_to_immu_flags(dmareq);
25783a634bfcSVikram Hegde 
257950200e77SFrank Van Der Linden 	attrp = &((ddi_dma_impl_t *)handle)->dmai_attr;
25803a634bfcSVikram Hegde 
258150200e77SFrank Van Der Linden 	dmar_object = &dmareq->dmar_object;
25823a634bfcSVikram Hegde 	pparray = dmar_object->dmao_obj.virt_obj.v_priv;
25833a634bfcSVikram Hegde 	vaddr = dmar_object->dmao_obj.virt_obj.v_addr;
25843a634bfcSVikram Hegde 	buftype = dmar_object->dmao_type;
25853a634bfcSVikram Hegde 	size = dmar_object->dmao_size;
25863a634bfcSVikram Hegde 
258750200e77SFrank Van Der Linden 	IMMU_DPROBE3(immu__map__dvma, dev_info_t *, rdip, ddi_dma_atyp_t,
258850200e77SFrank Van Der Linden 	    buftype, uint_t, size);
25893a634bfcSVikram Hegde 
259050200e77SFrank Van Der Linden 	dcookies = &ihp->ihp_dcookies[0];
259150200e77SFrank Van Der Linden 
259250200e77SFrank Van Der Linden 	pcnt = dmax = 0;
25933a634bfcSVikram Hegde 
25943a634bfcSVikram Hegde 	/* retrieve paddr, psize, offset from dmareq */
25953a634bfcSVikram Hegde 	if (buftype == DMA_OTYP_PAGES) {
25963a634bfcSVikram Hegde 		page = dmar_object->dmao_obj.pp_obj.pp_pp;
25973a634bfcSVikram Hegde 		offset =  dmar_object->dmao_obj.pp_obj.pp_offset &
25983a634bfcSVikram Hegde 		    MMU_PAGEOFFSET;
25993a634bfcSVikram Hegde 		paddr = pfn_to_pa(page->p_pagenum) + offset;
26003a634bfcSVikram Hegde 		psize = MIN((MMU_PAGESIZE - offset), size);
26013a634bfcSVikram Hegde 		page = page->p_next;
260250200e77SFrank Van Der Linden 		vas = dmar_object->dmao_obj.virt_obj.v_as;
26033a634bfcSVikram Hegde 	} else {
260450200e77SFrank Van Der Linden 		if (vas == NULL) {
260550200e77SFrank Van Der Linden 			vas = &kas;
26063a634bfcSVikram Hegde 		}
26073a634bfcSVikram Hegde 		offset = (uintptr_t)vaddr & MMU_PAGEOFFSET;
26083a634bfcSVikram Hegde 		if (pparray != NULL) {
26093a634bfcSVikram Hegde 			paddr = pfn_to_pa(pparray[pcnt]->p_pagenum) + offset;
26103a634bfcSVikram Hegde 			psize = MIN((MMU_PAGESIZE - offset), size);
26113a634bfcSVikram Hegde 			pcnt++;
26123a634bfcSVikram Hegde 		} else {
261350200e77SFrank Van Der Linden 			paddr = pfn_to_pa(hat_getpfnum(vas->a_hat,
26143a634bfcSVikram Hegde 			    vaddr)) + offset;
26153a634bfcSVikram Hegde 			psize = MIN(size, (MMU_PAGESIZE - offset));
26163a634bfcSVikram Hegde 			vaddr += psize;
26173a634bfcSVikram Hegde 		}
26183a634bfcSVikram Hegde 	}
26193a634bfcSVikram Hegde 
262050200e77SFrank Van Der Linden 	npgalloc = IMMU_BTOPR(size + offset);
26213a634bfcSVikram Hegde 
262250200e77SFrank Van Der Linden 	if (npgalloc <= IMMU_NPREPTES && ihp->ihp_predvma != 0) {
262350200e77SFrank Van Der Linden #ifdef BUGGY_DRIVERS
262450200e77SFrank Van Der Linden 		rwmask = PDTE_MASK_R | PDTE_MASK_W | immu->immu_ptemask;
262550200e77SFrank Van Der Linden #else
262650200e77SFrank Van Der Linden 		rwmask = immu->immu_ptemask;
262750200e77SFrank Van Der Linden 		if (immu_flags & IMMU_FLAGS_READ)
262850200e77SFrank Van Der Linden 			rwmask |= PDTE_MASK_R;
262950200e77SFrank Van Der Linden 		if (immu_flags & IMMU_FLAGS_WRITE)
263050200e77SFrank Van Der Linden 			rwmask |= PDTE_MASK_W;
263150200e77SFrank Van Der Linden #endif
263250200e77SFrank Van Der Linden #ifdef DEBUG
263350200e77SFrank Van Der Linden 		rwmask |= PDTE_MASK_P;
263450200e77SFrank Van Der Linden #endif
263550200e77SFrank Van Der Linden 		sdvma = ihp->ihp_predvma;
263650200e77SFrank Van Der Linden 		ihp->ihp_npremapped = npgalloc;
263750200e77SFrank Van Der Linden 		*ihp->ihp_preptes[0] =
263850200e77SFrank Van Der Linden 		    PDTE_PADDR(paddr & ~MMU_PAGEOFFSET) | rwmask;
263950200e77SFrank Van Der Linden 	} else {
264050200e77SFrank Van Der Linden 		ihp->ihp_npremapped = 0;
264150200e77SFrank Van Der Linden 		sdvma = dvma_alloc(domain, attrp, npgalloc,
264250200e77SFrank Van Der Linden 		    dmareq->dmar_fp == DDI_DMA_SLEEP ? VM_SLEEP : VM_NOSLEEP);
264350200e77SFrank Van Der Linden 		if (sdvma == 0)
264450200e77SFrank Van Der Linden 			return (DDI_DMA_NORESOURCES);
26453a634bfcSVikram Hegde 
264650200e77SFrank Van Der Linden 		dcookies[0].dck_paddr = (paddr & ~MMU_PAGEOFFSET);
264750200e77SFrank Van Der Linden 		dcookies[0].dck_npages = 1;
264850200e77SFrank Van Der Linden 	}
264950200e77SFrank Van Der Linden 
265050200e77SFrank Van Der Linden 	IMMU_DPROBE3(immu__dvma__alloc, dev_info_t *, rdip, uint64_t, npgalloc,
265150200e77SFrank Van Der Linden 	    uint64_t, sdvma);
265250200e77SFrank Van Der Linden 
265350200e77SFrank Van Der Linden 	dvma = sdvma;
265450200e77SFrank Van Der Linden 	pde_set = 0;
265550200e77SFrank Van Der Linden 	npages = 1;
26563a634bfcSVikram Hegde 	size -= psize;
26573a634bfcSVikram Hegde 	while (size > 0) {
26583a634bfcSVikram Hegde 		/* get the size for this page (i.e. partial or full page) */
26593a634bfcSVikram Hegde 		psize = MIN(size, MMU_PAGESIZE);
26603a634bfcSVikram Hegde 		if (buftype == DMA_OTYP_PAGES) {
26613a634bfcSVikram Hegde 			/* get the paddr from the page_t */
26623a634bfcSVikram Hegde 			paddr = pfn_to_pa(page->p_pagenum);
26633a634bfcSVikram Hegde 			page = page->p_next;
26643a634bfcSVikram Hegde 		} else if (pparray != NULL) {
26653a634bfcSVikram Hegde 			/* index into the array of page_t's to get the paddr */
26663a634bfcSVikram Hegde 			paddr = pfn_to_pa(pparray[pcnt]->p_pagenum);
26673a634bfcSVikram Hegde 			pcnt++;
26683a634bfcSVikram Hegde 		} else {
26693a634bfcSVikram Hegde 			/* call into the VM to get the paddr */
267050200e77SFrank Van Der Linden 			paddr = pfn_to_pa(hat_getpfnum(vas->a_hat, vaddr));
26713a634bfcSVikram Hegde 			vaddr += psize;
26723a634bfcSVikram Hegde 		}
267350200e77SFrank Van Der Linden 
267450200e77SFrank Van Der Linden 		if (ihp->ihp_npremapped > 0) {
26752cedd8f0SHans Rosenfeld 			*ihp->ihp_preptes[npages] =
267650200e77SFrank Van Der Linden 			    PDTE_PADDR(paddr) | rwmask;
267750200e77SFrank Van Der Linden 		} else if (IMMU_CONTIG_PADDR(dcookies[dmax], paddr)) {
267850200e77SFrank Van Der Linden 			dcookies[dmax].dck_npages++;
267950200e77SFrank Van Der Linden 		} else {
268050200e77SFrank Van Der Linden 			/* No, we need a new dcookie */
268150200e77SFrank Van Der Linden 			if (dmax == (IMMU_NDCK - 1)) {
268250200e77SFrank Van Der Linden 				/*
268350200e77SFrank Van Der Linden 				 * Ran out of dcookies. Map them now.
268450200e77SFrank Van Der Linden 				 */
268550200e77SFrank Van Der Linden 				if (dvma_map(domain, dvma,
268650200e77SFrank Van Der Linden 				    npages, dcookies, dmax + 1, rdip,
268750200e77SFrank Van Der Linden 				    immu_flags))
268850200e77SFrank Van Der Linden 					pde_set++;
268950200e77SFrank Van Der Linden 
269050200e77SFrank Van Der Linden 				IMMU_DPROBE4(immu__dvmamap__early,
269150200e77SFrank Van Der Linden 				    dev_info_t *, rdip, uint64_t, dvma,
269250200e77SFrank Van Der Linden 				    uint_t, npages, uint_t, dmax+1);
269350200e77SFrank Van Der Linden 
269450200e77SFrank Van Der Linden 				dvma += (npages << IMMU_PAGESHIFT);
269550200e77SFrank Van Der Linden 				npages = 0;
269650200e77SFrank Van Der Linden 				dmax = 0;
26972cedd8f0SHans Rosenfeld 			} else {
269850200e77SFrank Van Der Linden 				dmax++;
26992cedd8f0SHans Rosenfeld 			}
270050200e77SFrank Van Der Linden 			dcookies[dmax].dck_paddr = paddr;
270150200e77SFrank Van Der Linden 			dcookies[dmax].dck_npages = 1;
270250200e77SFrank Van Der Linden 		}
27033a634bfcSVikram Hegde 		size -= psize;
27042cedd8f0SHans Rosenfeld 		if (npages != 0)
27052cedd8f0SHans Rosenfeld 			npages++;
27063a634bfcSVikram Hegde 	}
27073a634bfcSVikram Hegde 
270850200e77SFrank Van Der Linden 	/*
270950200e77SFrank Van Der Linden 	 * Finish up, mapping all, or all of the remaining,
271050200e77SFrank Van Der Linden 	 * physical memory ranges.
271150200e77SFrank Van Der Linden 	 */
271250200e77SFrank Van Der Linden 	if (ihp->ihp_npremapped == 0 && npages > 0) {
271350200e77SFrank Van Der Linden 		IMMU_DPROBE4(immu__dvmamap__late, dev_info_t *, rdip, \
271450200e77SFrank Van Der Linden 		    uint64_t, dvma, uint_t, npages, uint_t, dmax+1);
271550200e77SFrank Van Der Linden 
271650200e77SFrank Van Der Linden 		if (dvma_map(domain, dvma, npages, dcookies,
271750200e77SFrank Van Der Linden 		    dmax + 1, rdip, immu_flags))
271850200e77SFrank Van Der Linden 			pde_set++;
271950200e77SFrank Van Der Linden 	}
272050200e77SFrank Van Der Linden 
272150200e77SFrank Van Der Linden 	/* Invalidate the IOTLB */
272250200e77SFrank Van Der Linden 	immu_flush_iotlb_psi(immu, domain->dom_did, sdvma, npgalloc,
272350200e77SFrank Van Der Linden 	    pde_set > 0 ? TLB_IVA_WHOLE : TLB_IVA_LEAF,
272450200e77SFrank Van Der Linden 	    &ihp->ihp_inv_wait);
272550200e77SFrank Van Der Linden 
272650200e77SFrank Van Der Linden 	ihp->ihp_ndvseg = 1;
272750200e77SFrank Van Der Linden 	ihp->ihp_dvseg[0].dvs_start = sdvma;
272850200e77SFrank Van Der Linden 	ihp->ihp_dvseg[0].dvs_len = dmar_object->dmao_size;
27293a634bfcSVikram Hegde 
273050200e77SFrank Van Der Linden 	dma_out->dmao_size = dmar_object->dmao_size;
273150200e77SFrank Van Der Linden 	dma_out->dmao_obj.dvma_obj.dv_off = offset & IMMU_PAGEOFFSET;
273250200e77SFrank Van Der Linden 	dma_out->dmao_obj.dvma_obj.dv_nseg = 1;
273350200e77SFrank Van Der Linden 	dma_out->dmao_obj.dvma_obj.dv_seg = &ihp->ihp_dvseg[0];
273450200e77SFrank Van Der Linden 	dma_out->dmao_type = DMA_OTYP_DVADDR;
273550200e77SFrank Van Der Linden 
273650200e77SFrank Van Der Linden 	return (DDI_DMA_MAPPED);
273750200e77SFrank Van Der Linden }
273850200e77SFrank Van Der Linden 
273950200e77SFrank Van Der Linden static int
immu_unmap_dvmaseg(dev_info_t * rdip,ddi_dma_obj_t * dmao)274050200e77SFrank Van Der Linden immu_unmap_dvmaseg(dev_info_t *rdip, ddi_dma_obj_t *dmao)
274150200e77SFrank Van Der Linden {
274250200e77SFrank Van Der Linden 	uint64_t dvma, npages;
274350200e77SFrank Van Der Linden 	domain_t *domain;
274450200e77SFrank Van Der Linden 	struct dvmaseg *dvs;
27453a634bfcSVikram Hegde 
274650200e77SFrank Van Der Linden 	domain = IMMU_DEVI(rdip)->imd_domain;
274750200e77SFrank Van Der Linden 	dvs = dmao->dmao_obj.dvma_obj.dv_seg;
274850200e77SFrank Van Der Linden 
274950200e77SFrank Van Der Linden 	dvma = dvs[0].dvs_start;
275050200e77SFrank Van Der Linden 	npages = IMMU_BTOPR(dvs[0].dvs_len + dmao->dmao_obj.dvma_obj.dv_off);
275150200e77SFrank Van Der Linden 
275250200e77SFrank Van Der Linden #ifdef DEBUG
275350200e77SFrank Van Der Linden 	/* Unmap only in DEBUG mode */
275450200e77SFrank Van Der Linden 	dvma_unmap(domain, dvma, npages, rdip);
275550200e77SFrank Van Der Linden #endif
275650200e77SFrank Van Der Linden 	dvma_free(domain, dvma, npages);
275750200e77SFrank Van Der Linden 
275850200e77SFrank Van Der Linden 	IMMU_DPROBE3(immu__dvma__free, dev_info_t *, rdip, uint_t, npages,
275950200e77SFrank Van Der Linden 	    uint64_t, dvma);
276050200e77SFrank Van Der Linden 
276150200e77SFrank Van Der Linden #ifdef DEBUG
276250200e77SFrank Van Der Linden 	/*
276350200e77SFrank Van Der Linden 	 * In the DEBUG case, the unmap was actually done,
276450200e77SFrank Van Der Linden 	 * but an IOTLB flush was not done. So, an explicit
276550200e77SFrank Van Der Linden 	 * write back flush is needed.
276650200e77SFrank Van Der Linden 	 */
276750200e77SFrank Van Der Linden 	immu_regs_wbf_flush(domain->dom_immu);
276850200e77SFrank Van Der Linden #endif
27693a634bfcSVikram Hegde 
27703a634bfcSVikram Hegde 	return (DDI_SUCCESS);
27713a634bfcSVikram Hegde }
27723a634bfcSVikram Hegde 
27733a634bfcSVikram Hegde /* ############################# Functions exported ######################## */
27743a634bfcSVikram Hegde 
27753a634bfcSVikram Hegde /*
27763a634bfcSVikram Hegde  * setup the DVMA subsystem
27773a634bfcSVikram Hegde  * this code runs only for the first IOMMU unit
27783a634bfcSVikram Hegde  */
27793a634bfcSVikram Hegde void
immu_dvma_setup(list_t * listp)27803a634bfcSVikram Hegde immu_dvma_setup(list_t *listp)
27813a634bfcSVikram Hegde {
27823a634bfcSVikram Hegde 	immu_t *immu;
27833a634bfcSVikram Hegde 	uint_t kval;
27843a634bfcSVikram Hegde 	size_t nchains;
27853a634bfcSVikram Hegde 
27863a634bfcSVikram Hegde 	/* locks */
27873a634bfcSVikram Hegde 	mutex_init(&immu_domain_lock, NULL, MUTEX_DEFAULT, NULL);
27883a634bfcSVikram Hegde 
27893a634bfcSVikram Hegde 	/* Create lists */
27903a634bfcSVikram Hegde 	list_create(&immu_unity_domain_list, sizeof (domain_t),
27913a634bfcSVikram Hegde 	    offsetof(domain_t, dom_maptype_node));
27923a634bfcSVikram Hegde 	list_create(&immu_xlate_domain_list, sizeof (domain_t),
27933a634bfcSVikram Hegde 	    offsetof(domain_t, dom_maptype_node));
27943a634bfcSVikram Hegde 
27953a634bfcSVikram Hegde 	/* Setup BDF domain hash */
27963a634bfcSVikram Hegde 	nchains = 0xff;
27973a634bfcSVikram Hegde 	kval = mod_hash_iddata_gen(nchains);
27983a634bfcSVikram Hegde 
27993a634bfcSVikram Hegde 	bdf_domain_hash = mod_hash_create_extended("BDF-DOMAIN_HASH",
28003a634bfcSVikram Hegde 	    nchains, mod_hash_null_keydtor, mod_hash_null_valdtor,
28013a634bfcSVikram Hegde 	    mod_hash_byid, (void *)(uintptr_t)kval, mod_hash_idkey_cmp,
28023a634bfcSVikram Hegde 	    KM_NOSLEEP);
28033a634bfcSVikram Hegde 
28043a634bfcSVikram Hegde 	immu = list_head(listp);
28053a634bfcSVikram Hegde 	for (; immu; immu = list_next(listp, immu)) {
28063a634bfcSVikram Hegde 		create_unity_domain(immu);
28073a634bfcSVikram Hegde 		did_init(immu);
28083a634bfcSVikram Hegde 		context_init(immu);
28093a634bfcSVikram Hegde 		immu->immu_dvma_setup = B_TRUE;
28103a634bfcSVikram Hegde 	}
28113a634bfcSVikram Hegde }
28123a634bfcSVikram Hegde 
28133a634bfcSVikram Hegde /*
28143a634bfcSVikram Hegde  * Startup up one DVMA unit
28153a634bfcSVikram Hegde  */
28163a634bfcSVikram Hegde void
immu_dvma_startup(immu_t * immu)28173a634bfcSVikram Hegde immu_dvma_startup(immu_t *immu)
28183a634bfcSVikram Hegde {
28193a634bfcSVikram Hegde 	if (immu_gfxdvma_enable == B_FALSE &&
28203a634bfcSVikram Hegde 	    immu->immu_dvma_gfx_only == B_TRUE) {
28213a634bfcSVikram Hegde 		return;
28223a634bfcSVikram Hegde 	}
28233a634bfcSVikram Hegde 
28243a634bfcSVikram Hegde 	/*
28253a634bfcSVikram Hegde 	 * DVMA will start once IOMMU is "running"
28263a634bfcSVikram Hegde 	 */
28273a634bfcSVikram Hegde 	immu->immu_dvma_running = B_TRUE;
28283a634bfcSVikram Hegde }
28293a634bfcSVikram Hegde 
28303a634bfcSVikram Hegde /*
28313a634bfcSVikram Hegde  * immu_dvma_physmem_update()
28323a634bfcSVikram Hegde  *       called when the installed memory on a
28333a634bfcSVikram Hegde  *       system increases, to expand domain DVMA
28343a634bfcSVikram Hegde  *       for domains with UNITY mapping
28353a634bfcSVikram Hegde  */
28363a634bfcSVikram Hegde void
immu_dvma_physmem_update(uint64_t addr,uint64_t size)28373a634bfcSVikram Hegde immu_dvma_physmem_update(uint64_t addr, uint64_t size)
28383a634bfcSVikram Hegde {
28393a634bfcSVikram Hegde 	uint64_t start;
28403a634bfcSVikram Hegde 	uint64_t npages;
2841e03dceedSVikram Hegde 	int dcount;
284250200e77SFrank Van Der Linden 	immu_dcookie_t dcookies[1] = {0};
28433a634bfcSVikram Hegde 	domain_t *domain;
28443a634bfcSVikram Hegde 
28453a634bfcSVikram Hegde 	/*
28463a634bfcSVikram Hegde 	 * Just walk the system-wide list of domains with
28473a634bfcSVikram Hegde 	 * UNITY mapping. Both the list of *all* domains
28483a634bfcSVikram Hegde 	 * and *UNITY* domains is protected by the same
28493a634bfcSVikram Hegde 	 * single lock
28503a634bfcSVikram Hegde 	 */
28513a634bfcSVikram Hegde 	mutex_enter(&immu_domain_lock);
28523a634bfcSVikram Hegde 	domain = list_head(&immu_unity_domain_list);
28533a634bfcSVikram Hegde 	for (; domain; domain = list_next(&immu_unity_domain_list, domain)) {
2854be56ae36SFrank Van Der Linden 		/*
2855be56ae36SFrank Van Der Linden 		 * Nothing to do if the IOMMU supports passthrough.
2856be56ae36SFrank Van Der Linden 		 */
2857be56ae36SFrank Van Der Linden 		if (IMMU_ECAP_GET_PT(domain->dom_immu->immu_regs_excap))
2858be56ae36SFrank Van Der Linden 			continue;
28593a634bfcSVikram Hegde 
28603a634bfcSVikram Hegde 		/* There is no vmem_arena for unity domains. Just map it */
286150200e77SFrank Van Der Linden 		ddi_err(DER_LOG, domain->dom_dip,
286250200e77SFrank Van Der Linden 		    "iommu: unity-domain: Adding map "
28633a634bfcSVikram Hegde 		    "[0x%" PRIx64 " - 0x%" PRIx64 "]", addr, addr + size);
28643a634bfcSVikram Hegde 
28653a634bfcSVikram Hegde 		start = IMMU_ROUNDOWN(addr);
28663a634bfcSVikram Hegde 		npages = (IMMU_ROUNDUP(size) / IMMU_PAGESIZE) + 1;
28673a634bfcSVikram Hegde 
2868e03dceedSVikram Hegde 		dcookies[0].dck_paddr = start;
2869e03dceedSVikram Hegde 		dcookies[0].dck_npages = npages;
2870e03dceedSVikram Hegde 		dcount = 1;
287150200e77SFrank Van Der Linden 		(void) dvma_map(domain, start, npages,
2872e03dceedSVikram Hegde 		    dcookies, dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
28733a634bfcSVikram Hegde 
28743a634bfcSVikram Hegde 	}
28753a634bfcSVikram Hegde 	mutex_exit(&immu_domain_lock);
28763a634bfcSVikram Hegde }
28773a634bfcSVikram Hegde 
28783a634bfcSVikram Hegde int
immu_dvma_device_setup(dev_info_t * rdip,immu_flags_t immu_flags)287950200e77SFrank Van Der Linden immu_dvma_device_setup(dev_info_t *rdip, immu_flags_t immu_flags)
28803a634bfcSVikram Hegde {
288150200e77SFrank Van Der Linden 	dev_info_t *ddip, *odip;
28823a634bfcSVikram Hegde 	immu_t *immu;
288350200e77SFrank Van Der Linden 	domain_t *domain;
28843a634bfcSVikram Hegde 
288550200e77SFrank Van Der Linden 	odip = rdip;
28863a634bfcSVikram Hegde 
2887e03dceedSVikram Hegde 	immu = immu_dvma_get_immu(rdip, immu_flags);
2888e03dceedSVikram Hegde 	if (immu == NULL) {
2889e03dceedSVikram Hegde 		/*
2890e03dceedSVikram Hegde 		 * possible that there is no IOMMU unit for this device
2891e03dceedSVikram Hegde 		 * - BIOS bugs are one example.
2892e03dceedSVikram Hegde 		 */
289350200e77SFrank Van Der Linden 		ddi_err(DER_WARN, rdip, "No iommu unit found for device");
2894e03dceedSVikram Hegde 		return (DDI_DMA_NORESOURCES);
2895e03dceedSVikram Hegde 	}
2896e03dceedSVikram Hegde 
2897e03dceedSVikram Hegde 	/*
2898e03dceedSVikram Hegde 	 * redirect isa devices attached under lpc to lpc dip
2899e03dceedSVikram Hegde 	 */
2900e03dceedSVikram Hegde 	if (strcmp(ddi_node_name(ddi_get_parent(rdip)), "isa") == 0) {
2901e03dceedSVikram Hegde 		rdip = get_lpc_devinfo(immu, rdip, immu_flags);
2902e03dceedSVikram Hegde 		if (rdip == NULL) {
290350200e77SFrank Van Der Linden 			ddi_err(DER_PANIC, rdip, "iommu redirect failed");
2904e03dceedSVikram Hegde 			/*NOTREACHED*/
2905e03dceedSVikram Hegde 		}
2906e03dceedSVikram Hegde 	}
2907e03dceedSVikram Hegde 
2908e03dceedSVikram Hegde 	/* Reset immu, as redirection can change IMMU */
2909e03dceedSVikram Hegde 	immu = NULL;
2910e03dceedSVikram Hegde 
2911e03dceedSVikram Hegde 	/*
2912e03dceedSVikram Hegde 	 * for gart, redirect to the real graphic devinfo
2913e03dceedSVikram Hegde 	 */
2914e03dceedSVikram Hegde 	if (strcmp(ddi_node_name(rdip), "agpgart") == 0) {
2915e03dceedSVikram Hegde 		rdip = get_gfx_devinfo(rdip);
2916e03dceedSVikram Hegde 		if (rdip == NULL) {
291750200e77SFrank Van Der Linden 			ddi_err(DER_PANIC, rdip, "iommu redirect failed");
2918e03dceedSVikram Hegde 			/*NOTREACHED*/
2919e03dceedSVikram Hegde 		}
2920e03dceedSVikram Hegde 	}
2921e03dceedSVikram Hegde 
29223a634bfcSVikram Hegde 	/*
29233a634bfcSVikram Hegde 	 * Setup DVMA domain for the device. This does
29243a634bfcSVikram Hegde 	 * work only the first time we do DVMA for a
29253a634bfcSVikram Hegde 	 * device.
29263a634bfcSVikram Hegde 	 */
29273a634bfcSVikram Hegde 	ddip = NULL;
29283a634bfcSVikram Hegde 	domain = device_domain(rdip, &ddip, immu_flags);
29293a634bfcSVikram Hegde 	if (domain == NULL) {
29303a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "Intel IOMMU setup failed for device");
29313a634bfcSVikram Hegde 		return (DDI_DMA_NORESOURCES);
29323a634bfcSVikram Hegde 	}
29333a634bfcSVikram Hegde 
293450200e77SFrank Van Der Linden 	immu = domain->dom_immu;
293550200e77SFrank Van Der Linden 
29363a634bfcSVikram Hegde 	/*
29373a634bfcSVikram Hegde 	 * If a domain is found, we must also have a domain dip
29383a634bfcSVikram Hegde 	 * which is the topmost ancestor dip of rdip that shares
29393a634bfcSVikram Hegde 	 * the same domain with rdip.
29403a634bfcSVikram Hegde 	 */
29413a634bfcSVikram Hegde 	if (domain->dom_did == 0 || ddip == NULL) {
29423a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "domain did 0(%d) or ddip NULL(%p)",
29433a634bfcSVikram Hegde 		    domain->dom_did, ddip);
29443a634bfcSVikram Hegde 		return (DDI_DMA_NORESOURCES);
29453a634bfcSVikram Hegde 	}
29463a634bfcSVikram Hegde 
294750200e77SFrank Van Der Linden 	if (odip != rdip)
294850200e77SFrank Van Der Linden 		set_domain(odip, ddip, domain);
29493a634bfcSVikram Hegde 
29503a634bfcSVikram Hegde 	/*
29513a634bfcSVikram Hegde 	 * Update the root and context entries
29523a634bfcSVikram Hegde 	 */
29533a634bfcSVikram Hegde 	if (immu_context_update(immu, domain, ddip, rdip, immu_flags)
29543a634bfcSVikram Hegde 	    != DDI_SUCCESS) {
29553a634bfcSVikram Hegde 		ddi_err(DER_MODE, rdip, "DVMA map: context update failed");
29563a634bfcSVikram Hegde 		return (DDI_DMA_NORESOURCES);
29573a634bfcSVikram Hegde 	}
29583a634bfcSVikram Hegde 
295950200e77SFrank Van Der Linden 	return (DDI_SUCCESS);
29603a634bfcSVikram Hegde }
29613a634bfcSVikram Hegde 
29623a634bfcSVikram Hegde int
immu_map_memrange(dev_info_t * rdip,memrng_t * mrng)296350200e77SFrank Van Der Linden immu_map_memrange(dev_info_t *rdip, memrng_t *mrng)
29643a634bfcSVikram Hegde {
296550200e77SFrank Van Der Linden 	immu_dcookie_t dcookies[1] = {0};
296650200e77SFrank Van Der Linden 	boolean_t pde_set;
29673a634bfcSVikram Hegde 	immu_t *immu;
296850200e77SFrank Van Der Linden 	domain_t *domain;
296950200e77SFrank Van Der Linden 	immu_inv_wait_t iw;
29703a634bfcSVikram Hegde 
297150200e77SFrank Van Der Linden 	dcookies[0].dck_paddr = mrng->mrng_start;
297250200e77SFrank Van Der Linden 	dcookies[0].dck_npages = mrng->mrng_npages;
29733a634bfcSVikram Hegde 
297450200e77SFrank Van Der Linden 	domain = IMMU_DEVI(rdip)->imd_domain;
297550200e77SFrank Van Der Linden 	immu = domain->dom_immu;
29763a634bfcSVikram Hegde 
297750200e77SFrank Van Der Linden 	pde_set = dvma_map(domain, mrng->mrng_start,
297850200e77SFrank Van Der Linden 	    mrng->mrng_npages, dcookies, 1, rdip,
297950200e77SFrank Van Der Linden 	    IMMU_FLAGS_READ | IMMU_FLAGS_WRITE);
29803a634bfcSVikram Hegde 
298150200e77SFrank Van Der Linden 	immu_init_inv_wait(&iw, "memrange", B_TRUE);
29823a634bfcSVikram Hegde 
298350200e77SFrank Van Der Linden 	immu_flush_iotlb_psi(immu, domain->dom_did, mrng->mrng_start,
298450200e77SFrank Van Der Linden 	    mrng->mrng_npages, pde_set == B_TRUE ?
298550200e77SFrank Van Der Linden 	    TLB_IVA_WHOLE : TLB_IVA_LEAF, &iw);
298650200e77SFrank Van Der Linden 
298750200e77SFrank Van Der Linden 	return (DDI_SUCCESS);
298850200e77SFrank Van Der Linden }
298950200e77SFrank Van Der Linden 
299050200e77SFrank Van Der Linden immu_devi_t *
immu_devi_get(dev_info_t * rdip)299150200e77SFrank Van Der Linden immu_devi_get(dev_info_t *rdip)
299250200e77SFrank Van Der Linden {
299350200e77SFrank Van Der Linden 	immu_devi_t *immu_devi;
299450200e77SFrank Van Der Linden 	volatile uintptr_t *vptr = (uintptr_t *)&(DEVI(rdip)->devi_iommu);
299550200e77SFrank Van Der Linden 
299650200e77SFrank Van Der Linden 	/* Just want atomic reads. No need for lock */
299750200e77SFrank Van Der Linden 	immu_devi = (immu_devi_t *)(uintptr_t)atomic_or_64_nv((uint64_t *)vptr,
299850200e77SFrank Van Der Linden 	    0);
299950200e77SFrank Van Der Linden 	return (immu_devi);
300050200e77SFrank Van Der Linden }
300150200e77SFrank Van Der Linden 
300250200e77SFrank Van Der Linden /*ARGSUSED*/
300350200e77SFrank Van Der Linden int
immu_hdl_priv_ctor(void * buf,void * arg,int kmf)300450200e77SFrank Van Der Linden immu_hdl_priv_ctor(void *buf, void *arg, int kmf)
300550200e77SFrank Van Der Linden {
300650200e77SFrank Van Der Linden 	immu_hdl_priv_t *ihp;
300750200e77SFrank Van Der Linden 
300850200e77SFrank Van Der Linden 	ihp = buf;
300950200e77SFrank Van Der Linden 	immu_init_inv_wait(&ihp->ihp_inv_wait, "dmahandle", B_FALSE);
301050200e77SFrank Van Der Linden 
301150200e77SFrank Van Der Linden 	return (0);
301250200e77SFrank Van Der Linden }
3013e03dceedSVikram Hegde 
301450200e77SFrank Van Der Linden /*
301550200e77SFrank Van Der Linden  * iommulib interface functions
301650200e77SFrank Van Der Linden  */
301750200e77SFrank Van Der Linden static int
immu_probe(iommulib_handle_t handle,dev_info_t * dip)301850200e77SFrank Van Der Linden immu_probe(iommulib_handle_t handle, dev_info_t *dip)
301950200e77SFrank Van Der Linden {
302050200e77SFrank Van Der Linden 	immu_devi_t *immu_devi;
302150200e77SFrank Van Der Linden 	int ret;
302250200e77SFrank Van Der Linden 
302350200e77SFrank Van Der Linden 	if (!immu_enable)
302450200e77SFrank Van Der Linden 		return (DDI_FAILURE);
3025e03dceedSVikram Hegde 
3026e03dceedSVikram Hegde 	/*
302750200e77SFrank Van Der Linden 	 * Make sure the device has all the IOMMU structures
302850200e77SFrank Van Der Linden 	 * initialized. If this device goes through an IOMMU
302950200e77SFrank Van Der Linden 	 * unit (e.g. this probe function returns success),
303050200e77SFrank Van Der Linden 	 * this will be called at most N times, with N being
303150200e77SFrank Van Der Linden 	 * the number of IOMMUs in the system.
303250200e77SFrank Van Der Linden 	 *
303350200e77SFrank Van Der Linden 	 * After that, when iommulib_nex_open succeeds,
303450200e77SFrank Van Der Linden 	 * we can always assume that this device has all
303550200e77SFrank Van Der Linden 	 * the structures initialized. IOMMU_USED(dip) will
303650200e77SFrank Van Der Linden 	 * be true. There is no need to find the controlling
303750200e77SFrank Van Der Linden 	 * IOMMU/domain again.
3038e03dceedSVikram Hegde 	 */
303950200e77SFrank Van Der Linden 	ret = immu_dvma_device_setup(dip, IMMU_FLAGS_NOSLEEP);
304050200e77SFrank Van Der Linden 	if (ret != DDI_SUCCESS)
304150200e77SFrank Van Der Linden 		return (ret);
3042e03dceedSVikram Hegde 
304350200e77SFrank Van Der Linden 	immu_devi = IMMU_DEVI(dip);
3044e03dceedSVikram Hegde 
3045e03dceedSVikram Hegde 	/*
304650200e77SFrank Van Der Linden 	 * For unity domains, there is no need to call in to
304750200e77SFrank Van Der Linden 	 * the IOMMU code.
3048e03dceedSVikram Hegde 	 */
304950200e77SFrank Van Der Linden 	if (immu_devi->imd_domain->dom_did == IMMU_UNITY_DID)
305050200e77SFrank Van Der Linden 		return (DDI_FAILURE);
305150200e77SFrank Van Der Linden 
305250200e77SFrank Van Der Linden 	if (immu_devi->imd_immu->immu_dip == iommulib_iommu_getdip(handle))
305350200e77SFrank Van Der Linden 		return (DDI_SUCCESS);
305450200e77SFrank Van Der Linden 
305550200e77SFrank Van Der Linden 	return (DDI_FAILURE);
305650200e77SFrank Van Der Linden }
305750200e77SFrank Van Der Linden 
305850200e77SFrank Van Der Linden /*ARGSUSED*/
305950200e77SFrank Van Der Linden static int
immu_allochdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_attr_t * attr,int (* waitfp)(caddr_t),caddr_t arg,ddi_dma_handle_t * dma_handlep)306050200e77SFrank Van Der Linden immu_allochdl(iommulib_handle_t handle,
306150200e77SFrank Van Der Linden     dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
306250200e77SFrank Van Der Linden     int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep)
306350200e77SFrank Van Der Linden {
306450200e77SFrank Van Der Linden 	int ret;
306550200e77SFrank Van Der Linden 	immu_hdl_priv_t *ihp;
306650200e77SFrank Van Der Linden 	immu_t *immu;
306750200e77SFrank Van Der Linden 
306850200e77SFrank Van Der Linden 	ret = iommulib_iommu_dma_allochdl(dip, rdip, attr, waitfp,
306950200e77SFrank Van Der Linden 	    arg, dma_handlep);
307050200e77SFrank Van Der Linden 	if (ret == DDI_SUCCESS) {
307150200e77SFrank Van Der Linden 		immu = IMMU_DEVI(rdip)->imd_immu;
307250200e77SFrank Van Der Linden 
307350200e77SFrank Van Der Linden 		ihp = kmem_cache_alloc(immu->immu_hdl_cache,
307450200e77SFrank Van Der Linden 		    waitfp == DDI_DMA_SLEEP ? KM_SLEEP : KM_NOSLEEP);
307550200e77SFrank Van Der Linden 		if (ihp == NULL) {
307650200e77SFrank Van Der Linden 			(void) iommulib_iommu_dma_freehdl(dip, rdip,
307750200e77SFrank Van Der Linden 			    *dma_handlep);
307850200e77SFrank Van Der Linden 			return (DDI_DMA_NORESOURCES);
3079e03dceedSVikram Hegde 		}
3080e03dceedSVikram Hegde 
308150200e77SFrank Van Der Linden 		if (IMMU_DEVI(rdip)->imd_use_premap)
308250200e77SFrank Van Der Linden 			dvma_prealloc(rdip, ihp, attr);
308350200e77SFrank Van Der Linden 		else {
308450200e77SFrank Van Der Linden 			ihp->ihp_npremapped = 0;
308550200e77SFrank Van Der Linden 			ihp->ihp_predvma = 0;
308650200e77SFrank Van Der Linden 		}
308750200e77SFrank Van Der Linden 		ret = iommulib_iommu_dmahdl_setprivate(dip, rdip, *dma_handlep,
308850200e77SFrank Van Der Linden 		    ihp);
30893a634bfcSVikram Hegde 	}
309050200e77SFrank Van Der Linden 	return (ret);
309150200e77SFrank Van Der Linden }
30923a634bfcSVikram Hegde 
309350200e77SFrank Van Der Linden /*ARGSUSED*/
309450200e77SFrank Van Der Linden static int
immu_freehdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle)309550200e77SFrank Van Der Linden immu_freehdl(iommulib_handle_t handle,
309650200e77SFrank Van Der Linden     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle)
309750200e77SFrank Van Der Linden {
309850200e77SFrank Van Der Linden 	immu_hdl_priv_t *ihp;
309950200e77SFrank Van Der Linden 
310050200e77SFrank Van Der Linden 	ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle);
310150200e77SFrank Van Der Linden 	if (ihp != NULL) {
310250200e77SFrank Van Der Linden 		if (IMMU_DEVI(rdip)->imd_use_premap)
310350200e77SFrank Van Der Linden 			dvma_prefree(rdip, ihp);
310450200e77SFrank Van Der Linden 		kmem_cache_free(IMMU_DEVI(rdip)->imd_immu->immu_hdl_cache, ihp);
31053a634bfcSVikram Hegde 	}
31063a634bfcSVikram Hegde 
310750200e77SFrank Van Der Linden 	return (iommulib_iommu_dma_freehdl(dip, rdip, dma_handle));
310850200e77SFrank Van Der Linden }
310950200e77SFrank Van Der Linden 
311050200e77SFrank Van Der Linden 
311150200e77SFrank Van Der Linden /*ARGSUSED*/
311250200e77SFrank Van Der Linden static int
immu_bindhdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,struct ddi_dma_req * dma_req,ddi_dma_cookie_t * cookiep,uint_t * ccountp)311350200e77SFrank Van Der Linden immu_bindhdl(iommulib_handle_t handle, dev_info_t *dip,
311450200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
311550200e77SFrank Van Der Linden     struct ddi_dma_req *dma_req, ddi_dma_cookie_t *cookiep,
311650200e77SFrank Van Der Linden     uint_t *ccountp)
311750200e77SFrank Van Der Linden {
311850200e77SFrank Van Der Linden 	int ret;
311950200e77SFrank Van Der Linden 	immu_hdl_priv_t *ihp;
312050200e77SFrank Van Der Linden 
312150200e77SFrank Van Der Linden 	ret = iommulib_iommu_dma_bindhdl(dip, rdip, dma_handle,
312250200e77SFrank Van Der Linden 	    dma_req, cookiep, ccountp);
312350200e77SFrank Van Der Linden 
312450200e77SFrank Van Der Linden 	if (ret == DDI_DMA_MAPPED) {
312550200e77SFrank Van Der Linden 		ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle);
312650200e77SFrank Van Der Linden 		immu_flush_wait(IMMU_DEVI(rdip)->imd_immu, &ihp->ihp_inv_wait);
31273a634bfcSVikram Hegde 	}
31283a634bfcSVikram Hegde 
312950200e77SFrank Van Der Linden 	return (ret);
313050200e77SFrank Van Der Linden }
31313a634bfcSVikram Hegde 
313250200e77SFrank Van Der Linden /*ARGSUSED*/
313350200e77SFrank Van Der Linden static int
immu_unbindhdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle)313450200e77SFrank Van Der Linden immu_unbindhdl(iommulib_handle_t handle,
313550200e77SFrank Van Der Linden     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle)
313650200e77SFrank Van Der Linden {
313750200e77SFrank Van Der Linden 	return (iommulib_iommu_dma_unbindhdl(dip, rdip, dma_handle));
313850200e77SFrank Van Der Linden }
31393a634bfcSVikram Hegde 
314050200e77SFrank Van Der Linden /*ARGSUSED*/
314150200e77SFrank Van Der Linden static int
immu_sync(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,off_t off,size_t len,uint_t cachefl)314250200e77SFrank Van Der Linden immu_sync(iommulib_handle_t handle, dev_info_t *dip,
314350200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off,
314450200e77SFrank Van Der Linden     size_t len, uint_t cachefl)
314550200e77SFrank Van Der Linden {
314650200e77SFrank Van Der Linden 	return (iommulib_iommu_dma_sync(dip, rdip, dma_handle, off, len,
314750200e77SFrank Van Der Linden 	    cachefl));
31483a634bfcSVikram Hegde }
31493a634bfcSVikram Hegde 
315050200e77SFrank Van Der Linden /*ARGSUSED*/
315150200e77SFrank Van Der Linden static int
immu_win(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,uint_t win,off_t * offp,size_t * lenp,ddi_dma_cookie_t * cookiep,uint_t * ccountp)315250200e77SFrank Van Der Linden immu_win(iommulib_handle_t handle, dev_info_t *dip,
315350200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win,
315450200e77SFrank Van Der Linden     off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep,
315550200e77SFrank Van Der Linden     uint_t *ccountp)
31563a634bfcSVikram Hegde {
315750200e77SFrank Van Der Linden 	return (iommulib_iommu_dma_win(dip, rdip, dma_handle, win, offp,
315850200e77SFrank Van Der Linden 	    lenp, cookiep, ccountp));
315950200e77SFrank Van Der Linden }
31603a634bfcSVikram Hegde 
316150200e77SFrank Van Der Linden /*ARGSUSED*/
316250200e77SFrank Van Der Linden static int
immu_mapobject(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,struct ddi_dma_req * dmareq,ddi_dma_obj_t * dmao)316350200e77SFrank Van Der Linden immu_mapobject(iommulib_handle_t handle, dev_info_t *dip,
316450200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
316550200e77SFrank Van Der Linden     struct ddi_dma_req *dmareq, ddi_dma_obj_t *dmao)
316650200e77SFrank Van Der Linden {
316750200e77SFrank Van Der Linden 	immu_hdl_priv_t *ihp;
316850200e77SFrank Van Der Linden 
316950200e77SFrank Van Der Linden 	ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle);
317050200e77SFrank Van Der Linden 
317150200e77SFrank Van Der Linden 	return (immu_map_dvmaseg(rdip, dma_handle, ihp, dmareq, dmao));
317250200e77SFrank Van Der Linden }
317350200e77SFrank Van Der Linden 
317450200e77SFrank Van Der Linden /*ARGSUSED*/
317550200e77SFrank Van Der Linden static int
immu_unmapobject(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,ddi_dma_obj_t * dmao)317650200e77SFrank Van Der Linden immu_unmapobject(iommulib_handle_t handle, dev_info_t *dip,
317750200e77SFrank Van Der Linden     dev_info_t *rdip, ddi_dma_handle_t dma_handle, ddi_dma_obj_t *dmao)
317850200e77SFrank Van Der Linden {
317950200e77SFrank Van Der Linden 	immu_hdl_priv_t *ihp;
318050200e77SFrank Van Der Linden 
318150200e77SFrank Van Der Linden 	ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle);
318250200e77SFrank Van Der Linden 	if (ihp->ihp_npremapped > 0)
318350200e77SFrank Van Der Linden 		return (DDI_SUCCESS);
318450200e77SFrank Van Der Linden 	return (immu_unmap_dvmaseg(rdip, dmao));
318550200e77SFrank Van Der Linden }
3186