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