xref: /onnv-gate/usr/src/uts/i86pc/io/immu.c (revision 11600:651a9a4f7b5f)
1*11600SVikram.Hegde@Sun.COM /*
2*11600SVikram.Hegde@Sun.COM  * CDDL HEADER START
3*11600SVikram.Hegde@Sun.COM  *
4*11600SVikram.Hegde@Sun.COM  * The contents of this file are subject to the terms of the
5*11600SVikram.Hegde@Sun.COM  * Common Development and Distribution License (the "License").
6*11600SVikram.Hegde@Sun.COM  * You may not use this file except in compliance with the License.
7*11600SVikram.Hegde@Sun.COM  *
8*11600SVikram.Hegde@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*11600SVikram.Hegde@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*11600SVikram.Hegde@Sun.COM  * See the License for the specific language governing permissions
11*11600SVikram.Hegde@Sun.COM  * and limitations under the License.
12*11600SVikram.Hegde@Sun.COM  *
13*11600SVikram.Hegde@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*11600SVikram.Hegde@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*11600SVikram.Hegde@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*11600SVikram.Hegde@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*11600SVikram.Hegde@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*11600SVikram.Hegde@Sun.COM  *
19*11600SVikram.Hegde@Sun.COM  * CDDL HEADER END
20*11600SVikram.Hegde@Sun.COM  */
21*11600SVikram.Hegde@Sun.COM /*
22*11600SVikram.Hegde@Sun.COM  * Portions Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23*11600SVikram.Hegde@Sun.COM  * Use is subject to license terms.
24*11600SVikram.Hegde@Sun.COM  */
25*11600SVikram.Hegde@Sun.COM /*
26*11600SVikram.Hegde@Sun.COM  * Copyright (c) 2009, Intel Corporation.
27*11600SVikram.Hegde@Sun.COM  * All rights reserved.
28*11600SVikram.Hegde@Sun.COM  */
29*11600SVikram.Hegde@Sun.COM 
30*11600SVikram.Hegde@Sun.COM /*
31*11600SVikram.Hegde@Sun.COM  * Intel IOMMU implementation
32*11600SVikram.Hegde@Sun.COM  * This file contains Intel IOMMU code exported
33*11600SVikram.Hegde@Sun.COM  * to the rest of the system and code that deals
34*11600SVikram.Hegde@Sun.COM  * with the Intel IOMMU as a whole.
35*11600SVikram.Hegde@Sun.COM  */
36*11600SVikram.Hegde@Sun.COM 
37*11600SVikram.Hegde@Sun.COM #include <sys/conf.h>
38*11600SVikram.Hegde@Sun.COM #include <sys/modctl.h>
39*11600SVikram.Hegde@Sun.COM #include <sys/pci.h>
40*11600SVikram.Hegde@Sun.COM #include <sys/pci_impl.h>
41*11600SVikram.Hegde@Sun.COM #include <sys/sysmacros.h>
42*11600SVikram.Hegde@Sun.COM #include <sys/ddi.h>
43*11600SVikram.Hegde@Sun.COM #include <sys/ddidmareq.h>
44*11600SVikram.Hegde@Sun.COM #include <sys/ddi_impldefs.h>
45*11600SVikram.Hegde@Sun.COM #include <sys/ddifm.h>
46*11600SVikram.Hegde@Sun.COM #include <sys/sunndi.h>
47*11600SVikram.Hegde@Sun.COM #include <sys/debug.h>
48*11600SVikram.Hegde@Sun.COM #include <sys/fm/protocol.h>
49*11600SVikram.Hegde@Sun.COM #include <sys/note.h>
50*11600SVikram.Hegde@Sun.COM #include <sys/apic.h>
51*11600SVikram.Hegde@Sun.COM #include <vm/hat_i86.h>
52*11600SVikram.Hegde@Sun.COM #include <sys/smp_impldefs.h>
53*11600SVikram.Hegde@Sun.COM #include <sys/spl.h>
54*11600SVikram.Hegde@Sun.COM #include <sys/archsystm.h>
55*11600SVikram.Hegde@Sun.COM #include <sys/x86_archext.h>
56*11600SVikram.Hegde@Sun.COM #include <sys/rootnex.h>
57*11600SVikram.Hegde@Sun.COM #include <sys/avl.h>
58*11600SVikram.Hegde@Sun.COM #include <sys/bootconf.h>
59*11600SVikram.Hegde@Sun.COM #include <sys/bootinfo.h>
60*11600SVikram.Hegde@Sun.COM #include <sys/atomic.h>
61*11600SVikram.Hegde@Sun.COM #include <sys/immu.h>
62*11600SVikram.Hegde@Sun.COM 
63*11600SVikram.Hegde@Sun.COM /* ########################### Globals and tunables ######################## */
64*11600SVikram.Hegde@Sun.COM /*
65*11600SVikram.Hegde@Sun.COM  * Global switches (boolean) that can be toggled either via boot options
66*11600SVikram.Hegde@Sun.COM  * or via /etc/system or kmdb
67*11600SVikram.Hegde@Sun.COM  */
68*11600SVikram.Hegde@Sun.COM 
69*11600SVikram.Hegde@Sun.COM /* Various features */
70*11600SVikram.Hegde@Sun.COM boolean_t immu_enable = B_TRUE;
71*11600SVikram.Hegde@Sun.COM boolean_t immu_dvma_enable = B_TRUE;
72*11600SVikram.Hegde@Sun.COM 
73*11600SVikram.Hegde@Sun.COM /* accessed in other files so not static */
74*11600SVikram.Hegde@Sun.COM boolean_t immu_gfxdvma_enable = B_TRUE;
75*11600SVikram.Hegde@Sun.COM boolean_t immu_intrmap_enable = B_FALSE;
76*11600SVikram.Hegde@Sun.COM boolean_t immu_qinv_enable = B_FALSE;
77*11600SVikram.Hegde@Sun.COM 
78*11600SVikram.Hegde@Sun.COM /* various quirks that need working around */
79*11600SVikram.Hegde@Sun.COM 
80*11600SVikram.Hegde@Sun.COM /* XXX We always map page 0 read/write for now */
81*11600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbpage0 = B_TRUE;
82*11600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbrmrr = B_TRUE;
83*11600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbfullpa;
84*11600SVikram.Hegde@Sun.COM boolean_t immu_quirk_mobile4;
85*11600SVikram.Hegde@Sun.COM 
86*11600SVikram.Hegde@Sun.COM boolean_t immu_mmio_safe = B_TRUE;
87*11600SVikram.Hegde@Sun.COM 
88*11600SVikram.Hegde@Sun.COM /* debug messages */
89*11600SVikram.Hegde@Sun.COM boolean_t immu_dmar_print;
90*11600SVikram.Hegde@Sun.COM 
91*11600SVikram.Hegde@Sun.COM /* ############  END OPTIONS section ################ */
92*11600SVikram.Hegde@Sun.COM 
93*11600SVikram.Hegde@Sun.COM /*
94*11600SVikram.Hegde@Sun.COM  * Global used internally by Intel IOMMU code
95*11600SVikram.Hegde@Sun.COM  */
96*11600SVikram.Hegde@Sun.COM dev_info_t *root_devinfo;
97*11600SVikram.Hegde@Sun.COM kmutex_t immu_lock;
98*11600SVikram.Hegde@Sun.COM list_t immu_list;
99*11600SVikram.Hegde@Sun.COM boolean_t immu_setup;
100*11600SVikram.Hegde@Sun.COM boolean_t immu_running;
101*11600SVikram.Hegde@Sun.COM boolean_t immu_quiesced;
102*11600SVikram.Hegde@Sun.COM 
103*11600SVikram.Hegde@Sun.COM /* ######################## END Globals and tunables ###################### */
104*11600SVikram.Hegde@Sun.COM /* Globals used only in this file */
105*11600SVikram.Hegde@Sun.COM static char **black_array;
106*11600SVikram.Hegde@Sun.COM static uint_t nblacks;
107*11600SVikram.Hegde@Sun.COM /* ###################### Utility routines ############################# */
108*11600SVikram.Hegde@Sun.COM 
109*11600SVikram.Hegde@Sun.COM /*
110*11600SVikram.Hegde@Sun.COM  * Check if the device has mobile 4 chipset
111*11600SVikram.Hegde@Sun.COM  */
112*11600SVikram.Hegde@Sun.COM static int
113*11600SVikram.Hegde@Sun.COM check_mobile4(dev_info_t *dip, void *arg)
114*11600SVikram.Hegde@Sun.COM {
115*11600SVikram.Hegde@Sun.COM 	_NOTE(ARGUNUSED(arg));
116*11600SVikram.Hegde@Sun.COM 	int vendor, device;
117*11600SVikram.Hegde@Sun.COM 	int *ip = (int *)arg;
118*11600SVikram.Hegde@Sun.COM 
119*11600SVikram.Hegde@Sun.COM 	ASSERT(arg);
120*11600SVikram.Hegde@Sun.COM 
121*11600SVikram.Hegde@Sun.COM 	vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
122*11600SVikram.Hegde@Sun.COM 	    "vendor-id", -1);
123*11600SVikram.Hegde@Sun.COM 	device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
124*11600SVikram.Hegde@Sun.COM 	    "device-id", -1);
125*11600SVikram.Hegde@Sun.COM 
126*11600SVikram.Hegde@Sun.COM 	if (vendor == 0x8086 && device == 0x2a40) {
127*11600SVikram.Hegde@Sun.COM 		*ip = B_TRUE;
128*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_NOTE, dip, "IMMU: Mobile 4 chipset detected. "
129*11600SVikram.Hegde@Sun.COM 		    "Force setting IOMMU write buffer");
130*11600SVikram.Hegde@Sun.COM 		return (DDI_WALK_TERMINATE);
131*11600SVikram.Hegde@Sun.COM 	} else {
132*11600SVikram.Hegde@Sun.COM 		return (DDI_WALK_CONTINUE);
133*11600SVikram.Hegde@Sun.COM 	}
134*11600SVikram.Hegde@Sun.COM }
135*11600SVikram.Hegde@Sun.COM 
136*11600SVikram.Hegde@Sun.COM static void
137*11600SVikram.Hegde@Sun.COM map_bios_rsvd_mem(dev_info_t *dip)
138*11600SVikram.Hegde@Sun.COM {
139*11600SVikram.Hegde@Sun.COM 	struct memlist *mp;
140*11600SVikram.Hegde@Sun.COM 	int e;
141*11600SVikram.Hegde@Sun.COM 
142*11600SVikram.Hegde@Sun.COM 	memlist_read_lock();
143*11600SVikram.Hegde@Sun.COM 
144*11600SVikram.Hegde@Sun.COM 	mp = bios_rsvd;
145*11600SVikram.Hegde@Sun.COM 	while (mp != NULL) {
146*11600SVikram.Hegde@Sun.COM 		memrng_t *mrng = {0};
147*11600SVikram.Hegde@Sun.COM 
148*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "IMMU: Mapping BIOS rsvd range "
149*11600SVikram.Hegde@Sun.COM 		    "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address,
150*11600SVikram.Hegde@Sun.COM 		    mp->ml_address + mp->ml_size);
151*11600SVikram.Hegde@Sun.COM 
152*11600SVikram.Hegde@Sun.COM 		mrng->mrng_start = IMMU_ROUNDOWN(mp->ml_address);
153*11600SVikram.Hegde@Sun.COM 		mrng->mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE;
154*11600SVikram.Hegde@Sun.COM 
155*11600SVikram.Hegde@Sun.COM 		e = immu_dvma_map(NULL, NULL, mrng, 0, dip, IMMU_FLAGS_MEMRNG);
156*11600SVikram.Hegde@Sun.COM 		ASSERT(e == DDI_DMA_MAPPED || e == DDI_DMA_USE_PHYSICAL);
157*11600SVikram.Hegde@Sun.COM 
158*11600SVikram.Hegde@Sun.COM 		mp = mp->ml_next;
159*11600SVikram.Hegde@Sun.COM 	}
160*11600SVikram.Hegde@Sun.COM 
161*11600SVikram.Hegde@Sun.COM 	memlist_read_unlock();
162*11600SVikram.Hegde@Sun.COM }
163*11600SVikram.Hegde@Sun.COM 
164*11600SVikram.Hegde@Sun.COM /*
165*11600SVikram.Hegde@Sun.COM  * Check if the device is USB controller
166*11600SVikram.Hegde@Sun.COM  */
167*11600SVikram.Hegde@Sun.COM /*ARGSUSED*/
168*11600SVikram.Hegde@Sun.COM static void
169*11600SVikram.Hegde@Sun.COM check_usb(dev_info_t *dip, void *arg)
170*11600SVikram.Hegde@Sun.COM {
171*11600SVikram.Hegde@Sun.COM 	const char *drv = ddi_driver_name(dip);
172*11600SVikram.Hegde@Sun.COM 
173*11600SVikram.Hegde@Sun.COM 	if (drv == NULL ||
174*11600SVikram.Hegde@Sun.COM 	    (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 &&
175*11600SVikram.Hegde@Sun.COM 	    strcmp(drv, "ehci") != 0)) {
176*11600SVikram.Hegde@Sun.COM 		return;
177*11600SVikram.Hegde@Sun.COM 	}
178*11600SVikram.Hegde@Sun.COM 
179*11600SVikram.Hegde@Sun.COM 	/* This must come first since it does unity mapping */
180*11600SVikram.Hegde@Sun.COM 	if (immu_quirk_usbfullpa == B_TRUE) {
181*11600SVikram.Hegde@Sun.COM 		int e;
182*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_NOTE, dip, "Applying USB FULL PA quirk");
183*11600SVikram.Hegde@Sun.COM 		e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY);
184*11600SVikram.Hegde@Sun.COM 		/* for unity mode, map will return USE_PHYSICAL */
185*11600SVikram.Hegde@Sun.COM 		ASSERT(e == DDI_DMA_USE_PHYSICAL);
186*11600SVikram.Hegde@Sun.COM 	}
187*11600SVikram.Hegde@Sun.COM 
188*11600SVikram.Hegde@Sun.COM 	if (immu_quirk_usbrmrr == B_TRUE) {
189*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "Applying USB RMRR quirk");
190*11600SVikram.Hegde@Sun.COM 		map_bios_rsvd_mem(dip);
191*11600SVikram.Hegde@Sun.COM 	}
192*11600SVikram.Hegde@Sun.COM }
193*11600SVikram.Hegde@Sun.COM 
194*11600SVikram.Hegde@Sun.COM /*
195*11600SVikram.Hegde@Sun.COM  * Check if the device is a LPC device
196*11600SVikram.Hegde@Sun.COM  */
197*11600SVikram.Hegde@Sun.COM /*ARGSUSED*/
198*11600SVikram.Hegde@Sun.COM static void
199*11600SVikram.Hegde@Sun.COM check_lpc(dev_info_t *dip, void *arg)
200*11600SVikram.Hegde@Sun.COM {
201*11600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
202*11600SVikram.Hegde@Sun.COM 
203*11600SVikram.Hegde@Sun.COM 	immu_devi = immu_devi_get(dip);
204*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_devi);
205*11600SVikram.Hegde@Sun.COM 	if (immu_devi->imd_lpc == B_TRUE) {
206*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "IMMU: Found LPC device");
207*11600SVikram.Hegde@Sun.COM 		/* This will put the immu_devi on the LPC "specials" list */
208*11600SVikram.Hegde@Sun.COM 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
209*11600SVikram.Hegde@Sun.COM 	}
210*11600SVikram.Hegde@Sun.COM }
211*11600SVikram.Hegde@Sun.COM 
212*11600SVikram.Hegde@Sun.COM /*
213*11600SVikram.Hegde@Sun.COM  * Check if the device is a GFX device
214*11600SVikram.Hegde@Sun.COM  */
215*11600SVikram.Hegde@Sun.COM /*ARGSUSED*/
216*11600SVikram.Hegde@Sun.COM static void
217*11600SVikram.Hegde@Sun.COM check_gfx(dev_info_t *dip, void *arg)
218*11600SVikram.Hegde@Sun.COM {
219*11600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
220*11600SVikram.Hegde@Sun.COM 	int e;
221*11600SVikram.Hegde@Sun.COM 
222*11600SVikram.Hegde@Sun.COM 	immu_devi = immu_devi_get(dip);
223*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_devi);
224*11600SVikram.Hegde@Sun.COM 	if (immu_devi->imd_display == B_TRUE) {
225*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "IMMU: Found GFX device");
226*11600SVikram.Hegde@Sun.COM 		/* This will put the immu_devi on the GFX "specials" list */
227*11600SVikram.Hegde@Sun.COM 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
228*11600SVikram.Hegde@Sun.COM 		e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY);
229*11600SVikram.Hegde@Sun.COM 		/* for unity mode, map will return USE_PHYSICAL */
230*11600SVikram.Hegde@Sun.COM 		ASSERT(e == DDI_DMA_USE_PHYSICAL);
231*11600SVikram.Hegde@Sun.COM 	}
232*11600SVikram.Hegde@Sun.COM }
233*11600SVikram.Hegde@Sun.COM 
234*11600SVikram.Hegde@Sun.COM static void
235*11600SVikram.Hegde@Sun.COM walk_tree(int (*f)(dev_info_t *, void *), void *arg)
236*11600SVikram.Hegde@Sun.COM {
237*11600SVikram.Hegde@Sun.COM 	int count;
238*11600SVikram.Hegde@Sun.COM 
239*11600SVikram.Hegde@Sun.COM 	ndi_devi_enter(root_devinfo, &count);
240*11600SVikram.Hegde@Sun.COM 	ddi_walk_devs(ddi_get_child(root_devinfo), f, arg);
241*11600SVikram.Hegde@Sun.COM 	ndi_devi_exit(root_devinfo, count);
242*11600SVikram.Hegde@Sun.COM }
243*11600SVikram.Hegde@Sun.COM 
244*11600SVikram.Hegde@Sun.COM static int
245*11600SVikram.Hegde@Sun.COM check_pre_setup_quirks(dev_info_t *dip, void *arg)
246*11600SVikram.Hegde@Sun.COM {
247*11600SVikram.Hegde@Sun.COM 	/* just 1 check right now */
248*11600SVikram.Hegde@Sun.COM 	return (check_mobile4(dip, arg));
249*11600SVikram.Hegde@Sun.COM }
250*11600SVikram.Hegde@Sun.COM 
251*11600SVikram.Hegde@Sun.COM static int
252*11600SVikram.Hegde@Sun.COM check_pre_startup_quirks(dev_info_t *dip, void *arg)
253*11600SVikram.Hegde@Sun.COM {
254*11600SVikram.Hegde@Sun.COM 	if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) {
255*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_PANIC, dip, "Failed to get immu_devi");
256*11600SVikram.Hegde@Sun.COM 	}
257*11600SVikram.Hegde@Sun.COM 
258*11600SVikram.Hegde@Sun.COM 	check_gfx(dip, arg);
259*11600SVikram.Hegde@Sun.COM 
260*11600SVikram.Hegde@Sun.COM 	check_lpc(dip, arg);
261*11600SVikram.Hegde@Sun.COM 
262*11600SVikram.Hegde@Sun.COM 	check_usb(dip, arg);
263*11600SVikram.Hegde@Sun.COM 
264*11600SVikram.Hegde@Sun.COM 	return (DDI_WALK_CONTINUE);
265*11600SVikram.Hegde@Sun.COM }
266*11600SVikram.Hegde@Sun.COM 
267*11600SVikram.Hegde@Sun.COM static void
268*11600SVikram.Hegde@Sun.COM pre_setup_quirks(void)
269*11600SVikram.Hegde@Sun.COM {
270*11600SVikram.Hegde@Sun.COM 	walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4);
271*11600SVikram.Hegde@Sun.COM }
272*11600SVikram.Hegde@Sun.COM 
273*11600SVikram.Hegde@Sun.COM static void
274*11600SVikram.Hegde@Sun.COM pre_startup_quirks(void)
275*11600SVikram.Hegde@Sun.COM {
276*11600SVikram.Hegde@Sun.COM 	walk_tree(check_pre_startup_quirks, NULL);
277*11600SVikram.Hegde@Sun.COM 
278*11600SVikram.Hegde@Sun.COM 	immu_dmar_rmrr_map();
279*11600SVikram.Hegde@Sun.COM }
280*11600SVikram.Hegde@Sun.COM 
281*11600SVikram.Hegde@Sun.COM /*
282*11600SVikram.Hegde@Sun.COM  * get_bootopt()
283*11600SVikram.Hegde@Sun.COM  * 	check a boot option  (always a boolean)
284*11600SVikram.Hegde@Sun.COM  */
285*11600SVikram.Hegde@Sun.COM static void
286*11600SVikram.Hegde@Sun.COM get_bootopt(char *bopt, boolean_t *kvar)
287*11600SVikram.Hegde@Sun.COM {
288*11600SVikram.Hegde@Sun.COM 	char *val = NULL;
289*11600SVikram.Hegde@Sun.COM 
290*11600SVikram.Hegde@Sun.COM 	ASSERT(bopt);
291*11600SVikram.Hegde@Sun.COM 	ASSERT(kvar);
292*11600SVikram.Hegde@Sun.COM 
293*11600SVikram.Hegde@Sun.COM 	/*
294*11600SVikram.Hegde@Sun.COM 	 * All boot options set at the GRUB menu become
295*11600SVikram.Hegde@Sun.COM 	 * properties on the rootnex.
296*11600SVikram.Hegde@Sun.COM 	 */
297*11600SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo,
298*11600SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS, bopt, &val) == DDI_SUCCESS) {
299*11600SVikram.Hegde@Sun.COM 		ASSERT(val);
300*11600SVikram.Hegde@Sun.COM 		if (strcmp(val, "true") == 0) {
301*11600SVikram.Hegde@Sun.COM 			*kvar = B_TRUE;
302*11600SVikram.Hegde@Sun.COM 		} else if (strcmp(val, "false") == 0) {
303*11600SVikram.Hegde@Sun.COM 			*kvar = B_FALSE;
304*11600SVikram.Hegde@Sun.COM 		} else {
305*11600SVikram.Hegde@Sun.COM 			ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ",
306*11600SVikram.Hegde@Sun.COM 			    "is not set to true or false. Ignoring option.",
307*11600SVikram.Hegde@Sun.COM 			    bopt, val);
308*11600SVikram.Hegde@Sun.COM 		}
309*11600SVikram.Hegde@Sun.COM 		ddi_prop_free(val);
310*11600SVikram.Hegde@Sun.COM 	}
311*11600SVikram.Hegde@Sun.COM }
312*11600SVikram.Hegde@Sun.COM 
313*11600SVikram.Hegde@Sun.COM static void
314*11600SVikram.Hegde@Sun.COM read_boot_options(void)
315*11600SVikram.Hegde@Sun.COM {
316*11600SVikram.Hegde@Sun.COM 	/* enable/disable options */
317*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-enable", &immu_enable);
318*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-dvma-enable", &immu_dvma_enable);
319*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
320*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-intrmap-enable", &immu_intrmap_enable);
321*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-qinv-enable", &immu_qinv_enable);
322*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-mmio-safe", &immu_mmio_safe);
323*11600SVikram.Hegde@Sun.COM 
324*11600SVikram.Hegde@Sun.COM 	/* workaround switches */
325*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
326*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
327*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
328*11600SVikram.Hegde@Sun.COM 
329*11600SVikram.Hegde@Sun.COM 	/* debug printing */
330*11600SVikram.Hegde@Sun.COM 	get_bootopt("immu-dmar-print", &immu_dmar_print);
331*11600SVikram.Hegde@Sun.COM }
332*11600SVikram.Hegde@Sun.COM 
333*11600SVikram.Hegde@Sun.COM /*
334*11600SVikram.Hegde@Sun.COM  * Note, this will not catch hardware not enumerated
335*11600SVikram.Hegde@Sun.COM  * in early boot
336*11600SVikram.Hegde@Sun.COM  */
337*11600SVikram.Hegde@Sun.COM static boolean_t
338*11600SVikram.Hegde@Sun.COM blacklisted_driver(void)
339*11600SVikram.Hegde@Sun.COM {
340*11600SVikram.Hegde@Sun.COM 	char **strptr;
341*11600SVikram.Hegde@Sun.COM 	int i;
342*11600SVikram.Hegde@Sun.COM 	major_t maj;
343*11600SVikram.Hegde@Sun.COM 
344*11600SVikram.Hegde@Sun.COM 	ASSERT((black_array == NULL) ^ (nblacks != 0));
345*11600SVikram.Hegde@Sun.COM 
346*11600SVikram.Hegde@Sun.COM 	/* need at least 2 strings */
347*11600SVikram.Hegde@Sun.COM 	if (nblacks < 2) {
348*11600SVikram.Hegde@Sun.COM 		return (B_FALSE);
349*11600SVikram.Hegde@Sun.COM 	}
350*11600SVikram.Hegde@Sun.COM 
351*11600SVikram.Hegde@Sun.COM 	strptr = black_array;
352*11600SVikram.Hegde@Sun.COM 	for (i = 0; nblacks - i > 1; i++) {
353*11600SVikram.Hegde@Sun.COM 		if (strcmp(*strptr++, "DRIVER") == 0) {
354*11600SVikram.Hegde@Sun.COM 			if ((maj = ddi_name_to_major(*strptr++))
355*11600SVikram.Hegde@Sun.COM 			    != DDI_MAJOR_T_NONE) {
356*11600SVikram.Hegde@Sun.COM 				/* is there hardware bound to this drvr */
357*11600SVikram.Hegde@Sun.COM 				if (devnamesp[maj].dn_head != NULL) {
358*11600SVikram.Hegde@Sun.COM 					return (B_TRUE);
359*11600SVikram.Hegde@Sun.COM 				}
360*11600SVikram.Hegde@Sun.COM 			}
361*11600SVikram.Hegde@Sun.COM 			i += 1;   /* for loop adds 1, so add only 1 here */
362*11600SVikram.Hegde@Sun.COM 		}
363*11600SVikram.Hegde@Sun.COM 	}
364*11600SVikram.Hegde@Sun.COM 
365*11600SVikram.Hegde@Sun.COM 	return (B_FALSE);
366*11600SVikram.Hegde@Sun.COM }
367*11600SVikram.Hegde@Sun.COM 
368*11600SVikram.Hegde@Sun.COM static boolean_t
369*11600SVikram.Hegde@Sun.COM blacklisted_smbios(void)
370*11600SVikram.Hegde@Sun.COM {
371*11600SVikram.Hegde@Sun.COM 	id_t smid;
372*11600SVikram.Hegde@Sun.COM 	smbios_hdl_t *smhdl;
373*11600SVikram.Hegde@Sun.COM 	smbios_info_t sminf;
374*11600SVikram.Hegde@Sun.COM 	smbios_system_t smsys;
375*11600SVikram.Hegde@Sun.COM 	char *mfg, *product, *version;
376*11600SVikram.Hegde@Sun.COM 	char **strptr;
377*11600SVikram.Hegde@Sun.COM 	int i;
378*11600SVikram.Hegde@Sun.COM 
379*11600SVikram.Hegde@Sun.COM 	ASSERT((black_array == NULL) ^ (nblacks != 0));
380*11600SVikram.Hegde@Sun.COM 
381*11600SVikram.Hegde@Sun.COM 	/* need at least 4 strings for this setting */
382*11600SVikram.Hegde@Sun.COM 	if (nblacks < 4) {
383*11600SVikram.Hegde@Sun.COM 		return (B_FALSE);
384*11600SVikram.Hegde@Sun.COM 	}
385*11600SVikram.Hegde@Sun.COM 
386*11600SVikram.Hegde@Sun.COM 	smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
387*11600SVikram.Hegde@Sun.COM 	if (smhdl == NULL ||
388*11600SVikram.Hegde@Sun.COM 	    (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR ||
389*11600SVikram.Hegde@Sun.COM 	    smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) {
390*11600SVikram.Hegde@Sun.COM 		return (B_FALSE);
391*11600SVikram.Hegde@Sun.COM 	}
392*11600SVikram.Hegde@Sun.COM 
393*11600SVikram.Hegde@Sun.COM 	mfg = (char *)sminf.smbi_manufacturer;
394*11600SVikram.Hegde@Sun.COM 	product = (char *)sminf.smbi_product;
395*11600SVikram.Hegde@Sun.COM 	version = (char *)sminf.smbi_version;
396*11600SVikram.Hegde@Sun.COM 
397*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n");
398*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg);
399*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product);
400*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version);
401*11600SVikram.Hegde@Sun.COM 
402*11600SVikram.Hegde@Sun.COM 	strptr = black_array;
403*11600SVikram.Hegde@Sun.COM 	for (i = 0; nblacks - i > 3; i++) {
404*11600SVikram.Hegde@Sun.COM 		if (strcmp(*strptr++, "SMBIOS") == 0) {
405*11600SVikram.Hegde@Sun.COM 			if (strcmp(*strptr++, mfg) == 0 &&
406*11600SVikram.Hegde@Sun.COM 			    ((char *)strptr == '\0' ||
407*11600SVikram.Hegde@Sun.COM 			    strcmp(*strptr++, product) == 0) &&
408*11600SVikram.Hegde@Sun.COM 			    ((char *)strptr == '\0' ||
409*11600SVikram.Hegde@Sun.COM 			    strcmp(*strptr++, version) == 0)) {
410*11600SVikram.Hegde@Sun.COM 				return (B_TRUE);
411*11600SVikram.Hegde@Sun.COM 			}
412*11600SVikram.Hegde@Sun.COM 			i += 3;
413*11600SVikram.Hegde@Sun.COM 		}
414*11600SVikram.Hegde@Sun.COM 	}
415*11600SVikram.Hegde@Sun.COM 
416*11600SVikram.Hegde@Sun.COM 	return (B_FALSE);
417*11600SVikram.Hegde@Sun.COM }
418*11600SVikram.Hegde@Sun.COM 
419*11600SVikram.Hegde@Sun.COM static boolean_t
420*11600SVikram.Hegde@Sun.COM blacklisted_acpi(void)
421*11600SVikram.Hegde@Sun.COM {
422*11600SVikram.Hegde@Sun.COM 	ASSERT((black_array == NULL) ^ (nblacks != 0));
423*11600SVikram.Hegde@Sun.COM 	if (nblacks == 0) {
424*11600SVikram.Hegde@Sun.COM 		return (B_FALSE);
425*11600SVikram.Hegde@Sun.COM 	}
426*11600SVikram.Hegde@Sun.COM 
427*11600SVikram.Hegde@Sun.COM 	return (immu_dmar_blacklisted(black_array, nblacks));
428*11600SVikram.Hegde@Sun.COM }
429*11600SVikram.Hegde@Sun.COM 
430*11600SVikram.Hegde@Sun.COM /*
431*11600SVikram.Hegde@Sun.COM  * Check if system is blacklisted by Intel IOMMU driver
432*11600SVikram.Hegde@Sun.COM  * i.e. should Intel IOMMU be disabled on this system
433*11600SVikram.Hegde@Sun.COM  * Currently a system can be blacklistd based on the
434*11600SVikram.Hegde@Sun.COM  * following bases:
435*11600SVikram.Hegde@Sun.COM  *
436*11600SVikram.Hegde@Sun.COM  * 1. DMAR ACPI table information.
437*11600SVikram.Hegde@Sun.COM  *    This information includes things like
438*11600SVikram.Hegde@Sun.COM  *    manufacturer and revision number. If rootnex.conf
439*11600SVikram.Hegde@Sun.COM  *    has matching info set in its blacklist property
440*11600SVikram.Hegde@Sun.COM  *    then Intel IOMMu will be disabled
441*11600SVikram.Hegde@Sun.COM  *
442*11600SVikram.Hegde@Sun.COM  * 2. SMBIOS information
443*11600SVikram.Hegde@Sun.COM  *
444*11600SVikram.Hegde@Sun.COM  * 3. Driver installed - useful if a particular
445*11600SVikram.Hegde@Sun.COM  *    driver or hardware is toxic if Intel IOMMU
446*11600SVikram.Hegde@Sun.COM  *    is turned on.
447*11600SVikram.Hegde@Sun.COM  */
448*11600SVikram.Hegde@Sun.COM 
449*11600SVikram.Hegde@Sun.COM static void
450*11600SVikram.Hegde@Sun.COM blacklist_setup(void)
451*11600SVikram.Hegde@Sun.COM {
452*11600SVikram.Hegde@Sun.COM 	char **string_array;
453*11600SVikram.Hegde@Sun.COM 	uint_t nstrings;
454*11600SVikram.Hegde@Sun.COM 
455*11600SVikram.Hegde@Sun.COM 	/*
456*11600SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf blacklist property.
457*11600SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
458*11600SVikram.Hegde@Sun.COM 	 * property list needs it
459*11600SVikram.Hegde@Sun.COM 	 */
460*11600SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string_array(
461*11600SVikram.Hegde@Sun.COM 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
462*11600SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist",
463*11600SVikram.Hegde@Sun.COM 	    &string_array, &nstrings) != DDI_PROP_SUCCESS) {
464*11600SVikram.Hegde@Sun.COM 		return;
465*11600SVikram.Hegde@Sun.COM 	}
466*11600SVikram.Hegde@Sun.COM 
467*11600SVikram.Hegde@Sun.COM 	/* smallest blacklist criteria works with multiples of 2 */
468*11600SVikram.Hegde@Sun.COM 	if (nstrings % 2 != 0) {
469*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist "
470*11600SVikram.Hegde@Sun.COM 		    "rootnex.conf: number of strings must be a "
471*11600SVikram.Hegde@Sun.COM 		    "multiple of 2");
472*11600SVikram.Hegde@Sun.COM 		ddi_prop_free(string_array);
473*11600SVikram.Hegde@Sun.COM 		return;
474*11600SVikram.Hegde@Sun.COM 	}
475*11600SVikram.Hegde@Sun.COM 
476*11600SVikram.Hegde@Sun.COM 	black_array = string_array;
477*11600SVikram.Hegde@Sun.COM 	nblacks = nstrings;
478*11600SVikram.Hegde@Sun.COM }
479*11600SVikram.Hegde@Sun.COM 
480*11600SVikram.Hegde@Sun.COM static void
481*11600SVikram.Hegde@Sun.COM blacklist_destroy(void)
482*11600SVikram.Hegde@Sun.COM {
483*11600SVikram.Hegde@Sun.COM 	if (black_array) {
484*11600SVikram.Hegde@Sun.COM 		ddi_prop_free(black_array);
485*11600SVikram.Hegde@Sun.COM 		black_array = NULL;
486*11600SVikram.Hegde@Sun.COM 		nblacks = 0;
487*11600SVikram.Hegde@Sun.COM 	}
488*11600SVikram.Hegde@Sun.COM 
489*11600SVikram.Hegde@Sun.COM 	ASSERT(black_array == NULL);
490*11600SVikram.Hegde@Sun.COM 	ASSERT(nblacks == 0);
491*11600SVikram.Hegde@Sun.COM }
492*11600SVikram.Hegde@Sun.COM 
493*11600SVikram.Hegde@Sun.COM 
494*11600SVikram.Hegde@Sun.COM /*
495*11600SVikram.Hegde@Sun.COM  * Now set all the fields in the order they are defined
496*11600SVikram.Hegde@Sun.COM  * We do this only as a defensive-coding practice, it is
497*11600SVikram.Hegde@Sun.COM  * not a correctness issue.
498*11600SVikram.Hegde@Sun.COM  */
499*11600SVikram.Hegde@Sun.COM static void *
500*11600SVikram.Hegde@Sun.COM immu_state_alloc(int seg, void *dmar_unit)
501*11600SVikram.Hegde@Sun.COM {
502*11600SVikram.Hegde@Sun.COM 	immu_t *immu;
503*11600SVikram.Hegde@Sun.COM 
504*11600SVikram.Hegde@Sun.COM 	dmar_unit = immu_dmar_walk_units(seg, dmar_unit);
505*11600SVikram.Hegde@Sun.COM 	if (dmar_unit == NULL) {
506*11600SVikram.Hegde@Sun.COM 		/* No more IOMMUs in this segment */
507*11600SVikram.Hegde@Sun.COM 		return (NULL);
508*11600SVikram.Hegde@Sun.COM 	}
509*11600SVikram.Hegde@Sun.COM 
510*11600SVikram.Hegde@Sun.COM 	immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP);
511*11600SVikram.Hegde@Sun.COM 
512*11600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL);
513*11600SVikram.Hegde@Sun.COM 
514*11600SVikram.Hegde@Sun.COM 	mutex_enter(&(immu->immu_lock));
515*11600SVikram.Hegde@Sun.COM 
516*11600SVikram.Hegde@Sun.COM 	immu->immu_dmar_unit = dmar_unit;
517*11600SVikram.Hegde@Sun.COM 	immu->immu_name = ddi_strdup(immu_dmar_unit_name(dmar_unit),
518*11600SVikram.Hegde@Sun.COM 	    KM_SLEEP);
519*11600SVikram.Hegde@Sun.COM 	immu->immu_dip = immu_dmar_unit_dip(dmar_unit);
520*11600SVikram.Hegde@Sun.COM 
521*11600SVikram.Hegde@Sun.COM 	/*
522*11600SVikram.Hegde@Sun.COM 	 * the immu_intr_lock mutex is grabbed by the IOMMU
523*11600SVikram.Hegde@Sun.COM 	 * unit's interrupt handler so we need to use an
524*11600SVikram.Hegde@Sun.COM 	 * interrupt cookie for the mutex
525*11600SVikram.Hegde@Sun.COM 	 */
526*11600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER,
527*11600SVikram.Hegde@Sun.COM 	    (void *)ipltospl(IMMU_INTR_IPL));
528*11600SVikram.Hegde@Sun.COM 
529*11600SVikram.Hegde@Sun.COM 	/* IOMMU regs related */
530*11600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL);
531*11600SVikram.Hegde@Sun.COM 
532*11600SVikram.Hegde@Sun.COM 	/* DVMA related */
533*11600SVikram.Hegde@Sun.COM 	immu->immu_dvma_coherent = B_FALSE;
534*11600SVikram.Hegde@Sun.COM 
535*11600SVikram.Hegde@Sun.COM 	/* DVMA context related */
536*11600SVikram.Hegde@Sun.COM 	rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL);
537*11600SVikram.Hegde@Sun.COM 
538*11600SVikram.Hegde@Sun.COM 	/* DVMA domain related */
539*11600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_domain_list), sizeof (domain_t),
540*11600SVikram.Hegde@Sun.COM 	    offsetof(domain_t, dom_immu_node));
541*11600SVikram.Hegde@Sun.COM 
542*11600SVikram.Hegde@Sun.COM 	/* DVMA special device lists */
543*11600SVikram.Hegde@Sun.COM 	immu->immu_dvma_gfx_only = B_FALSE;
544*11600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t),
545*11600SVikram.Hegde@Sun.COM 	    offsetof(immu_devi_t, imd_spc_node));
546*11600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t),
547*11600SVikram.Hegde@Sun.COM 	    offsetof(immu_devi_t, imd_spc_node));
548*11600SVikram.Hegde@Sun.COM 
549*11600SVikram.Hegde@Sun.COM 	/* interrupt remapping related */
550*11600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL);
551*11600SVikram.Hegde@Sun.COM 
552*11600SVikram.Hegde@Sun.COM 	/* qinv related */
553*11600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL);
554*11600SVikram.Hegde@Sun.COM 
555*11600SVikram.Hegde@Sun.COM 	/*
556*11600SVikram.Hegde@Sun.COM 	 * insert this immu unit into the system-wide list
557*11600SVikram.Hegde@Sun.COM 	 */
558*11600SVikram.Hegde@Sun.COM 	list_insert_tail(&immu_list, immu);
559*11600SVikram.Hegde@Sun.COM 
560*11600SVikram.Hegde@Sun.COM 	mutex_exit(&(immu->immu_lock));
561*11600SVikram.Hegde@Sun.COM 
562*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_LOG, immu->immu_dip, "IMMU: unit setup");
563*11600SVikram.Hegde@Sun.COM 
564*11600SVikram.Hegde@Sun.COM 	immu_dmar_set_immu(dmar_unit, immu);
565*11600SVikram.Hegde@Sun.COM 
566*11600SVikram.Hegde@Sun.COM 	return (dmar_unit);
567*11600SVikram.Hegde@Sun.COM }
568*11600SVikram.Hegde@Sun.COM 
569*11600SVikram.Hegde@Sun.COM static void
570*11600SVikram.Hegde@Sun.COM immu_subsystems_setup(void)
571*11600SVikram.Hegde@Sun.COM {
572*11600SVikram.Hegde@Sun.COM 	int seg;
573*11600SVikram.Hegde@Sun.COM 	void *unit_hdl;
574*11600SVikram.Hegde@Sun.COM 
575*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_VERB, NULL,
576*11600SVikram.Hegde@Sun.COM 	    "Creating state structures for Intel IOMMU units\n");
577*11600SVikram.Hegde@Sun.COM 
578*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_FALSE);
579*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_running == B_FALSE);
580*11600SVikram.Hegde@Sun.COM 
581*11600SVikram.Hegde@Sun.COM 	mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL);
582*11600SVikram.Hegde@Sun.COM 	list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node));
583*11600SVikram.Hegde@Sun.COM 
584*11600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
585*11600SVikram.Hegde@Sun.COM 
586*11600SVikram.Hegde@Sun.COM 	unit_hdl = NULL;
587*11600SVikram.Hegde@Sun.COM 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
588*11600SVikram.Hegde@Sun.COM 		while (unit_hdl = immu_state_alloc(seg, unit_hdl)) {
589*11600SVikram.Hegde@Sun.COM 			;
590*11600SVikram.Hegde@Sun.COM 		}
591*11600SVikram.Hegde@Sun.COM 	}
592*11600SVikram.Hegde@Sun.COM 
593*11600SVikram.Hegde@Sun.COM 	immu_regs_setup(&immu_list);	/* subsequent code needs this first */
594*11600SVikram.Hegde@Sun.COM 	immu_dvma_setup(&immu_list);
595*11600SVikram.Hegde@Sun.COM 	immu_intrmap_setup(&immu_list);
596*11600SVikram.Hegde@Sun.COM 	immu_qinv_setup(&immu_list);
597*11600SVikram.Hegde@Sun.COM 
598*11600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
599*11600SVikram.Hegde@Sun.COM }
600*11600SVikram.Hegde@Sun.COM 
601*11600SVikram.Hegde@Sun.COM /*
602*11600SVikram.Hegde@Sun.COM  * immu_subsystems_startup()
603*11600SVikram.Hegde@Sun.COM  * 	startup all units that were setup
604*11600SVikram.Hegde@Sun.COM  */
605*11600SVikram.Hegde@Sun.COM static void
606*11600SVikram.Hegde@Sun.COM immu_subsystems_startup(void)
607*11600SVikram.Hegde@Sun.COM {
608*11600SVikram.Hegde@Sun.COM 	immu_t *immu;
609*11600SVikram.Hegde@Sun.COM 
610*11600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
611*11600SVikram.Hegde@Sun.COM 
612*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_TRUE);
613*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_running == B_FALSE);
614*11600SVikram.Hegde@Sun.COM 
615*11600SVikram.Hegde@Sun.COM 	immu_dmar_startup();
616*11600SVikram.Hegde@Sun.COM 
617*11600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
618*11600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
619*11600SVikram.Hegde@Sun.COM 
620*11600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
621*11600SVikram.Hegde@Sun.COM 
622*11600SVikram.Hegde@Sun.COM 		immu_intr_register(immu);
623*11600SVikram.Hegde@Sun.COM 		immu_dvma_startup(immu);
624*11600SVikram.Hegde@Sun.COM 		immu_intrmap_startup(immu);
625*11600SVikram.Hegde@Sun.COM 		immu_qinv_startup(immu);
626*11600SVikram.Hegde@Sun.COM 
627*11600SVikram.Hegde@Sun.COM 		/*
628*11600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
629*11600SVikram.Hegde@Sun.COM 		 * the actual startup. This will
630*11600SVikram.Hegde@Sun.COM 		 * set immu->immu_running  field
631*11600SVikram.Hegde@Sun.COM 		 * if the unit is successfully
632*11600SVikram.Hegde@Sun.COM 		 * started
633*11600SVikram.Hegde@Sun.COM 		 */
634*11600SVikram.Hegde@Sun.COM 		immu_regs_startup(immu);
635*11600SVikram.Hegde@Sun.COM 
636*11600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
637*11600SVikram.Hegde@Sun.COM 	}
638*11600SVikram.Hegde@Sun.COM 
639*11600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
640*11600SVikram.Hegde@Sun.COM }
641*11600SVikram.Hegde@Sun.COM 
642*11600SVikram.Hegde@Sun.COM /* ##################  Intel IOMMU internal interfaces ###################### */
643*11600SVikram.Hegde@Sun.COM 
644*11600SVikram.Hegde@Sun.COM /*
645*11600SVikram.Hegde@Sun.COM  * Internal interfaces for IOMMU code (i.e. not exported to rootnex
646*11600SVikram.Hegde@Sun.COM  * or rest of system)
647*11600SVikram.Hegde@Sun.COM  */
648*11600SVikram.Hegde@Sun.COM 
649*11600SVikram.Hegde@Sun.COM /*
650*11600SVikram.Hegde@Sun.COM  * ddip can be NULL, in which case we walk up until we find the root dip
651*11600SVikram.Hegde@Sun.COM  * NOTE: We never visit the root dip since its not a hardware node
652*11600SVikram.Hegde@Sun.COM  */
653*11600SVikram.Hegde@Sun.COM int
654*11600SVikram.Hegde@Sun.COM immu_walk_ancestor(
655*11600SVikram.Hegde@Sun.COM 	dev_info_t *rdip,
656*11600SVikram.Hegde@Sun.COM 	dev_info_t *ddip,
657*11600SVikram.Hegde@Sun.COM 	int (*func)(dev_info_t *, void *arg),
658*11600SVikram.Hegde@Sun.COM 	void *arg,
659*11600SVikram.Hegde@Sun.COM 	int *lvlp,
660*11600SVikram.Hegde@Sun.COM 	immu_flags_t immu_flags)
661*11600SVikram.Hegde@Sun.COM {
662*11600SVikram.Hegde@Sun.COM 	dev_info_t *pdip;
663*11600SVikram.Hegde@Sun.COM 	int level;
664*11600SVikram.Hegde@Sun.COM 	int error = DDI_SUCCESS;
665*11600SVikram.Hegde@Sun.COM 
666*11600SVikram.Hegde@Sun.COM 	ASSERT(root_devinfo);
667*11600SVikram.Hegde@Sun.COM 	ASSERT(rdip);
668*11600SVikram.Hegde@Sun.COM 	ASSERT(rdip != root_devinfo);
669*11600SVikram.Hegde@Sun.COM 	ASSERT(func);
670*11600SVikram.Hegde@Sun.COM 
671*11600SVikram.Hegde@Sun.COM 	/* ddip and immu can be NULL */
672*11600SVikram.Hegde@Sun.COM 
673*11600SVikram.Hegde@Sun.COM 	/* Hold rdip so that branch is not detached */
674*11600SVikram.Hegde@Sun.COM 	ndi_hold_devi(rdip);
675*11600SVikram.Hegde@Sun.COM 	for (pdip = rdip, level = 1; pdip && pdip != root_devinfo;
676*11600SVikram.Hegde@Sun.COM 	    pdip = ddi_get_parent(pdip), level++) {
677*11600SVikram.Hegde@Sun.COM 
678*11600SVikram.Hegde@Sun.COM 		if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) {
679*11600SVikram.Hegde@Sun.COM 			error = DDI_FAILURE;
680*11600SVikram.Hegde@Sun.COM 			break;
681*11600SVikram.Hegde@Sun.COM 		}
682*11600SVikram.Hegde@Sun.COM 		if (func(pdip, arg) == DDI_WALK_TERMINATE) {
683*11600SVikram.Hegde@Sun.COM 			break;
684*11600SVikram.Hegde@Sun.COM 		}
685*11600SVikram.Hegde@Sun.COM 		if (immu_flags & IMMU_FLAGS_DONTPASS) {
686*11600SVikram.Hegde@Sun.COM 			break;
687*11600SVikram.Hegde@Sun.COM 		}
688*11600SVikram.Hegde@Sun.COM 		if (pdip == ddip) {
689*11600SVikram.Hegde@Sun.COM 			break;
690*11600SVikram.Hegde@Sun.COM 		}
691*11600SVikram.Hegde@Sun.COM 	}
692*11600SVikram.Hegde@Sun.COM 
693*11600SVikram.Hegde@Sun.COM 	ndi_rele_devi(rdip);
694*11600SVikram.Hegde@Sun.COM 
695*11600SVikram.Hegde@Sun.COM 	if (lvlp)
696*11600SVikram.Hegde@Sun.COM 		*lvlp = level;
697*11600SVikram.Hegde@Sun.COM 
698*11600SVikram.Hegde@Sun.COM 	return (error);
699*11600SVikram.Hegde@Sun.COM }
700*11600SVikram.Hegde@Sun.COM 
701*11600SVikram.Hegde@Sun.COM /* ########################  Intel IOMMU entry points ####################### */
702*11600SVikram.Hegde@Sun.COM /*
703*11600SVikram.Hegde@Sun.COM  * immu_init()
704*11600SVikram.Hegde@Sun.COM  *	called from rootnex_attach(). setup but don't startup the Intel IOMMU
705*11600SVikram.Hegde@Sun.COM  *      This is the first function called in Intel IOMMU code
706*11600SVikram.Hegde@Sun.COM  */
707*11600SVikram.Hegde@Sun.COM void
708*11600SVikram.Hegde@Sun.COM immu_init(void)
709*11600SVikram.Hegde@Sun.COM {
710*11600SVikram.Hegde@Sun.COM 	char *phony_reg = "A thing of beauty is a joy forever";
711*11600SVikram.Hegde@Sun.COM 
712*11600SVikram.Hegde@Sun.COM 	/* Set some global shorthands that are needed by all of IOMMU code */
713*11600SVikram.Hegde@Sun.COM 	ASSERT(root_devinfo == NULL);
714*11600SVikram.Hegde@Sun.COM 	root_devinfo = ddi_root_node();
715*11600SVikram.Hegde@Sun.COM 
716*11600SVikram.Hegde@Sun.COM 	/*
717*11600SVikram.Hegde@Sun.COM 	 * Intel IOMMU only supported only if MMU(CPU) page size is ==
718*11600SVikram.Hegde@Sun.COM 	 * IOMMU pages size.
719*11600SVikram.Hegde@Sun.COM 	 */
720*11600SVikram.Hegde@Sun.COM 	/*LINTED*/
721*11600SVikram.Hegde@Sun.COM 	if (MMU_PAGESIZE != IMMU_PAGESIZE) {
722*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL,
723*11600SVikram.Hegde@Sun.COM 		    "MMU page size (%d) is not equal to\n"
724*11600SVikram.Hegde@Sun.COM 		    "IOMMU page size (%d). "
725*11600SVikram.Hegde@Sun.COM 		    "Disabling Intel IOMMU. ",
726*11600SVikram.Hegde@Sun.COM 		    MMU_PAGESIZE, IMMU_PAGESIZE);
727*11600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
728*11600SVikram.Hegde@Sun.COM 		return;
729*11600SVikram.Hegde@Sun.COM 	}
730*11600SVikram.Hegde@Sun.COM 
731*11600SVikram.Hegde@Sun.COM 	/*
732*11600SVikram.Hegde@Sun.COM 	 * retrieve the Intel IOMMU boot options.
733*11600SVikram.Hegde@Sun.COM 	 * Do this before parsing immu ACPI table
734*11600SVikram.Hegde@Sun.COM 	 * as a boot option could potentially affect
735*11600SVikram.Hegde@Sun.COM 	 * ACPI parsing.
736*11600SVikram.Hegde@Sun.COM 	 */
737*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n");
738*11600SVikram.Hegde@Sun.COM 	read_boot_options();
739*11600SVikram.Hegde@Sun.COM 
740*11600SVikram.Hegde@Sun.COM 	/*
741*11600SVikram.Hegde@Sun.COM 	 * Check the IOMMU enable boot-option first.
742*11600SVikram.Hegde@Sun.COM 	 * This is so that we can skip parsing the ACPI table
743*11600SVikram.Hegde@Sun.COM 	 * if necessary because that may cause problems in
744*11600SVikram.Hegde@Sun.COM 	 * systems with buggy BIOS or ACPI tables
745*11600SVikram.Hegde@Sun.COM 	 */
746*11600SVikram.Hegde@Sun.COM 	if (immu_enable == B_FALSE) {
747*11600SVikram.Hegde@Sun.COM 		return;
748*11600SVikram.Hegde@Sun.COM 	}
749*11600SVikram.Hegde@Sun.COM 
750*11600SVikram.Hegde@Sun.COM 	/*
751*11600SVikram.Hegde@Sun.COM 	 * Next, check if the system even has an Intel IOMMU
752*11600SVikram.Hegde@Sun.COM 	 * We use the presence or absence of the IOMMU ACPI
753*11600SVikram.Hegde@Sun.COM 	 * table to detect Intel IOMMU.
754*11600SVikram.Hegde@Sun.COM 	 */
755*11600SVikram.Hegde@Sun.COM 	if (immu_dmar_setup() != DDI_SUCCESS) {
756*11600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
757*11600SVikram.Hegde@Sun.COM 		return;
758*11600SVikram.Hegde@Sun.COM 	}
759*11600SVikram.Hegde@Sun.COM 
760*11600SVikram.Hegde@Sun.COM 	/*
761*11600SVikram.Hegde@Sun.COM 	 * Check blacklists
762*11600SVikram.Hegde@Sun.COM 	 */
763*11600SVikram.Hegde@Sun.COM 	blacklist_setup();
764*11600SVikram.Hegde@Sun.COM 
765*11600SVikram.Hegde@Sun.COM 	if (blacklisted_smbios() == B_TRUE) {
766*11600SVikram.Hegde@Sun.COM 		blacklist_destroy();
767*11600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
768*11600SVikram.Hegde@Sun.COM 		return;
769*11600SVikram.Hegde@Sun.COM 	}
770*11600SVikram.Hegde@Sun.COM 
771*11600SVikram.Hegde@Sun.COM 	if (blacklisted_driver() == B_TRUE) {
772*11600SVikram.Hegde@Sun.COM 		blacklist_destroy();
773*11600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
774*11600SVikram.Hegde@Sun.COM 		return;
775*11600SVikram.Hegde@Sun.COM 	}
776*11600SVikram.Hegde@Sun.COM 
777*11600SVikram.Hegde@Sun.COM 	/*
778*11600SVikram.Hegde@Sun.COM 	 * Read the "raw" DMAR ACPI table to get information
779*11600SVikram.Hegde@Sun.COM 	 * and convert into a form we can use.
780*11600SVikram.Hegde@Sun.COM 	 */
781*11600SVikram.Hegde@Sun.COM 	if (immu_dmar_parse() != DDI_SUCCESS) {
782*11600SVikram.Hegde@Sun.COM 		blacklist_destroy();
783*11600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
784*11600SVikram.Hegde@Sun.COM 		return;
785*11600SVikram.Hegde@Sun.COM 	}
786*11600SVikram.Hegde@Sun.COM 
787*11600SVikram.Hegde@Sun.COM 	/*
788*11600SVikram.Hegde@Sun.COM 	 * now that we have processed the ACPI table
789*11600SVikram.Hegde@Sun.COM 	 * check if we need to blacklist this system
790*11600SVikram.Hegde@Sun.COM 	 * based on ACPI info
791*11600SVikram.Hegde@Sun.COM 	 */
792*11600SVikram.Hegde@Sun.COM 	if (blacklisted_acpi() == B_TRUE) {
793*11600SVikram.Hegde@Sun.COM 		immu_dmar_destroy();
794*11600SVikram.Hegde@Sun.COM 		blacklist_destroy();
795*11600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
796*11600SVikram.Hegde@Sun.COM 		return;
797*11600SVikram.Hegde@Sun.COM 	}
798*11600SVikram.Hegde@Sun.COM 
799*11600SVikram.Hegde@Sun.COM 	blacklist_destroy();
800*11600SVikram.Hegde@Sun.COM 
801*11600SVikram.Hegde@Sun.COM 	/*
802*11600SVikram.Hegde@Sun.COM 	 * Check if system has HW quirks.
803*11600SVikram.Hegde@Sun.COM 	 */
804*11600SVikram.Hegde@Sun.COM 	pre_setup_quirks();
805*11600SVikram.Hegde@Sun.COM 
806*11600SVikram.Hegde@Sun.COM 	/* Now do the rest of the setup */
807*11600SVikram.Hegde@Sun.COM 	immu_subsystems_setup();
808*11600SVikram.Hegde@Sun.COM 
809*11600SVikram.Hegde@Sun.COM 	/*
810*11600SVikram.Hegde@Sun.COM 	 * Now that the IMMU is setup, create a phony
811*11600SVikram.Hegde@Sun.COM 	 * reg prop so that suspend/resume works
812*11600SVikram.Hegde@Sun.COM 	 */
813*11600SVikram.Hegde@Sun.COM 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg",
814*11600SVikram.Hegde@Sun.COM 	    (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) {
815*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_PANIC, NULL, "Failed to create reg prop for "
816*11600SVikram.Hegde@Sun.COM 		    "rootnex node");
817*11600SVikram.Hegde@Sun.COM 		/*NOTREACHED*/
818*11600SVikram.Hegde@Sun.COM 	}
819*11600SVikram.Hegde@Sun.COM 
820*11600SVikram.Hegde@Sun.COM 	immu_setup = B_TRUE;
821*11600SVikram.Hegde@Sun.COM }
822*11600SVikram.Hegde@Sun.COM 
823*11600SVikram.Hegde@Sun.COM /*
824*11600SVikram.Hegde@Sun.COM  * immu_startup()
825*11600SVikram.Hegde@Sun.COM  * 	called directly by boot code to startup
826*11600SVikram.Hegde@Sun.COM  *      all units of the IOMMU
827*11600SVikram.Hegde@Sun.COM  */
828*11600SVikram.Hegde@Sun.COM void
829*11600SVikram.Hegde@Sun.COM immu_startup(void)
830*11600SVikram.Hegde@Sun.COM {
831*11600SVikram.Hegde@Sun.COM 	/*
832*11600SVikram.Hegde@Sun.COM 	 * If IOMMU is disabled, do nothing
833*11600SVikram.Hegde@Sun.COM 	 */
834*11600SVikram.Hegde@Sun.COM 	if (immu_enable == B_FALSE) {
835*11600SVikram.Hegde@Sun.COM 		return;
836*11600SVikram.Hegde@Sun.COM 	}
837*11600SVikram.Hegde@Sun.COM 
838*11600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
839*11600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, "
840*11600SVikram.Hegde@Sun.COM 		    "skipping IOMU startup");
841*11600SVikram.Hegde@Sun.COM 		return;
842*11600SVikram.Hegde@Sun.COM 	}
843*11600SVikram.Hegde@Sun.COM 
844*11600SVikram.Hegde@Sun.COM 	pre_startup_quirks();
845*11600SVikram.Hegde@Sun.COM 
846*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL,
847*11600SVikram.Hegde@Sun.COM 	    "?Starting Intel IOMMU (dmar) units...\n");
848*11600SVikram.Hegde@Sun.COM 
849*11600SVikram.Hegde@Sun.COM 	immu_subsystems_startup();
850*11600SVikram.Hegde@Sun.COM 
851*11600SVikram.Hegde@Sun.COM 	immu_running = B_TRUE;
852*11600SVikram.Hegde@Sun.COM }
853*11600SVikram.Hegde@Sun.COM 
854*11600SVikram.Hegde@Sun.COM /*
855*11600SVikram.Hegde@Sun.COM  * immu_map_sgl()
856*11600SVikram.Hegde@Sun.COM  * 	called from rootnex_coredma_bindhdl() when Intel
857*11600SVikram.Hegde@Sun.COM  *	IOMMU is enabled to build DVMA cookies and map them.
858*11600SVikram.Hegde@Sun.COM  */
859*11600SVikram.Hegde@Sun.COM int
860*11600SVikram.Hegde@Sun.COM immu_map_sgl(ddi_dma_impl_t *hp, struct ddi_dma_req *dmareq,
861*11600SVikram.Hegde@Sun.COM     int prealloc_count, dev_info_t *rdip)
862*11600SVikram.Hegde@Sun.COM {
863*11600SVikram.Hegde@Sun.COM 	if (immu_running == B_FALSE) {
864*11600SVikram.Hegde@Sun.COM 		return (DDI_DMA_USE_PHYSICAL);
865*11600SVikram.Hegde@Sun.COM 	}
866*11600SVikram.Hegde@Sun.COM 
867*11600SVikram.Hegde@Sun.COM 	return (immu_dvma_map(hp, dmareq, NULL, prealloc_count, rdip,
868*11600SVikram.Hegde@Sun.COM 	    IMMU_FLAGS_DMAHDL));
869*11600SVikram.Hegde@Sun.COM }
870*11600SVikram.Hegde@Sun.COM 
871*11600SVikram.Hegde@Sun.COM /*
872*11600SVikram.Hegde@Sun.COM  * immu_unmap_sgl()
873*11600SVikram.Hegde@Sun.COM  * 	called from rootnex_coredma_unbindhdl(), to unmap DVMA
874*11600SVikram.Hegde@Sun.COM  * 	cookies and free them
875*11600SVikram.Hegde@Sun.COM  */
876*11600SVikram.Hegde@Sun.COM int
877*11600SVikram.Hegde@Sun.COM immu_unmap_sgl(ddi_dma_impl_t *hp, dev_info_t *rdip)
878*11600SVikram.Hegde@Sun.COM {
879*11600SVikram.Hegde@Sun.COM 	if (immu_running == B_FALSE) {
880*11600SVikram.Hegde@Sun.COM 		return (DDI_DMA_USE_PHYSICAL);
881*11600SVikram.Hegde@Sun.COM 	}
882*11600SVikram.Hegde@Sun.COM 
883*11600SVikram.Hegde@Sun.COM 	return (immu_dvma_unmap(hp, rdip));
884*11600SVikram.Hegde@Sun.COM }
885*11600SVikram.Hegde@Sun.COM 
886*11600SVikram.Hegde@Sun.COM /*
887*11600SVikram.Hegde@Sun.COM  * Hook to notify IOMMU code of device tree changes
888*11600SVikram.Hegde@Sun.COM  */
889*11600SVikram.Hegde@Sun.COM void
890*11600SVikram.Hegde@Sun.COM immu_device_tree_changed(void)
891*11600SVikram.Hegde@Sun.COM {
892*11600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
893*11600SVikram.Hegde@Sun.COM 		return;
894*11600SVikram.Hegde@Sun.COM 	}
895*11600SVikram.Hegde@Sun.COM 
896*11600SVikram.Hegde@Sun.COM 	ddi_err(DER_WARN, NULL, "Intel IOMMU currently "
897*11600SVikram.Hegde@Sun.COM 	    "does not use device tree updates");
898*11600SVikram.Hegde@Sun.COM }
899*11600SVikram.Hegde@Sun.COM 
900*11600SVikram.Hegde@Sun.COM /*
901*11600SVikram.Hegde@Sun.COM  * Hook to notify IOMMU code of memory changes
902*11600SVikram.Hegde@Sun.COM  */
903*11600SVikram.Hegde@Sun.COM void
904*11600SVikram.Hegde@Sun.COM immu_physmem_update(uint64_t addr, uint64_t size)
905*11600SVikram.Hegde@Sun.COM {
906*11600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
907*11600SVikram.Hegde@Sun.COM 		return;
908*11600SVikram.Hegde@Sun.COM 	}
909*11600SVikram.Hegde@Sun.COM 	immu_dvma_physmem_update(addr, size);
910*11600SVikram.Hegde@Sun.COM }
911*11600SVikram.Hegde@Sun.COM 
912*11600SVikram.Hegde@Sun.COM /*
913*11600SVikram.Hegde@Sun.COM  * immu_quiesce()
914*11600SVikram.Hegde@Sun.COM  * 	quiesce all units that are running
915*11600SVikram.Hegde@Sun.COM  */
916*11600SVikram.Hegde@Sun.COM int
917*11600SVikram.Hegde@Sun.COM immu_quiesce(void)
918*11600SVikram.Hegde@Sun.COM {
919*11600SVikram.Hegde@Sun.COM 	immu_t *immu;
920*11600SVikram.Hegde@Sun.COM 	int ret = DDI_SUCCESS;
921*11600SVikram.Hegde@Sun.COM 
922*11600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
923*11600SVikram.Hegde@Sun.COM 
924*11600SVikram.Hegde@Sun.COM 	if (immu_running == B_FALSE)
925*11600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
926*11600SVikram.Hegde@Sun.COM 
927*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_TRUE);
928*11600SVikram.Hegde@Sun.COM 
929*11600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
930*11600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
931*11600SVikram.Hegde@Sun.COM 
932*11600SVikram.Hegde@Sun.COM 		/* if immu is not running, we dont quiesce */
933*11600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_FALSE)
934*11600SVikram.Hegde@Sun.COM 			continue;
935*11600SVikram.Hegde@Sun.COM 
936*11600SVikram.Hegde@Sun.COM 		/* flush caches */
937*11600SVikram.Hegde@Sun.COM 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
938*11600SVikram.Hegde@Sun.COM 		immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL);
939*11600SVikram.Hegde@Sun.COM 		rw_exit(&(immu->immu_ctx_rwlock));
940*11600SVikram.Hegde@Sun.COM 		immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL);
941*11600SVikram.Hegde@Sun.COM 		immu_regs_wbf_flush(immu);
942*11600SVikram.Hegde@Sun.COM 
943*11600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
944*11600SVikram.Hegde@Sun.COM 
945*11600SVikram.Hegde@Sun.COM 		/*
946*11600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
947*11600SVikram.Hegde@Sun.COM 		 * the actual shutdown.
948*11600SVikram.Hegde@Sun.COM 		 */
949*11600SVikram.Hegde@Sun.COM 		immu_regs_shutdown(immu);
950*11600SVikram.Hegde@Sun.COM 		immu_regs_suspend(immu);
951*11600SVikram.Hegde@Sun.COM 
952*11600SVikram.Hegde@Sun.COM 		/* if immu is still running, we failed */
953*11600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_TRUE)
954*11600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
955*11600SVikram.Hegde@Sun.COM 		else
956*11600SVikram.Hegde@Sun.COM 			immu->immu_regs_quiesced = B_TRUE;
957*11600SVikram.Hegde@Sun.COM 
958*11600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
959*11600SVikram.Hegde@Sun.COM 	}
960*11600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
961*11600SVikram.Hegde@Sun.COM 
962*11600SVikram.Hegde@Sun.COM 	if (ret == DDI_SUCCESS) {
963*11600SVikram.Hegde@Sun.COM 		immu_running = B_FALSE;
964*11600SVikram.Hegde@Sun.COM 		immu_quiesced = B_TRUE;
965*11600SVikram.Hegde@Sun.COM 	}
966*11600SVikram.Hegde@Sun.COM 
967*11600SVikram.Hegde@Sun.COM 	return (ret);
968*11600SVikram.Hegde@Sun.COM }
969*11600SVikram.Hegde@Sun.COM 
970*11600SVikram.Hegde@Sun.COM /*
971*11600SVikram.Hegde@Sun.COM  * immu_unquiesce()
972*11600SVikram.Hegde@Sun.COM  * 	unquiesce all units
973*11600SVikram.Hegde@Sun.COM  */
974*11600SVikram.Hegde@Sun.COM int
975*11600SVikram.Hegde@Sun.COM immu_unquiesce(void)
976*11600SVikram.Hegde@Sun.COM {
977*11600SVikram.Hegde@Sun.COM 	immu_t *immu;
978*11600SVikram.Hegde@Sun.COM 	int ret = DDI_SUCCESS;
979*11600SVikram.Hegde@Sun.COM 
980*11600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
981*11600SVikram.Hegde@Sun.COM 
982*11600SVikram.Hegde@Sun.COM 	if (immu_quiesced == B_FALSE)
983*11600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
984*11600SVikram.Hegde@Sun.COM 
985*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_TRUE);
986*11600SVikram.Hegde@Sun.COM 	ASSERT(immu_running == B_FALSE);
987*11600SVikram.Hegde@Sun.COM 
988*11600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
989*11600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
990*11600SVikram.Hegde@Sun.COM 
991*11600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
992*11600SVikram.Hegde@Sun.COM 
993*11600SVikram.Hegde@Sun.COM 		/* if immu was not quiesced, i.e was not running before */
994*11600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_quiesced == B_FALSE)
995*11600SVikram.Hegde@Sun.COM 			continue;
996*11600SVikram.Hegde@Sun.COM 
997*11600SVikram.Hegde@Sun.COM 		if (immu_regs_resume(immu) != DDI_SUCCESS) {
998*11600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
999*11600SVikram.Hegde@Sun.COM 			continue;
1000*11600SVikram.Hegde@Sun.COM 		}
1001*11600SVikram.Hegde@Sun.COM 
1002*11600SVikram.Hegde@Sun.COM 		/* flush caches before unquiesce */
1003*11600SVikram.Hegde@Sun.COM 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
1004*11600SVikram.Hegde@Sun.COM 		immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL);
1005*11600SVikram.Hegde@Sun.COM 		rw_exit(&(immu->immu_ctx_rwlock));
1006*11600SVikram.Hegde@Sun.COM 		immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL);
1007*11600SVikram.Hegde@Sun.COM 
1008*11600SVikram.Hegde@Sun.COM 		/*
1009*11600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
1010*11600SVikram.Hegde@Sun.COM 		 * the actual startup. This will
1011*11600SVikram.Hegde@Sun.COM 		 * set immu->immu_regs_running  field
1012*11600SVikram.Hegde@Sun.COM 		 * if the unit is successfully
1013*11600SVikram.Hegde@Sun.COM 		 * started
1014*11600SVikram.Hegde@Sun.COM 		 */
1015*11600SVikram.Hegde@Sun.COM 		immu_regs_startup(immu);
1016*11600SVikram.Hegde@Sun.COM 
1017*11600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_FALSE) {
1018*11600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
1019*11600SVikram.Hegde@Sun.COM 		} else {
1020*11600SVikram.Hegde@Sun.COM 			immu_quiesced = B_TRUE;
1021*11600SVikram.Hegde@Sun.COM 			immu_running = B_TRUE;
1022*11600SVikram.Hegde@Sun.COM 			immu->immu_regs_quiesced = B_FALSE;
1023*11600SVikram.Hegde@Sun.COM 		}
1024*11600SVikram.Hegde@Sun.COM 
1025*11600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
1026*11600SVikram.Hegde@Sun.COM 	}
1027*11600SVikram.Hegde@Sun.COM 
1028*11600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
1029*11600SVikram.Hegde@Sun.COM 
1030*11600SVikram.Hegde@Sun.COM 	return (ret);
1031*11600SVikram.Hegde@Sun.COM }
1032*11600SVikram.Hegde@Sun.COM 
1033*11600SVikram.Hegde@Sun.COM /* ##############  END Intel IOMMU entry points ################## */
1034