xref: /titanic_53/usr/src/uts/i86pc/io/immu.c (revision 3adb2334459b30a605b8d375be6c3f3a3ec9ef38)
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 /*
223a634bfcSVikram Hegde  * Portions Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
233a634bfcSVikram Hegde  * Use is subject to license terms.
243a634bfcSVikram Hegde  */
253a634bfcSVikram Hegde /*
263a634bfcSVikram Hegde  * Copyright (c) 2009, Intel Corporation.
273a634bfcSVikram Hegde  * All rights reserved.
283a634bfcSVikram Hegde  */
293a634bfcSVikram Hegde 
303a634bfcSVikram Hegde /*
313a634bfcSVikram Hegde  * Intel IOMMU implementation
323a634bfcSVikram Hegde  * This file contains Intel IOMMU code exported
333a634bfcSVikram Hegde  * to the rest of the system and code that deals
343a634bfcSVikram Hegde  * with the Intel IOMMU as a whole.
353a634bfcSVikram Hegde  */
363a634bfcSVikram Hegde 
373a634bfcSVikram Hegde #include <sys/conf.h>
383a634bfcSVikram Hegde #include <sys/modctl.h>
393a634bfcSVikram Hegde #include <sys/pci.h>
403a634bfcSVikram Hegde #include <sys/pci_impl.h>
413a634bfcSVikram Hegde #include <sys/sysmacros.h>
423a634bfcSVikram Hegde #include <sys/ddi.h>
433a634bfcSVikram Hegde #include <sys/ddidmareq.h>
443a634bfcSVikram Hegde #include <sys/ddi_impldefs.h>
453a634bfcSVikram Hegde #include <sys/ddifm.h>
463a634bfcSVikram Hegde #include <sys/sunndi.h>
473a634bfcSVikram Hegde #include <sys/debug.h>
483a634bfcSVikram Hegde #include <sys/fm/protocol.h>
493a634bfcSVikram Hegde #include <sys/note.h>
503a634bfcSVikram Hegde #include <sys/apic.h>
513a634bfcSVikram Hegde #include <vm/hat_i86.h>
523a634bfcSVikram Hegde #include <sys/smp_impldefs.h>
533a634bfcSVikram Hegde #include <sys/spl.h>
543a634bfcSVikram Hegde #include <sys/archsystm.h>
553a634bfcSVikram Hegde #include <sys/x86_archext.h>
563a634bfcSVikram Hegde #include <sys/rootnex.h>
573a634bfcSVikram Hegde #include <sys/avl.h>
583a634bfcSVikram Hegde #include <sys/bootconf.h>
593a634bfcSVikram Hegde #include <sys/bootinfo.h>
603a634bfcSVikram Hegde #include <sys/atomic.h>
613a634bfcSVikram Hegde #include <sys/immu.h>
623a634bfcSVikram Hegde /* ########################### Globals and tunables ######################## */
633a634bfcSVikram Hegde /*
643a634bfcSVikram Hegde  * Global switches (boolean) that can be toggled either via boot options
653a634bfcSVikram Hegde  * or via /etc/system or kmdb
663a634bfcSVikram Hegde  */
673a634bfcSVikram Hegde 
683a634bfcSVikram Hegde /* Various features */
69*3adb2334SVikram Hegde boolean_t immu_enable = B_TRUE;
703a634bfcSVikram Hegde boolean_t immu_dvma_enable = B_TRUE;
713a634bfcSVikram Hegde 
723a634bfcSVikram Hegde /* accessed in other files so not static */
733a634bfcSVikram Hegde boolean_t immu_gfxdvma_enable = B_TRUE;
743a634bfcSVikram Hegde boolean_t immu_intrmap_enable = B_FALSE;
753a634bfcSVikram Hegde boolean_t immu_qinv_enable = B_FALSE;
763a634bfcSVikram Hegde 
773a634bfcSVikram Hegde /* various quirks that need working around */
783a634bfcSVikram Hegde 
793a634bfcSVikram Hegde /* XXX We always map page 0 read/write for now */
803a634bfcSVikram Hegde boolean_t immu_quirk_usbpage0 = B_TRUE;
813a634bfcSVikram Hegde boolean_t immu_quirk_usbrmrr = B_TRUE;
823a634bfcSVikram Hegde boolean_t immu_quirk_usbfullpa;
833a634bfcSVikram Hegde boolean_t immu_quirk_mobile4;
843a634bfcSVikram Hegde 
853a634bfcSVikram Hegde /* debug messages */
863a634bfcSVikram Hegde boolean_t immu_dmar_print;
873a634bfcSVikram Hegde 
88e03dceedSVikram Hegde /* Tunables */
89e03dceedSVikram Hegde int64_t immu_flush_gran = 5;
90e03dceedSVikram Hegde 
913a634bfcSVikram Hegde /* ############  END OPTIONS section ################ */
923a634bfcSVikram Hegde 
933a634bfcSVikram Hegde /*
943a634bfcSVikram Hegde  * Global used internally by Intel IOMMU code
953a634bfcSVikram Hegde  */
963a634bfcSVikram Hegde dev_info_t *root_devinfo;
973a634bfcSVikram Hegde kmutex_t immu_lock;
983a634bfcSVikram Hegde list_t immu_list;
99e03dceedSVikram Hegde void *immu_pgtable_cache;
1003a634bfcSVikram Hegde boolean_t immu_setup;
1013a634bfcSVikram Hegde boolean_t immu_running;
1023a634bfcSVikram Hegde boolean_t immu_quiesced;
1033a634bfcSVikram Hegde 
1043a634bfcSVikram Hegde /* ######################## END Globals and tunables ###################### */
1053a634bfcSVikram Hegde /* Globals used only in this file */
1063a634bfcSVikram Hegde static char **black_array;
1073a634bfcSVikram Hegde static uint_t nblacks;
1083a634bfcSVikram Hegde /* ###################### Utility routines ############################# */
1093a634bfcSVikram Hegde 
1103a634bfcSVikram Hegde /*
1113a634bfcSVikram Hegde  * Check if the device has mobile 4 chipset
1123a634bfcSVikram Hegde  */
1133a634bfcSVikram Hegde static int
1143a634bfcSVikram Hegde check_mobile4(dev_info_t *dip, void *arg)
1153a634bfcSVikram Hegde {
1163a634bfcSVikram Hegde 	_NOTE(ARGUNUSED(arg));
1173a634bfcSVikram Hegde 	int vendor, device;
1183a634bfcSVikram Hegde 	int *ip = (int *)arg;
1193a634bfcSVikram Hegde 
1203a634bfcSVikram Hegde 	ASSERT(arg);
1213a634bfcSVikram Hegde 
1223a634bfcSVikram Hegde 	vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1233a634bfcSVikram Hegde 	    "vendor-id", -1);
1243a634bfcSVikram Hegde 	device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1253a634bfcSVikram Hegde 	    "device-id", -1);
1263a634bfcSVikram Hegde 
1273a634bfcSVikram Hegde 	if (vendor == 0x8086 && device == 0x2a40) {
1283a634bfcSVikram Hegde 		*ip = B_TRUE;
1293a634bfcSVikram Hegde 		ddi_err(DER_NOTE, dip, "IMMU: Mobile 4 chipset detected. "
1303a634bfcSVikram Hegde 		    "Force setting IOMMU write buffer");
1313a634bfcSVikram Hegde 		return (DDI_WALK_TERMINATE);
1323a634bfcSVikram Hegde 	} else {
1333a634bfcSVikram Hegde 		return (DDI_WALK_CONTINUE);
1343a634bfcSVikram Hegde 	}
1353a634bfcSVikram Hegde }
1363a634bfcSVikram Hegde 
1373a634bfcSVikram Hegde static void
1383a634bfcSVikram Hegde map_bios_rsvd_mem(dev_info_t *dip)
1393a634bfcSVikram Hegde {
1403a634bfcSVikram Hegde 	struct memlist *mp;
1413a634bfcSVikram Hegde 	int e;
1423a634bfcSVikram Hegde 
1433a634bfcSVikram Hegde 	memlist_read_lock();
1443a634bfcSVikram Hegde 
1453a634bfcSVikram Hegde 	mp = bios_rsvd;
1463a634bfcSVikram Hegde 	while (mp != NULL) {
147e03dceedSVikram Hegde 		memrng_t mrng = {0};
1483a634bfcSVikram Hegde 
1493a634bfcSVikram Hegde 		ddi_err(DER_LOG, dip, "IMMU: Mapping BIOS rsvd range "
1503a634bfcSVikram Hegde 		    "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address,
1513a634bfcSVikram Hegde 		    mp->ml_address + mp->ml_size);
1523a634bfcSVikram Hegde 
153e03dceedSVikram Hegde 		mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address);
154e03dceedSVikram Hegde 		mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE;
1553a634bfcSVikram Hegde 
156e03dceedSVikram Hegde 		e = immu_dvma_map(NULL, NULL, &mrng, 0, dip, IMMU_FLAGS_MEMRNG);
1573a634bfcSVikram Hegde 		ASSERT(e == DDI_DMA_MAPPED || e == DDI_DMA_USE_PHYSICAL);
1583a634bfcSVikram Hegde 
1593a634bfcSVikram Hegde 		mp = mp->ml_next;
1603a634bfcSVikram Hegde 	}
1613a634bfcSVikram Hegde 
1623a634bfcSVikram Hegde 	memlist_read_unlock();
1633a634bfcSVikram Hegde }
1643a634bfcSVikram Hegde 
165e03dceedSVikram Hegde 
166e03dceedSVikram Hegde /*
167e03dceedSVikram Hegde  * Check if the driver requests physical mapping
168e03dceedSVikram Hegde  */
169e03dceedSVikram Hegde /*ARGSUSED*/
170e03dceedSVikram Hegde static void
171e03dceedSVikram Hegde check_physical(dev_info_t *dip, void *arg)
172e03dceedSVikram Hegde {
173e03dceedSVikram Hegde 	char *val;
174e03dceedSVikram Hegde 
175e03dceedSVikram Hegde 	/*
176e03dceedSVikram Hegde 	 * Check for the DVMA unity mapping property on the device
177e03dceedSVikram Hegde 	 */
178e03dceedSVikram Hegde 	val = NULL;
179e03dceedSVikram Hegde 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
180e03dceedSVikram Hegde 	    DDI_PROP_DONTPASS, DDI_DVMA_MAPTYPE_PROP, &val) == DDI_SUCCESS) {
181e03dceedSVikram Hegde 		ASSERT(val);
182e03dceedSVikram Hegde 		if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) != 0) {
183e03dceedSVikram Hegde 			ddi_err(DER_WARN, dip, "%s value \"%s\" is not valid",
184e03dceedSVikram Hegde 			    DDI_DVMA_MAPTYPE_PROP, val);
185e03dceedSVikram Hegde 		} else {
186e03dceedSVikram Hegde 			int e;
187e03dceedSVikram Hegde 
188e03dceedSVikram Hegde 			ddi_err(DER_NOTE, dip,
189e03dceedSVikram Hegde 			    "Using unity DVMA mapping for device");
190e03dceedSVikram Hegde 			e = immu_dvma_map(NULL, NULL, NULL, 0, dip,
191e03dceedSVikram Hegde 			    IMMU_FLAGS_UNITY);
192e03dceedSVikram Hegde 			/* for unity mode, map will return USE_PHYSICAL */
193e03dceedSVikram Hegde 			ASSERT(e == DDI_DMA_USE_PHYSICAL);
194e03dceedSVikram Hegde 		}
195e03dceedSVikram Hegde 		ddi_prop_free(val);
196e03dceedSVikram Hegde 	}
197e03dceedSVikram Hegde }
198e03dceedSVikram Hegde 
1993a634bfcSVikram Hegde /*
2003a634bfcSVikram Hegde  * Check if the device is USB controller
2013a634bfcSVikram Hegde  */
2023a634bfcSVikram Hegde /*ARGSUSED*/
2033a634bfcSVikram Hegde static void
2043a634bfcSVikram Hegde check_usb(dev_info_t *dip, void *arg)
2053a634bfcSVikram Hegde {
2063a634bfcSVikram Hegde 	const char *drv = ddi_driver_name(dip);
2073a634bfcSVikram Hegde 
2083a634bfcSVikram Hegde 	if (drv == NULL ||
2093a634bfcSVikram Hegde 	    (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 &&
2103a634bfcSVikram Hegde 	    strcmp(drv, "ehci") != 0)) {
2113a634bfcSVikram Hegde 		return;
2123a634bfcSVikram Hegde 	}
2133a634bfcSVikram Hegde 
2143a634bfcSVikram Hegde 	/* This must come first since it does unity mapping */
2153a634bfcSVikram Hegde 	if (immu_quirk_usbfullpa == B_TRUE) {
2163a634bfcSVikram Hegde 		int e;
2173a634bfcSVikram Hegde 		ddi_err(DER_NOTE, dip, "Applying USB FULL PA quirk");
2183a634bfcSVikram Hegde 		e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY);
2193a634bfcSVikram Hegde 		/* for unity mode, map will return USE_PHYSICAL */
2203a634bfcSVikram Hegde 		ASSERT(e == DDI_DMA_USE_PHYSICAL);
2213a634bfcSVikram Hegde 	}
2223a634bfcSVikram Hegde 
2233a634bfcSVikram Hegde 	if (immu_quirk_usbrmrr == B_TRUE) {
2243a634bfcSVikram Hegde 		ddi_err(DER_LOG, dip, "Applying USB RMRR quirk");
2253a634bfcSVikram Hegde 		map_bios_rsvd_mem(dip);
2263a634bfcSVikram Hegde 	}
2273a634bfcSVikram Hegde }
2283a634bfcSVikram Hegde 
2293a634bfcSVikram Hegde /*
2303a634bfcSVikram Hegde  * Check if the device is a LPC device
2313a634bfcSVikram Hegde  */
2323a634bfcSVikram Hegde /*ARGSUSED*/
2333a634bfcSVikram Hegde static void
2343a634bfcSVikram Hegde check_lpc(dev_info_t *dip, void *arg)
2353a634bfcSVikram Hegde {
2363a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
2373a634bfcSVikram Hegde 
2383a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
2393a634bfcSVikram Hegde 	ASSERT(immu_devi);
2403a634bfcSVikram Hegde 	if (immu_devi->imd_lpc == B_TRUE) {
2413a634bfcSVikram Hegde 		ddi_err(DER_LOG, dip, "IMMU: Found LPC device");
2423a634bfcSVikram Hegde 		/* This will put the immu_devi on the LPC "specials" list */
2433a634bfcSVikram Hegde 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
2443a634bfcSVikram Hegde 	}
2453a634bfcSVikram Hegde }
2463a634bfcSVikram Hegde 
2473a634bfcSVikram Hegde /*
2483a634bfcSVikram Hegde  * Check if the device is a GFX device
2493a634bfcSVikram Hegde  */
2503a634bfcSVikram Hegde /*ARGSUSED*/
2513a634bfcSVikram Hegde static void
2523a634bfcSVikram Hegde check_gfx(dev_info_t *dip, void *arg)
2533a634bfcSVikram Hegde {
2543a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
2553a634bfcSVikram Hegde 	int e;
2563a634bfcSVikram Hegde 
2573a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
2583a634bfcSVikram Hegde 	ASSERT(immu_devi);
2593a634bfcSVikram Hegde 	if (immu_devi->imd_display == B_TRUE) {
2603a634bfcSVikram Hegde 		ddi_err(DER_LOG, dip, "IMMU: Found GFX device");
2613a634bfcSVikram Hegde 		/* This will put the immu_devi on the GFX "specials" list */
2623a634bfcSVikram Hegde 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
2633a634bfcSVikram Hegde 		e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY);
2643a634bfcSVikram Hegde 		/* for unity mode, map will return USE_PHYSICAL */
2653a634bfcSVikram Hegde 		ASSERT(e == DDI_DMA_USE_PHYSICAL);
2663a634bfcSVikram Hegde 	}
2673a634bfcSVikram Hegde }
2683a634bfcSVikram Hegde 
2693a634bfcSVikram Hegde static void
2703a634bfcSVikram Hegde walk_tree(int (*f)(dev_info_t *, void *), void *arg)
2713a634bfcSVikram Hegde {
2723a634bfcSVikram Hegde 	int count;
2733a634bfcSVikram Hegde 
2743a634bfcSVikram Hegde 	ndi_devi_enter(root_devinfo, &count);
2753a634bfcSVikram Hegde 	ddi_walk_devs(ddi_get_child(root_devinfo), f, arg);
2763a634bfcSVikram Hegde 	ndi_devi_exit(root_devinfo, count);
2773a634bfcSVikram Hegde }
2783a634bfcSVikram Hegde 
2793a634bfcSVikram Hegde static int
2803a634bfcSVikram Hegde check_pre_setup_quirks(dev_info_t *dip, void *arg)
2813a634bfcSVikram Hegde {
2823a634bfcSVikram Hegde 	/* just 1 check right now */
2833a634bfcSVikram Hegde 	return (check_mobile4(dip, arg));
2843a634bfcSVikram Hegde }
2853a634bfcSVikram Hegde 
2863a634bfcSVikram Hegde static int
2873a634bfcSVikram Hegde check_pre_startup_quirks(dev_info_t *dip, void *arg)
2883a634bfcSVikram Hegde {
2893a634bfcSVikram Hegde 	if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) {
2903a634bfcSVikram Hegde 		ddi_err(DER_PANIC, dip, "Failed to get immu_devi");
2913a634bfcSVikram Hegde 	}
2923a634bfcSVikram Hegde 
2933a634bfcSVikram Hegde 	check_gfx(dip, arg);
2943a634bfcSVikram Hegde 
2953a634bfcSVikram Hegde 	check_lpc(dip, arg);
2963a634bfcSVikram Hegde 
2973a634bfcSVikram Hegde 	check_usb(dip, arg);
2983a634bfcSVikram Hegde 
299e03dceedSVikram Hegde 	check_physical(dip, arg);
300e03dceedSVikram Hegde 
3013a634bfcSVikram Hegde 	return (DDI_WALK_CONTINUE);
3023a634bfcSVikram Hegde }
3033a634bfcSVikram Hegde 
3043a634bfcSVikram Hegde static void
3053a634bfcSVikram Hegde pre_setup_quirks(void)
3063a634bfcSVikram Hegde {
3073a634bfcSVikram Hegde 	walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4);
3083a634bfcSVikram Hegde }
3093a634bfcSVikram Hegde 
3103a634bfcSVikram Hegde static void
3113a634bfcSVikram Hegde pre_startup_quirks(void)
3123a634bfcSVikram Hegde {
3133a634bfcSVikram Hegde 	walk_tree(check_pre_startup_quirks, NULL);
3143a634bfcSVikram Hegde 
3153a634bfcSVikram Hegde 	immu_dmar_rmrr_map();
3163a634bfcSVikram Hegde }
3173a634bfcSVikram Hegde 
3183a634bfcSVikram Hegde /*
319*3adb2334SVikram Hegde  * get_conf_opt()
320*3adb2334SVikram Hegde  * 	get a rootnex.conf setting  (always a boolean)
321*3adb2334SVikram Hegde  */
322*3adb2334SVikram Hegde static void
323*3adb2334SVikram Hegde get_conf_opt(char *bopt, boolean_t *kvar)
324*3adb2334SVikram Hegde {
325*3adb2334SVikram Hegde 	char *val = NULL;
326*3adb2334SVikram Hegde 
327*3adb2334SVikram Hegde 	ASSERT(bopt);
328*3adb2334SVikram Hegde 	ASSERT(kvar);
329*3adb2334SVikram Hegde 
330*3adb2334SVikram Hegde 	/*
331*3adb2334SVikram Hegde 	 * Check the rootnex.conf property
332*3adb2334SVikram Hegde 	 * Fake up a dev_t since searching the global
333*3adb2334SVikram Hegde 	 * property list needs it
334*3adb2334SVikram Hegde 	 */
335*3adb2334SVikram Hegde 	if (ddi_prop_lookup_string(makedevice(ddi_name_to_major("rootnex"), 0),
336*3adb2334SVikram Hegde 	    root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
337*3adb2334SVikram Hegde 	    bopt, &val) != DDI_PROP_SUCCESS) {
338*3adb2334SVikram Hegde 		return;
339*3adb2334SVikram Hegde 	}
340*3adb2334SVikram Hegde 
341*3adb2334SVikram Hegde 	ASSERT(val);
342*3adb2334SVikram Hegde 	if (strcmp(val, "true") == 0) {
343*3adb2334SVikram Hegde 		*kvar = B_TRUE;
344*3adb2334SVikram Hegde 	} else if (strcmp(val, "false") == 0) {
345*3adb2334SVikram Hegde 		*kvar = B_FALSE;
346*3adb2334SVikram Hegde 	} else {
347*3adb2334SVikram Hegde 		ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ",
348*3adb2334SVikram Hegde 		    "is not set to true or false. Ignoring option.",
349*3adb2334SVikram Hegde 		    bopt, val);
350*3adb2334SVikram Hegde 	}
351*3adb2334SVikram Hegde 	ddi_prop_free(val);
352*3adb2334SVikram Hegde }
353*3adb2334SVikram Hegde 
354*3adb2334SVikram Hegde /*
3553a634bfcSVikram Hegde  * get_bootopt()
3563a634bfcSVikram Hegde  * 	check a boot option  (always a boolean)
3573a634bfcSVikram Hegde  */
3583a634bfcSVikram Hegde static void
3593a634bfcSVikram Hegde get_bootopt(char *bopt, boolean_t *kvar)
3603a634bfcSVikram Hegde {
3613a634bfcSVikram Hegde 	char *val = NULL;
3623a634bfcSVikram Hegde 
3633a634bfcSVikram Hegde 	ASSERT(bopt);
3643a634bfcSVikram Hegde 	ASSERT(kvar);
3653a634bfcSVikram Hegde 
3663a634bfcSVikram Hegde 	/*
3673a634bfcSVikram Hegde 	 * All boot options set at the GRUB menu become
3683a634bfcSVikram Hegde 	 * properties on the rootnex.
3693a634bfcSVikram Hegde 	 */
3703a634bfcSVikram Hegde 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo,
3713a634bfcSVikram Hegde 	    DDI_PROP_DONTPASS, bopt, &val) == DDI_SUCCESS) {
3723a634bfcSVikram Hegde 		ASSERT(val);
3733a634bfcSVikram Hegde 		if (strcmp(val, "true") == 0) {
3743a634bfcSVikram Hegde 			*kvar = B_TRUE;
3753a634bfcSVikram Hegde 		} else if (strcmp(val, "false") == 0) {
3763a634bfcSVikram Hegde 			*kvar = B_FALSE;
3773a634bfcSVikram Hegde 		} else {
3783a634bfcSVikram Hegde 			ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ",
3793a634bfcSVikram Hegde 			    "is not set to true or false. Ignoring option.",
3803a634bfcSVikram Hegde 			    bopt, val);
3813a634bfcSVikram Hegde 		}
3823a634bfcSVikram Hegde 		ddi_prop_free(val);
3833a634bfcSVikram Hegde 	}
3843a634bfcSVikram Hegde }
3853a634bfcSVikram Hegde 
3863a634bfcSVikram Hegde static void
387*3adb2334SVikram Hegde get_conf_tunables(char *bopt, int64_t *ivar)
388e03dceedSVikram Hegde {
389e03dceedSVikram Hegde 	int64_t	*iarray;
390e03dceedSVikram Hegde 	uint_t n;
391e03dceedSVikram Hegde 
392e03dceedSVikram Hegde 	/*
393e03dceedSVikram Hegde 	 * Check the rootnex.conf property
394e03dceedSVikram Hegde 	 * Fake up a dev_t since searching the global
395e03dceedSVikram Hegde 	 * property list needs it
396e03dceedSVikram Hegde 	 */
397e03dceedSVikram Hegde 	if (ddi_prop_lookup_int64_array(
398e03dceedSVikram Hegde 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
399e03dceedSVikram Hegde 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt,
400e03dceedSVikram Hegde 	    &iarray, &n) != DDI_PROP_SUCCESS) {
401e03dceedSVikram Hegde 		return;
402e03dceedSVikram Hegde 	}
403e03dceedSVikram Hegde 
404e03dceedSVikram Hegde 	if (n != 1) {
405e03dceedSVikram Hegde 		ddi_err(DER_WARN, NULL, "More than one value specified for "
406e03dceedSVikram Hegde 		    "%s property. Ignoring and using default",
407e03dceedSVikram Hegde 		    "immu-flush-gran");
408e03dceedSVikram Hegde 		ddi_prop_free(iarray);
409e03dceedSVikram Hegde 		return;
410e03dceedSVikram Hegde 	}
411e03dceedSVikram Hegde 
412e03dceedSVikram Hegde 	if (iarray[0] < 0) {
413e03dceedSVikram Hegde 		ddi_err(DER_WARN, NULL, "Negative value specified for "
414e03dceedSVikram Hegde 		    "%s property. Inoring and Using default value",
415e03dceedSVikram Hegde 		    "immu-flush-gran");
416e03dceedSVikram Hegde 		ddi_prop_free(iarray);
417e03dceedSVikram Hegde 		return;
418e03dceedSVikram Hegde 	}
419e03dceedSVikram Hegde 
420e03dceedSVikram Hegde 	*ivar = iarray[0];
421e03dceedSVikram Hegde 
422e03dceedSVikram Hegde 	ddi_prop_free(iarray);
423e03dceedSVikram Hegde }
424e03dceedSVikram Hegde 
425e03dceedSVikram Hegde static void
426*3adb2334SVikram Hegde read_conf_options(void)
427*3adb2334SVikram Hegde {
428*3adb2334SVikram Hegde 	/* enable/disable options */
429*3adb2334SVikram Hegde 	get_conf_opt("immu-enable", &immu_enable);
430*3adb2334SVikram Hegde 	get_conf_opt("immu-dvma-enable", &immu_dvma_enable);
431*3adb2334SVikram Hegde 	get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
432*3adb2334SVikram Hegde 	get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable);
433*3adb2334SVikram Hegde 	get_conf_opt("immu-qinv-enable", &immu_qinv_enable);
434*3adb2334SVikram Hegde 
435*3adb2334SVikram Hegde 	/* workaround switches */
436*3adb2334SVikram Hegde 	get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
437*3adb2334SVikram Hegde 	get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
438*3adb2334SVikram Hegde 	get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
439*3adb2334SVikram Hegde 
440*3adb2334SVikram Hegde 	/* debug printing */
441*3adb2334SVikram Hegde 	get_conf_opt("immu-dmar-print", &immu_dmar_print);
442*3adb2334SVikram Hegde 
443*3adb2334SVikram Hegde 	/* get tunables */
444*3adb2334SVikram Hegde 	get_conf_tunables("immu-flush-gran", &immu_flush_gran);
445*3adb2334SVikram Hegde }
446*3adb2334SVikram Hegde 
447*3adb2334SVikram Hegde static void
4483a634bfcSVikram Hegde read_boot_options(void)
4493a634bfcSVikram Hegde {
4503a634bfcSVikram Hegde 	/* enable/disable options */
4513a634bfcSVikram Hegde 	get_bootopt("immu-enable", &immu_enable);
4523a634bfcSVikram Hegde 	get_bootopt("immu-dvma-enable", &immu_dvma_enable);
4533a634bfcSVikram Hegde 	get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
4543a634bfcSVikram Hegde 	get_bootopt("immu-intrmap-enable", &immu_intrmap_enable);
4553a634bfcSVikram Hegde 	get_bootopt("immu-qinv-enable", &immu_qinv_enable);
4563a634bfcSVikram Hegde 
4573a634bfcSVikram Hegde 	/* workaround switches */
4583a634bfcSVikram Hegde 	get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
4593a634bfcSVikram Hegde 	get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
4603a634bfcSVikram Hegde 	get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
4613a634bfcSVikram Hegde 
4623a634bfcSVikram Hegde 	/* debug printing */
4633a634bfcSVikram Hegde 	get_bootopt("immu-dmar-print", &immu_dmar_print);
4643a634bfcSVikram Hegde }
4653a634bfcSVikram Hegde 
4663a634bfcSVikram Hegde /*
4673a634bfcSVikram Hegde  * Note, this will not catch hardware not enumerated
4683a634bfcSVikram Hegde  * in early boot
4693a634bfcSVikram Hegde  */
4703a634bfcSVikram Hegde static boolean_t
4713a634bfcSVikram Hegde blacklisted_driver(void)
4723a634bfcSVikram Hegde {
4733a634bfcSVikram Hegde 	char **strptr;
4743a634bfcSVikram Hegde 	int i;
4753a634bfcSVikram Hegde 	major_t maj;
4763a634bfcSVikram Hegde 
4773a634bfcSVikram Hegde 	ASSERT((black_array == NULL) ^ (nblacks != 0));
4783a634bfcSVikram Hegde 
4793a634bfcSVikram Hegde 	/* need at least 2 strings */
4803a634bfcSVikram Hegde 	if (nblacks < 2) {
4813a634bfcSVikram Hegde 		return (B_FALSE);
4823a634bfcSVikram Hegde 	}
4833a634bfcSVikram Hegde 
4843a634bfcSVikram Hegde 	for (i = 0; nblacks - i > 1; i++) {
485e03dceedSVikram Hegde 		strptr = &black_array[i];
4863a634bfcSVikram Hegde 		if (strcmp(*strptr++, "DRIVER") == 0) {
4873a634bfcSVikram Hegde 			if ((maj = ddi_name_to_major(*strptr++))
4883a634bfcSVikram Hegde 			    != DDI_MAJOR_T_NONE) {
4893a634bfcSVikram Hegde 				/* is there hardware bound to this drvr */
4903a634bfcSVikram Hegde 				if (devnamesp[maj].dn_head != NULL) {
4913a634bfcSVikram Hegde 					return (B_TRUE);
4923a634bfcSVikram Hegde 				}
4933a634bfcSVikram Hegde 			}
4943a634bfcSVikram Hegde 			i += 1;   /* for loop adds 1, so add only 1 here */
4953a634bfcSVikram Hegde 		}
4963a634bfcSVikram Hegde 	}
4973a634bfcSVikram Hegde 
4983a634bfcSVikram Hegde 	return (B_FALSE);
4993a634bfcSVikram Hegde }
5003a634bfcSVikram Hegde 
5013a634bfcSVikram Hegde static boolean_t
5023a634bfcSVikram Hegde blacklisted_smbios(void)
5033a634bfcSVikram Hegde {
5043a634bfcSVikram Hegde 	id_t smid;
5053a634bfcSVikram Hegde 	smbios_hdl_t *smhdl;
5063a634bfcSVikram Hegde 	smbios_info_t sminf;
5073a634bfcSVikram Hegde 	smbios_system_t smsys;
5083a634bfcSVikram Hegde 	char *mfg, *product, *version;
5093a634bfcSVikram Hegde 	char **strptr;
5103a634bfcSVikram Hegde 	int i;
5113a634bfcSVikram Hegde 
5123a634bfcSVikram Hegde 	ASSERT((black_array == NULL) ^ (nblacks != 0));
5133a634bfcSVikram Hegde 
5143a634bfcSVikram Hegde 	/* need at least 4 strings for this setting */
5153a634bfcSVikram Hegde 	if (nblacks < 4) {
5163a634bfcSVikram Hegde 		return (B_FALSE);
5173a634bfcSVikram Hegde 	}
5183a634bfcSVikram Hegde 
5193a634bfcSVikram Hegde 	smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
5203a634bfcSVikram Hegde 	if (smhdl == NULL ||
5213a634bfcSVikram Hegde 	    (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR ||
5223a634bfcSVikram Hegde 	    smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) {
5233a634bfcSVikram Hegde 		return (B_FALSE);
5243a634bfcSVikram Hegde 	}
5253a634bfcSVikram Hegde 
5263a634bfcSVikram Hegde 	mfg = (char *)sminf.smbi_manufacturer;
5273a634bfcSVikram Hegde 	product = (char *)sminf.smbi_product;
5283a634bfcSVikram Hegde 	version = (char *)sminf.smbi_version;
5293a634bfcSVikram Hegde 
5303a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n");
5313a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg);
5323a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product);
5333a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version);
5343a634bfcSVikram Hegde 
5353a634bfcSVikram Hegde 	for (i = 0; nblacks - i > 3; i++) {
536e03dceedSVikram Hegde 		strptr = &black_array[i];
5373a634bfcSVikram Hegde 		if (strcmp(*strptr++, "SMBIOS") == 0) {
5383a634bfcSVikram Hegde 			if (strcmp(*strptr++, mfg) == 0 &&
5393a634bfcSVikram Hegde 			    ((char *)strptr == '\0' ||
5403a634bfcSVikram Hegde 			    strcmp(*strptr++, product) == 0) &&
5413a634bfcSVikram Hegde 			    ((char *)strptr == '\0' ||
5423a634bfcSVikram Hegde 			    strcmp(*strptr++, version) == 0)) {
5433a634bfcSVikram Hegde 				return (B_TRUE);
5443a634bfcSVikram Hegde 			}
5453a634bfcSVikram Hegde 			i += 3;
5463a634bfcSVikram Hegde 		}
5473a634bfcSVikram Hegde 	}
5483a634bfcSVikram Hegde 
5493a634bfcSVikram Hegde 	return (B_FALSE);
5503a634bfcSVikram Hegde }
5513a634bfcSVikram Hegde 
5523a634bfcSVikram Hegde static boolean_t
5533a634bfcSVikram Hegde blacklisted_acpi(void)
5543a634bfcSVikram Hegde {
5553a634bfcSVikram Hegde 	ASSERT((black_array == NULL) ^ (nblacks != 0));
5563a634bfcSVikram Hegde 	if (nblacks == 0) {
5573a634bfcSVikram Hegde 		return (B_FALSE);
5583a634bfcSVikram Hegde 	}
5593a634bfcSVikram Hegde 
5603a634bfcSVikram Hegde 	return (immu_dmar_blacklisted(black_array, nblacks));
5613a634bfcSVikram Hegde }
5623a634bfcSVikram Hegde 
5633a634bfcSVikram Hegde /*
5643a634bfcSVikram Hegde  * Check if system is blacklisted by Intel IOMMU driver
5653a634bfcSVikram Hegde  * i.e. should Intel IOMMU be disabled on this system
5663a634bfcSVikram Hegde  * Currently a system can be blacklistd based on the
5673a634bfcSVikram Hegde  * following bases:
5683a634bfcSVikram Hegde  *
5693a634bfcSVikram Hegde  * 1. DMAR ACPI table information.
5703a634bfcSVikram Hegde  *    This information includes things like
5713a634bfcSVikram Hegde  *    manufacturer and revision number. If rootnex.conf
5723a634bfcSVikram Hegde  *    has matching info set in its blacklist property
5733a634bfcSVikram Hegde  *    then Intel IOMMu will be disabled
5743a634bfcSVikram Hegde  *
5753a634bfcSVikram Hegde  * 2. SMBIOS information
5763a634bfcSVikram Hegde  *
5773a634bfcSVikram Hegde  * 3. Driver installed - useful if a particular
5783a634bfcSVikram Hegde  *    driver or hardware is toxic if Intel IOMMU
5793a634bfcSVikram Hegde  *    is turned on.
5803a634bfcSVikram Hegde  */
5813a634bfcSVikram Hegde 
5823a634bfcSVikram Hegde static void
5833a634bfcSVikram Hegde blacklist_setup(void)
5843a634bfcSVikram Hegde {
5853a634bfcSVikram Hegde 	char **string_array;
5863a634bfcSVikram Hegde 	uint_t nstrings;
5873a634bfcSVikram Hegde 
5883a634bfcSVikram Hegde 	/*
5893a634bfcSVikram Hegde 	 * Check the rootnex.conf blacklist property.
5903a634bfcSVikram Hegde 	 * Fake up a dev_t since searching the global
5913a634bfcSVikram Hegde 	 * property list needs it
5923a634bfcSVikram Hegde 	 */
5933a634bfcSVikram Hegde 	if (ddi_prop_lookup_string_array(
5943a634bfcSVikram Hegde 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
5953a634bfcSVikram Hegde 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist",
5963a634bfcSVikram Hegde 	    &string_array, &nstrings) != DDI_PROP_SUCCESS) {
5973a634bfcSVikram Hegde 		return;
5983a634bfcSVikram Hegde 	}
5993a634bfcSVikram Hegde 
6003a634bfcSVikram Hegde 	/* smallest blacklist criteria works with multiples of 2 */
6013a634bfcSVikram Hegde 	if (nstrings % 2 != 0) {
6023a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist "
6033a634bfcSVikram Hegde 		    "rootnex.conf: number of strings must be a "
6043a634bfcSVikram Hegde 		    "multiple of 2");
6053a634bfcSVikram Hegde 		ddi_prop_free(string_array);
6063a634bfcSVikram Hegde 		return;
6073a634bfcSVikram Hegde 	}
6083a634bfcSVikram Hegde 
6093a634bfcSVikram Hegde 	black_array = string_array;
6103a634bfcSVikram Hegde 	nblacks = nstrings;
6113a634bfcSVikram Hegde }
6123a634bfcSVikram Hegde 
6133a634bfcSVikram Hegde static void
6143a634bfcSVikram Hegde blacklist_destroy(void)
6153a634bfcSVikram Hegde {
6163a634bfcSVikram Hegde 	if (black_array) {
6173a634bfcSVikram Hegde 		ddi_prop_free(black_array);
6183a634bfcSVikram Hegde 		black_array = NULL;
6193a634bfcSVikram Hegde 		nblacks = 0;
6203a634bfcSVikram Hegde 	}
6213a634bfcSVikram Hegde 
6223a634bfcSVikram Hegde 	ASSERT(black_array == NULL);
6233a634bfcSVikram Hegde 	ASSERT(nblacks == 0);
6243a634bfcSVikram Hegde }
6253a634bfcSVikram Hegde 
6263a634bfcSVikram Hegde 
6273a634bfcSVikram Hegde /*
6283a634bfcSVikram Hegde  * Now set all the fields in the order they are defined
6293a634bfcSVikram Hegde  * We do this only as a defensive-coding practice, it is
6303a634bfcSVikram Hegde  * not a correctness issue.
6313a634bfcSVikram Hegde  */
6323a634bfcSVikram Hegde static void *
6333a634bfcSVikram Hegde immu_state_alloc(int seg, void *dmar_unit)
6343a634bfcSVikram Hegde {
6353a634bfcSVikram Hegde 	immu_t *immu;
6363a634bfcSVikram Hegde 
6373a634bfcSVikram Hegde 	dmar_unit = immu_dmar_walk_units(seg, dmar_unit);
6383a634bfcSVikram Hegde 	if (dmar_unit == NULL) {
6393a634bfcSVikram Hegde 		/* No more IOMMUs in this segment */
6403a634bfcSVikram Hegde 		return (NULL);
6413a634bfcSVikram Hegde 	}
6423a634bfcSVikram Hegde 
6433a634bfcSVikram Hegde 	immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP);
6443a634bfcSVikram Hegde 
6453a634bfcSVikram Hegde 	mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL);
6463a634bfcSVikram Hegde 
6473a634bfcSVikram Hegde 	mutex_enter(&(immu->immu_lock));
6483a634bfcSVikram Hegde 
6493a634bfcSVikram Hegde 	immu->immu_dmar_unit = dmar_unit;
6503a634bfcSVikram Hegde 	immu->immu_name = ddi_strdup(immu_dmar_unit_name(dmar_unit),
6513a634bfcSVikram Hegde 	    KM_SLEEP);
6523a634bfcSVikram Hegde 	immu->immu_dip = immu_dmar_unit_dip(dmar_unit);
6533a634bfcSVikram Hegde 
6543a634bfcSVikram Hegde 	/*
6553a634bfcSVikram Hegde 	 * the immu_intr_lock mutex is grabbed by the IOMMU
6563a634bfcSVikram Hegde 	 * unit's interrupt handler so we need to use an
6573a634bfcSVikram Hegde 	 * interrupt cookie for the mutex
6583a634bfcSVikram Hegde 	 */
6593a634bfcSVikram Hegde 	mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER,
6603a634bfcSVikram Hegde 	    (void *)ipltospl(IMMU_INTR_IPL));
6613a634bfcSVikram Hegde 
6623a634bfcSVikram Hegde 	/* IOMMU regs related */
6633a634bfcSVikram Hegde 	mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL);
664e03dceedSVikram Hegde 	cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL);
665e03dceedSVikram Hegde 	immu->immu_regs_busy = B_FALSE;
6663a634bfcSVikram Hegde 
6673a634bfcSVikram Hegde 	/* DVMA related */
6683a634bfcSVikram Hegde 	immu->immu_dvma_coherent = B_FALSE;
6693a634bfcSVikram Hegde 
6703a634bfcSVikram Hegde 	/* DVMA context related */
6713a634bfcSVikram Hegde 	rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL);
6723a634bfcSVikram Hegde 
6733a634bfcSVikram Hegde 	/* DVMA domain related */
6743a634bfcSVikram Hegde 	list_create(&(immu->immu_domain_list), sizeof (domain_t),
6753a634bfcSVikram Hegde 	    offsetof(domain_t, dom_immu_node));
6763a634bfcSVikram Hegde 
6773a634bfcSVikram Hegde 	/* DVMA special device lists */
6783a634bfcSVikram Hegde 	immu->immu_dvma_gfx_only = B_FALSE;
6793a634bfcSVikram Hegde 	list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t),
6803a634bfcSVikram Hegde 	    offsetof(immu_devi_t, imd_spc_node));
6813a634bfcSVikram Hegde 	list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t),
6823a634bfcSVikram Hegde 	    offsetof(immu_devi_t, imd_spc_node));
6833a634bfcSVikram Hegde 
6843a634bfcSVikram Hegde 	/* interrupt remapping related */
6853a634bfcSVikram Hegde 	mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL);
6863a634bfcSVikram Hegde 
6873a634bfcSVikram Hegde 	/* qinv related */
6883a634bfcSVikram Hegde 	mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL);
6893a634bfcSVikram Hegde 
6903a634bfcSVikram Hegde 	/*
6913a634bfcSVikram Hegde 	 * insert this immu unit into the system-wide list
6923a634bfcSVikram Hegde 	 */
6933a634bfcSVikram Hegde 	list_insert_tail(&immu_list, immu);
6943a634bfcSVikram Hegde 
6953a634bfcSVikram Hegde 	mutex_exit(&(immu->immu_lock));
6963a634bfcSVikram Hegde 
6973a634bfcSVikram Hegde 	ddi_err(DER_LOG, immu->immu_dip, "IMMU: unit setup");
6983a634bfcSVikram Hegde 
6993a634bfcSVikram Hegde 	immu_dmar_set_immu(dmar_unit, immu);
7003a634bfcSVikram Hegde 
7013a634bfcSVikram Hegde 	return (dmar_unit);
7023a634bfcSVikram Hegde }
7033a634bfcSVikram Hegde 
7043a634bfcSVikram Hegde static void
7053a634bfcSVikram Hegde immu_subsystems_setup(void)
7063a634bfcSVikram Hegde {
7073a634bfcSVikram Hegde 	int seg;
7083a634bfcSVikram Hegde 	void *unit_hdl;
7093a634bfcSVikram Hegde 
7103a634bfcSVikram Hegde 	ddi_err(DER_VERB, NULL,
7113a634bfcSVikram Hegde 	    "Creating state structures for Intel IOMMU units\n");
7123a634bfcSVikram Hegde 
7133a634bfcSVikram Hegde 	ASSERT(immu_setup == B_FALSE);
7143a634bfcSVikram Hegde 	ASSERT(immu_running == B_FALSE);
7153a634bfcSVikram Hegde 
7163a634bfcSVikram Hegde 	mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL);
7173a634bfcSVikram Hegde 	list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node));
7183a634bfcSVikram Hegde 
7193a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
7203a634bfcSVikram Hegde 
721e03dceedSVikram Hegde 	ASSERT(immu_pgtable_cache == NULL);
722e03dceedSVikram Hegde 
723e03dceedSVikram Hegde 	immu_pgtable_cache = kmem_cache_create("immu_pgtable_cache",
724e03dceedSVikram Hegde 	    sizeof (pgtable_t), 0,
725e03dceedSVikram Hegde 	    pgtable_ctor, pgtable_dtor, NULL, NULL, NULL, 0);
726e03dceedSVikram Hegde 
7273a634bfcSVikram Hegde 	unit_hdl = NULL;
7283a634bfcSVikram Hegde 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
7293a634bfcSVikram Hegde 		while (unit_hdl = immu_state_alloc(seg, unit_hdl)) {
7303a634bfcSVikram Hegde 			;
7313a634bfcSVikram Hegde 		}
7323a634bfcSVikram Hegde 	}
7333a634bfcSVikram Hegde 
7343a634bfcSVikram Hegde 	immu_regs_setup(&immu_list);	/* subsequent code needs this first */
7353a634bfcSVikram Hegde 	immu_dvma_setup(&immu_list);
7363a634bfcSVikram Hegde 	immu_intrmap_setup(&immu_list);
7373a634bfcSVikram Hegde 	immu_qinv_setup(&immu_list);
7383a634bfcSVikram Hegde 
7393a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
7403a634bfcSVikram Hegde }
7413a634bfcSVikram Hegde 
7423a634bfcSVikram Hegde /*
7433a634bfcSVikram Hegde  * immu_subsystems_startup()
7443a634bfcSVikram Hegde  * 	startup all units that were setup
7453a634bfcSVikram Hegde  */
7463a634bfcSVikram Hegde static void
7473a634bfcSVikram Hegde immu_subsystems_startup(void)
7483a634bfcSVikram Hegde {
7493a634bfcSVikram Hegde 	immu_t *immu;
7503a634bfcSVikram Hegde 
7513a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
7523a634bfcSVikram Hegde 
7533a634bfcSVikram Hegde 	ASSERT(immu_setup == B_TRUE);
7543a634bfcSVikram Hegde 	ASSERT(immu_running == B_FALSE);
7553a634bfcSVikram Hegde 
7563a634bfcSVikram Hegde 	immu_dmar_startup();
7573a634bfcSVikram Hegde 
7583a634bfcSVikram Hegde 	immu = list_head(&immu_list);
7593a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
7603a634bfcSVikram Hegde 
7613a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
7623a634bfcSVikram Hegde 
7633a634bfcSVikram Hegde 		immu_intr_register(immu);
7643a634bfcSVikram Hegde 		immu_dvma_startup(immu);
7653a634bfcSVikram Hegde 		immu_intrmap_startup(immu);
7663a634bfcSVikram Hegde 		immu_qinv_startup(immu);
7673a634bfcSVikram Hegde 
7683a634bfcSVikram Hegde 		/*
7693a634bfcSVikram Hegde 		 * Set IOMMU unit's regs to do
7703a634bfcSVikram Hegde 		 * the actual startup. This will
7713a634bfcSVikram Hegde 		 * set immu->immu_running  field
7723a634bfcSVikram Hegde 		 * if the unit is successfully
7733a634bfcSVikram Hegde 		 * started
7743a634bfcSVikram Hegde 		 */
7753a634bfcSVikram Hegde 		immu_regs_startup(immu);
7763a634bfcSVikram Hegde 
7773a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
7783a634bfcSVikram Hegde 	}
7793a634bfcSVikram Hegde 
7803a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
7813a634bfcSVikram Hegde }
7823a634bfcSVikram Hegde 
7833a634bfcSVikram Hegde /* ##################  Intel IOMMU internal interfaces ###################### */
7843a634bfcSVikram Hegde 
7853a634bfcSVikram Hegde /*
7863a634bfcSVikram Hegde  * Internal interfaces for IOMMU code (i.e. not exported to rootnex
7873a634bfcSVikram Hegde  * or rest of system)
7883a634bfcSVikram Hegde  */
7893a634bfcSVikram Hegde 
7903a634bfcSVikram Hegde /*
7913a634bfcSVikram Hegde  * ddip can be NULL, in which case we walk up until we find the root dip
7923a634bfcSVikram Hegde  * NOTE: We never visit the root dip since its not a hardware node
7933a634bfcSVikram Hegde  */
7943a634bfcSVikram Hegde int
7953a634bfcSVikram Hegde immu_walk_ancestor(
7963a634bfcSVikram Hegde 	dev_info_t *rdip,
7973a634bfcSVikram Hegde 	dev_info_t *ddip,
7983a634bfcSVikram Hegde 	int (*func)(dev_info_t *, void *arg),
7993a634bfcSVikram Hegde 	void *arg,
8003a634bfcSVikram Hegde 	int *lvlp,
8013a634bfcSVikram Hegde 	immu_flags_t immu_flags)
8023a634bfcSVikram Hegde {
8033a634bfcSVikram Hegde 	dev_info_t *pdip;
8043a634bfcSVikram Hegde 	int level;
8053a634bfcSVikram Hegde 	int error = DDI_SUCCESS;
8063a634bfcSVikram Hegde 
8073a634bfcSVikram Hegde 	ASSERT(root_devinfo);
8083a634bfcSVikram Hegde 	ASSERT(rdip);
8093a634bfcSVikram Hegde 	ASSERT(rdip != root_devinfo);
8103a634bfcSVikram Hegde 	ASSERT(func);
8113a634bfcSVikram Hegde 
8123a634bfcSVikram Hegde 	/* ddip and immu can be NULL */
8133a634bfcSVikram Hegde 
8143a634bfcSVikram Hegde 	/* Hold rdip so that branch is not detached */
8153a634bfcSVikram Hegde 	ndi_hold_devi(rdip);
8163a634bfcSVikram Hegde 	for (pdip = rdip, level = 1; pdip && pdip != root_devinfo;
8173a634bfcSVikram Hegde 	    pdip = ddi_get_parent(pdip), level++) {
8183a634bfcSVikram Hegde 
8193a634bfcSVikram Hegde 		if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) {
8203a634bfcSVikram Hegde 			error = DDI_FAILURE;
8213a634bfcSVikram Hegde 			break;
8223a634bfcSVikram Hegde 		}
8233a634bfcSVikram Hegde 		if (func(pdip, arg) == DDI_WALK_TERMINATE) {
8243a634bfcSVikram Hegde 			break;
8253a634bfcSVikram Hegde 		}
8263a634bfcSVikram Hegde 		if (immu_flags & IMMU_FLAGS_DONTPASS) {
8273a634bfcSVikram Hegde 			break;
8283a634bfcSVikram Hegde 		}
8293a634bfcSVikram Hegde 		if (pdip == ddip) {
8303a634bfcSVikram Hegde 			break;
8313a634bfcSVikram Hegde 		}
8323a634bfcSVikram Hegde 	}
8333a634bfcSVikram Hegde 
8343a634bfcSVikram Hegde 	ndi_rele_devi(rdip);
8353a634bfcSVikram Hegde 
8363a634bfcSVikram Hegde 	if (lvlp)
8373a634bfcSVikram Hegde 		*lvlp = level;
8383a634bfcSVikram Hegde 
8393a634bfcSVikram Hegde 	return (error);
8403a634bfcSVikram Hegde }
8413a634bfcSVikram Hegde 
8423a634bfcSVikram Hegde /* ########################  Intel IOMMU entry points ####################### */
8433a634bfcSVikram Hegde /*
8443a634bfcSVikram Hegde  * immu_init()
8453a634bfcSVikram Hegde  *	called from rootnex_attach(). setup but don't startup the Intel IOMMU
8463a634bfcSVikram Hegde  *      This is the first function called in Intel IOMMU code
8473a634bfcSVikram Hegde  */
8483a634bfcSVikram Hegde void
8493a634bfcSVikram Hegde immu_init(void)
8503a634bfcSVikram Hegde {
8513a634bfcSVikram Hegde 	char *phony_reg = "A thing of beauty is a joy forever";
8523a634bfcSVikram Hegde 
8533a634bfcSVikram Hegde 	/* Set some global shorthands that are needed by all of IOMMU code */
8543a634bfcSVikram Hegde 	ASSERT(root_devinfo == NULL);
8553a634bfcSVikram Hegde 	root_devinfo = ddi_root_node();
8563a634bfcSVikram Hegde 
8573a634bfcSVikram Hegde 	/*
8583a634bfcSVikram Hegde 	 * Intel IOMMU only supported only if MMU(CPU) page size is ==
8593a634bfcSVikram Hegde 	 * IOMMU pages size.
8603a634bfcSVikram Hegde 	 */
8613a634bfcSVikram Hegde 	/*LINTED*/
8623a634bfcSVikram Hegde 	if (MMU_PAGESIZE != IMMU_PAGESIZE) {
8633a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL,
8643a634bfcSVikram Hegde 		    "MMU page size (%d) is not equal to\n"
8653a634bfcSVikram Hegde 		    "IOMMU page size (%d). "
8663a634bfcSVikram Hegde 		    "Disabling Intel IOMMU. ",
8673a634bfcSVikram Hegde 		    MMU_PAGESIZE, IMMU_PAGESIZE);
8683a634bfcSVikram Hegde 		immu_enable = B_FALSE;
8693a634bfcSVikram Hegde 		return;
8703a634bfcSVikram Hegde 	}
8713a634bfcSVikram Hegde 
8723a634bfcSVikram Hegde 	/*
873*3adb2334SVikram Hegde 	 * Read rootnex.conf options. Do this before
874*3adb2334SVikram Hegde 	 * boot options so boot options can override .conf options.
875*3adb2334SVikram Hegde 	 */
876*3adb2334SVikram Hegde 	read_conf_options();
877*3adb2334SVikram Hegde 
878*3adb2334SVikram Hegde 	/*
8793a634bfcSVikram Hegde 	 * retrieve the Intel IOMMU boot options.
8803a634bfcSVikram Hegde 	 * Do this before parsing immu ACPI table
8813a634bfcSVikram Hegde 	 * as a boot option could potentially affect
8823a634bfcSVikram Hegde 	 * ACPI parsing.
8833a634bfcSVikram Hegde 	 */
8843a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n");
8853a634bfcSVikram Hegde 	read_boot_options();
8863a634bfcSVikram Hegde 
8873a634bfcSVikram Hegde 	/*
8883a634bfcSVikram Hegde 	 * Check the IOMMU enable boot-option first.
8893a634bfcSVikram Hegde 	 * This is so that we can skip parsing the ACPI table
8903a634bfcSVikram Hegde 	 * if necessary because that may cause problems in
8913a634bfcSVikram Hegde 	 * systems with buggy BIOS or ACPI tables
8923a634bfcSVikram Hegde 	 */
8933a634bfcSVikram Hegde 	if (immu_enable == B_FALSE) {
8943a634bfcSVikram Hegde 		return;
8953a634bfcSVikram Hegde 	}
8963a634bfcSVikram Hegde 
8973a634bfcSVikram Hegde 	/*
8983a634bfcSVikram Hegde 	 * Next, check if the system even has an Intel IOMMU
8993a634bfcSVikram Hegde 	 * We use the presence or absence of the IOMMU ACPI
9003a634bfcSVikram Hegde 	 * table to detect Intel IOMMU.
9013a634bfcSVikram Hegde 	 */
9023a634bfcSVikram Hegde 	if (immu_dmar_setup() != DDI_SUCCESS) {
9033a634bfcSVikram Hegde 		immu_enable = B_FALSE;
9043a634bfcSVikram Hegde 		return;
9053a634bfcSVikram Hegde 	}
9063a634bfcSVikram Hegde 
9073a634bfcSVikram Hegde 	/*
9083a634bfcSVikram Hegde 	 * Check blacklists
9093a634bfcSVikram Hegde 	 */
9103a634bfcSVikram Hegde 	blacklist_setup();
9113a634bfcSVikram Hegde 
9123a634bfcSVikram Hegde 	if (blacklisted_smbios() == B_TRUE) {
9133a634bfcSVikram Hegde 		blacklist_destroy();
9143a634bfcSVikram Hegde 		immu_enable = B_FALSE;
9153a634bfcSVikram Hegde 		return;
9163a634bfcSVikram Hegde 	}
9173a634bfcSVikram Hegde 
9183a634bfcSVikram Hegde 	if (blacklisted_driver() == B_TRUE) {
9193a634bfcSVikram Hegde 		blacklist_destroy();
9203a634bfcSVikram Hegde 		immu_enable = B_FALSE;
9213a634bfcSVikram Hegde 		return;
9223a634bfcSVikram Hegde 	}
9233a634bfcSVikram Hegde 
9243a634bfcSVikram Hegde 	/*
9253a634bfcSVikram Hegde 	 * Read the "raw" DMAR ACPI table to get information
9263a634bfcSVikram Hegde 	 * and convert into a form we can use.
9273a634bfcSVikram Hegde 	 */
9283a634bfcSVikram Hegde 	if (immu_dmar_parse() != DDI_SUCCESS) {
9293a634bfcSVikram Hegde 		blacklist_destroy();
9303a634bfcSVikram Hegde 		immu_enable = B_FALSE;
9313a634bfcSVikram Hegde 		return;
9323a634bfcSVikram Hegde 	}
9333a634bfcSVikram Hegde 
9343a634bfcSVikram Hegde 	/*
9353a634bfcSVikram Hegde 	 * now that we have processed the ACPI table
9363a634bfcSVikram Hegde 	 * check if we need to blacklist this system
9373a634bfcSVikram Hegde 	 * based on ACPI info
9383a634bfcSVikram Hegde 	 */
9393a634bfcSVikram Hegde 	if (blacklisted_acpi() == B_TRUE) {
9403a634bfcSVikram Hegde 		immu_dmar_destroy();
9413a634bfcSVikram Hegde 		blacklist_destroy();
9423a634bfcSVikram Hegde 		immu_enable = B_FALSE;
9433a634bfcSVikram Hegde 		return;
9443a634bfcSVikram Hegde 	}
9453a634bfcSVikram Hegde 
9463a634bfcSVikram Hegde 	blacklist_destroy();
9473a634bfcSVikram Hegde 
9483a634bfcSVikram Hegde 	/*
9493a634bfcSVikram Hegde 	 * Check if system has HW quirks.
9503a634bfcSVikram Hegde 	 */
9513a634bfcSVikram Hegde 	pre_setup_quirks();
9523a634bfcSVikram Hegde 
9533a634bfcSVikram Hegde 	/* Now do the rest of the setup */
9543a634bfcSVikram Hegde 	immu_subsystems_setup();
9553a634bfcSVikram Hegde 
9563a634bfcSVikram Hegde 	/*
9573a634bfcSVikram Hegde 	 * Now that the IMMU is setup, create a phony
9583a634bfcSVikram Hegde 	 * reg prop so that suspend/resume works
9593a634bfcSVikram Hegde 	 */
9603a634bfcSVikram Hegde 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg",
9613a634bfcSVikram Hegde 	    (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) {
9623a634bfcSVikram Hegde 		ddi_err(DER_PANIC, NULL, "Failed to create reg prop for "
9633a634bfcSVikram Hegde 		    "rootnex node");
9643a634bfcSVikram Hegde 		/*NOTREACHED*/
9653a634bfcSVikram Hegde 	}
9663a634bfcSVikram Hegde 
9673a634bfcSVikram Hegde 	immu_setup = B_TRUE;
9683a634bfcSVikram Hegde }
9693a634bfcSVikram Hegde 
9703a634bfcSVikram Hegde /*
9713a634bfcSVikram Hegde  * immu_startup()
9723a634bfcSVikram Hegde  * 	called directly by boot code to startup
9733a634bfcSVikram Hegde  *      all units of the IOMMU
9743a634bfcSVikram Hegde  */
9753a634bfcSVikram Hegde void
9763a634bfcSVikram Hegde immu_startup(void)
9773a634bfcSVikram Hegde {
9783a634bfcSVikram Hegde 	/*
9793a634bfcSVikram Hegde 	 * If IOMMU is disabled, do nothing
9803a634bfcSVikram Hegde 	 */
9813a634bfcSVikram Hegde 	if (immu_enable == B_FALSE) {
9823a634bfcSVikram Hegde 		return;
9833a634bfcSVikram Hegde 	}
9843a634bfcSVikram Hegde 
9853a634bfcSVikram Hegde 	if (immu_setup == B_FALSE) {
9863a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, "
9873a634bfcSVikram Hegde 		    "skipping IOMU startup");
9883a634bfcSVikram Hegde 		return;
9893a634bfcSVikram Hegde 	}
9903a634bfcSVikram Hegde 
9913a634bfcSVikram Hegde 	pre_startup_quirks();
9923a634bfcSVikram Hegde 
9933a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL,
9943a634bfcSVikram Hegde 	    "?Starting Intel IOMMU (dmar) units...\n");
9953a634bfcSVikram Hegde 
9963a634bfcSVikram Hegde 	immu_subsystems_startup();
9973a634bfcSVikram Hegde 
9983a634bfcSVikram Hegde 	immu_running = B_TRUE;
9993a634bfcSVikram Hegde }
10003a634bfcSVikram Hegde 
10013a634bfcSVikram Hegde /*
10023a634bfcSVikram Hegde  * immu_map_sgl()
10033a634bfcSVikram Hegde  * 	called from rootnex_coredma_bindhdl() when Intel
10043a634bfcSVikram Hegde  *	IOMMU is enabled to build DVMA cookies and map them.
10053a634bfcSVikram Hegde  */
10063a634bfcSVikram Hegde int
10073a634bfcSVikram Hegde immu_map_sgl(ddi_dma_impl_t *hp, struct ddi_dma_req *dmareq,
10083a634bfcSVikram Hegde     int prealloc_count, dev_info_t *rdip)
10093a634bfcSVikram Hegde {
10103a634bfcSVikram Hegde 	if (immu_running == B_FALSE) {
10113a634bfcSVikram Hegde 		return (DDI_DMA_USE_PHYSICAL);
10123a634bfcSVikram Hegde 	}
10133a634bfcSVikram Hegde 
10143a634bfcSVikram Hegde 	return (immu_dvma_map(hp, dmareq, NULL, prealloc_count, rdip,
10153a634bfcSVikram Hegde 	    IMMU_FLAGS_DMAHDL));
10163a634bfcSVikram Hegde }
10173a634bfcSVikram Hegde 
10183a634bfcSVikram Hegde /*
10193a634bfcSVikram Hegde  * immu_unmap_sgl()
10203a634bfcSVikram Hegde  * 	called from rootnex_coredma_unbindhdl(), to unmap DVMA
10213a634bfcSVikram Hegde  * 	cookies and free them
10223a634bfcSVikram Hegde  */
10233a634bfcSVikram Hegde int
10243a634bfcSVikram Hegde immu_unmap_sgl(ddi_dma_impl_t *hp, dev_info_t *rdip)
10253a634bfcSVikram Hegde {
10263a634bfcSVikram Hegde 	if (immu_running == B_FALSE) {
10273a634bfcSVikram Hegde 		return (DDI_DMA_USE_PHYSICAL);
10283a634bfcSVikram Hegde 	}
10293a634bfcSVikram Hegde 
10303a634bfcSVikram Hegde 	return (immu_dvma_unmap(hp, rdip));
10313a634bfcSVikram Hegde }
10323a634bfcSVikram Hegde 
10333a634bfcSVikram Hegde /*
10343a634bfcSVikram Hegde  * Hook to notify IOMMU code of device tree changes
10353a634bfcSVikram Hegde  */
10363a634bfcSVikram Hegde void
10373a634bfcSVikram Hegde immu_device_tree_changed(void)
10383a634bfcSVikram Hegde {
10393a634bfcSVikram Hegde 	if (immu_setup == B_FALSE) {
10403a634bfcSVikram Hegde 		return;
10413a634bfcSVikram Hegde 	}
10423a634bfcSVikram Hegde 
10433a634bfcSVikram Hegde 	ddi_err(DER_WARN, NULL, "Intel IOMMU currently "
10443a634bfcSVikram Hegde 	    "does not use device tree updates");
10453a634bfcSVikram Hegde }
10463a634bfcSVikram Hegde 
10473a634bfcSVikram Hegde /*
10483a634bfcSVikram Hegde  * Hook to notify IOMMU code of memory changes
10493a634bfcSVikram Hegde  */
10503a634bfcSVikram Hegde void
10513a634bfcSVikram Hegde immu_physmem_update(uint64_t addr, uint64_t size)
10523a634bfcSVikram Hegde {
10533a634bfcSVikram Hegde 	if (immu_setup == B_FALSE) {
10543a634bfcSVikram Hegde 		return;
10553a634bfcSVikram Hegde 	}
10563a634bfcSVikram Hegde 	immu_dvma_physmem_update(addr, size);
10573a634bfcSVikram Hegde }
10583a634bfcSVikram Hegde 
10593a634bfcSVikram Hegde /*
10603a634bfcSVikram Hegde  * immu_quiesce()
10613a634bfcSVikram Hegde  * 	quiesce all units that are running
10623a634bfcSVikram Hegde  */
10633a634bfcSVikram Hegde int
10643a634bfcSVikram Hegde immu_quiesce(void)
10653a634bfcSVikram Hegde {
10663a634bfcSVikram Hegde 	immu_t *immu;
10673a634bfcSVikram Hegde 	int ret = DDI_SUCCESS;
10683a634bfcSVikram Hegde 
10693a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
10703a634bfcSVikram Hegde 
10713a634bfcSVikram Hegde 	if (immu_running == B_FALSE)
10723a634bfcSVikram Hegde 		return (DDI_SUCCESS);
10733a634bfcSVikram Hegde 
10743a634bfcSVikram Hegde 	ASSERT(immu_setup == B_TRUE);
10753a634bfcSVikram Hegde 
10763a634bfcSVikram Hegde 	immu = list_head(&immu_list);
10773a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
10783a634bfcSVikram Hegde 
10793a634bfcSVikram Hegde 		/* if immu is not running, we dont quiesce */
10803a634bfcSVikram Hegde 		if (immu->immu_regs_running == B_FALSE)
10813a634bfcSVikram Hegde 			continue;
10823a634bfcSVikram Hegde 
10833a634bfcSVikram Hegde 		/* flush caches */
10843a634bfcSVikram Hegde 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
10853a634bfcSVikram Hegde 		immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL);
10863a634bfcSVikram Hegde 		rw_exit(&(immu->immu_ctx_rwlock));
10873a634bfcSVikram Hegde 		immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL);
10883a634bfcSVikram Hegde 		immu_regs_wbf_flush(immu);
10893a634bfcSVikram Hegde 
10903a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
10913a634bfcSVikram Hegde 
10923a634bfcSVikram Hegde 		/*
10933a634bfcSVikram Hegde 		 * Set IOMMU unit's regs to do
10943a634bfcSVikram Hegde 		 * the actual shutdown.
10953a634bfcSVikram Hegde 		 */
10963a634bfcSVikram Hegde 		immu_regs_shutdown(immu);
10973a634bfcSVikram Hegde 		immu_regs_suspend(immu);
10983a634bfcSVikram Hegde 
10993a634bfcSVikram Hegde 		/* if immu is still running, we failed */
11003a634bfcSVikram Hegde 		if (immu->immu_regs_running == B_TRUE)
11013a634bfcSVikram Hegde 			ret = DDI_FAILURE;
11023a634bfcSVikram Hegde 		else
11033a634bfcSVikram Hegde 			immu->immu_regs_quiesced = B_TRUE;
11043a634bfcSVikram Hegde 
11053a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
11063a634bfcSVikram Hegde 	}
11073a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
11083a634bfcSVikram Hegde 
11093a634bfcSVikram Hegde 	if (ret == DDI_SUCCESS) {
11103a634bfcSVikram Hegde 		immu_running = B_FALSE;
11113a634bfcSVikram Hegde 		immu_quiesced = B_TRUE;
11123a634bfcSVikram Hegde 	}
11133a634bfcSVikram Hegde 
11143a634bfcSVikram Hegde 	return (ret);
11153a634bfcSVikram Hegde }
11163a634bfcSVikram Hegde 
11173a634bfcSVikram Hegde /*
11183a634bfcSVikram Hegde  * immu_unquiesce()
11193a634bfcSVikram Hegde  * 	unquiesce all units
11203a634bfcSVikram Hegde  */
11213a634bfcSVikram Hegde int
11223a634bfcSVikram Hegde immu_unquiesce(void)
11233a634bfcSVikram Hegde {
11243a634bfcSVikram Hegde 	immu_t *immu;
11253a634bfcSVikram Hegde 	int ret = DDI_SUCCESS;
11263a634bfcSVikram Hegde 
11273a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
11283a634bfcSVikram Hegde 
11293a634bfcSVikram Hegde 	if (immu_quiesced == B_FALSE)
11303a634bfcSVikram Hegde 		return (DDI_SUCCESS);
11313a634bfcSVikram Hegde 
11323a634bfcSVikram Hegde 	ASSERT(immu_setup == B_TRUE);
11333a634bfcSVikram Hegde 	ASSERT(immu_running == B_FALSE);
11343a634bfcSVikram Hegde 
11353a634bfcSVikram Hegde 	immu = list_head(&immu_list);
11363a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
11373a634bfcSVikram Hegde 
11383a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
11393a634bfcSVikram Hegde 
11403a634bfcSVikram Hegde 		/* if immu was not quiesced, i.e was not running before */
1141e03dceedSVikram Hegde 		if (immu->immu_regs_quiesced == B_FALSE) {
1142e03dceedSVikram Hegde 			mutex_exit(&(immu->immu_lock));
11433a634bfcSVikram Hegde 			continue;
1144e03dceedSVikram Hegde 		}
11453a634bfcSVikram Hegde 
11463a634bfcSVikram Hegde 		if (immu_regs_resume(immu) != DDI_SUCCESS) {
11473a634bfcSVikram Hegde 			ret = DDI_FAILURE;
1148e03dceedSVikram Hegde 			mutex_exit(&(immu->immu_lock));
11493a634bfcSVikram Hegde 			continue;
11503a634bfcSVikram Hegde 		}
11513a634bfcSVikram Hegde 
11523a634bfcSVikram Hegde 		/* flush caches before unquiesce */
11533a634bfcSVikram Hegde 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
11543a634bfcSVikram Hegde 		immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL);
11553a634bfcSVikram Hegde 		rw_exit(&(immu->immu_ctx_rwlock));
11563a634bfcSVikram Hegde 		immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL);
11573a634bfcSVikram Hegde 
11583a634bfcSVikram Hegde 		/*
11593a634bfcSVikram Hegde 		 * Set IOMMU unit's regs to do
11603a634bfcSVikram Hegde 		 * the actual startup. This will
11613a634bfcSVikram Hegde 		 * set immu->immu_regs_running  field
11623a634bfcSVikram Hegde 		 * if the unit is successfully
11633a634bfcSVikram Hegde 		 * started
11643a634bfcSVikram Hegde 		 */
11653a634bfcSVikram Hegde 		immu_regs_startup(immu);
11663a634bfcSVikram Hegde 
11673a634bfcSVikram Hegde 		if (immu->immu_regs_running == B_FALSE) {
11683a634bfcSVikram Hegde 			ret = DDI_FAILURE;
11693a634bfcSVikram Hegde 		} else {
11703a634bfcSVikram Hegde 			immu_quiesced = B_TRUE;
11713a634bfcSVikram Hegde 			immu_running = B_TRUE;
11723a634bfcSVikram Hegde 			immu->immu_regs_quiesced = B_FALSE;
11733a634bfcSVikram Hegde 		}
11743a634bfcSVikram Hegde 
11753a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
11763a634bfcSVikram Hegde 	}
11773a634bfcSVikram Hegde 
11783a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
11793a634bfcSVikram Hegde 
11803a634bfcSVikram Hegde 	return (ret);
11813a634bfcSVikram Hegde }
11823a634bfcSVikram Hegde 
11833a634bfcSVikram Hegde /* ##############  END Intel IOMMU entry points ################## */
1184