xref: /illumos-gate/usr/src/uts/i86pc/io/immu.c (revision d5ebc493)
13a634bfcSVikram Hegde /*
23a634bfcSVikram Hegde  * CDDL HEADER START
33a634bfcSVikram Hegde  *
43a634bfcSVikram Hegde  * The contents of this file are subject to the terms of the
53a634bfcSVikram Hegde  * Common Development and Distribution License (the "License").
63a634bfcSVikram Hegde  * You may not use this file except in compliance with the License.
73a634bfcSVikram Hegde  *
83a634bfcSVikram Hegde  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93a634bfcSVikram Hegde  * or http://www.opensolaris.org/os/licensing.
103a634bfcSVikram Hegde  * See the License for the specific language governing permissions
113a634bfcSVikram Hegde  * and limitations under the License.
123a634bfcSVikram Hegde  *
133a634bfcSVikram Hegde  * When distributing Covered Code, include this CDDL HEADER in each
143a634bfcSVikram Hegde  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153a634bfcSVikram Hegde  * If applicable, add the following below this CDDL HEADER, with the
163a634bfcSVikram Hegde  * fields enclosed by brackets "[]" replaced with your own identifying
173a634bfcSVikram Hegde  * information: Portions Copyright [yyyy] [name of copyright owner]
183a634bfcSVikram Hegde  *
193a634bfcSVikram Hegde  * CDDL HEADER END
203a634bfcSVikram Hegde  */
213a634bfcSVikram Hegde /*
229e986f0eSFrank Van Der Linden  * Portions Copyright (c) 2010, Oracle and/or its affiliates.
239e986f0eSFrank Van Der Linden  * All rights reserved.
243a634bfcSVikram Hegde  */
253a634bfcSVikram Hegde /*
263a634bfcSVikram Hegde  * Copyright (c) 2009, Intel Corporation.
273a634bfcSVikram Hegde  * All rights reserved.
283a634bfcSVikram Hegde  */
293a634bfcSVikram Hegde 
303fe80ca4SDan Cross /*
313fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
323fe80ca4SDan Cross  */
333fe80ca4SDan Cross 
343a634bfcSVikram Hegde /*
353a634bfcSVikram Hegde  * Intel IOMMU implementation
363a634bfcSVikram Hegde  * This file contains Intel IOMMU code exported
373a634bfcSVikram Hegde  * to the rest of the system and code that deals
383a634bfcSVikram Hegde  * with the Intel IOMMU as a whole.
393a634bfcSVikram Hegde  */
403a634bfcSVikram Hegde 
413a634bfcSVikram Hegde #include <sys/conf.h>
423a634bfcSVikram Hegde #include <sys/modctl.h>
433a634bfcSVikram Hegde #include <sys/pci.h>
443a634bfcSVikram Hegde #include <sys/pci_impl.h>
453a634bfcSVikram Hegde #include <sys/sysmacros.h>
463a634bfcSVikram Hegde #include <sys/ddi.h>
473a634bfcSVikram Hegde #include <sys/ddidmareq.h>
483a634bfcSVikram Hegde #include <sys/ddi_impldefs.h>
493a634bfcSVikram Hegde #include <sys/ddifm.h>
503a634bfcSVikram Hegde #include <sys/sunndi.h>
513a634bfcSVikram Hegde #include <sys/debug.h>
523a634bfcSVikram Hegde #include <sys/fm/protocol.h>
533a634bfcSVikram Hegde #include <sys/note.h>
543a634bfcSVikram Hegde #include <sys/apic.h>
553a634bfcSVikram Hegde #include <vm/hat_i86.h>
563a634bfcSVikram Hegde #include <sys/smp_impldefs.h>
573a634bfcSVikram Hegde #include <sys/spl.h>
583a634bfcSVikram Hegde #include <sys/archsystm.h>
593a634bfcSVikram Hegde #include <sys/x86_archext.h>
603a634bfcSVikram Hegde #include <sys/avl.h>
613a634bfcSVikram Hegde #include <sys/bootconf.h>
623a634bfcSVikram Hegde #include <sys/bootinfo.h>
633a634bfcSVikram Hegde #include <sys/atomic.h>
643a634bfcSVikram Hegde #include <sys/immu.h>
653a634bfcSVikram Hegde /* ########################### Globals and tunables ######################## */
663a634bfcSVikram Hegde /*
673a634bfcSVikram Hegde  * Global switches (boolean) that can be toggled either via boot options
683a634bfcSVikram Hegde  * or via /etc/system or kmdb
693a634bfcSVikram Hegde  */
703a634bfcSVikram Hegde 
713a634bfcSVikram Hegde /* Various features */
723adb2334SVikram Hegde boolean_t immu_enable = B_TRUE;
733a634bfcSVikram Hegde boolean_t immu_dvma_enable = B_TRUE;
743a634bfcSVikram Hegde 
753a634bfcSVikram Hegde /* accessed in other files so not static */
763a634bfcSVikram Hegde boolean_t immu_gfxdvma_enable = B_TRUE;
773a634bfcSVikram Hegde boolean_t immu_intrmap_enable = B_FALSE;
7850200e77SFrank Van Der Linden boolean_t immu_qinv_enable = B_TRUE;
793a634bfcSVikram Hegde 
803a634bfcSVikram Hegde /* various quirks that need working around */
813a634bfcSVikram Hegde 
823a634bfcSVikram Hegde /* XXX We always map page 0 read/write for now */
833a634bfcSVikram Hegde boolean_t immu_quirk_usbpage0 = B_TRUE;
843a634bfcSVikram Hegde boolean_t immu_quirk_usbrmrr = B_TRUE;
853a634bfcSVikram Hegde boolean_t immu_quirk_usbfullpa;
863a634bfcSVikram Hegde boolean_t immu_quirk_mobile4;
873a634bfcSVikram Hegde 
883a634bfcSVikram Hegde /* debug messages */
893a634bfcSVikram Hegde boolean_t immu_dmar_print;
903a634bfcSVikram Hegde 
91e03dceedSVikram Hegde /* Tunables */
92e03dceedSVikram Hegde int64_t immu_flush_gran = 5;
93e03dceedSVikram Hegde 
949e986f0eSFrank Van Der Linden immu_flags_t immu_global_dvma_flags;
959e986f0eSFrank Van Der Linden 
963a634bfcSVikram Hegde /* ############  END OPTIONS section ################ */
973a634bfcSVikram Hegde 
983a634bfcSVikram Hegde /*
993a634bfcSVikram Hegde  * Global used internally by Intel IOMMU code
1003a634bfcSVikram Hegde  */
1013a634bfcSVikram Hegde dev_info_t *root_devinfo;
1023a634bfcSVikram Hegde kmutex_t immu_lock;
1033a634bfcSVikram Hegde list_t immu_list;
1043a634bfcSVikram Hegde boolean_t immu_setup;
1053a634bfcSVikram Hegde boolean_t immu_running;
1063a634bfcSVikram Hegde boolean_t immu_quiesced;
1073a634bfcSVikram Hegde 
1083a634bfcSVikram Hegde /* ######################## END Globals and tunables ###################### */
1093a634bfcSVikram Hegde /* Globals used only in this file */
1103a634bfcSVikram Hegde static char **black_array;
1113a634bfcSVikram Hegde static uint_t nblacks;
1129e986f0eSFrank Van Der Linden 
1139e986f0eSFrank Van Der Linden static char **unity_driver_array;
1149e986f0eSFrank Van Der Linden static uint_t nunity;
1159e986f0eSFrank Van Der Linden static char **xlate_driver_array;
1169e986f0eSFrank Van Der Linden static uint_t nxlate;
11750200e77SFrank Van Der Linden 
11850200e77SFrank Van Der Linden static char **premap_driver_array;
11950200e77SFrank Van Der Linden static uint_t npremap;
12050200e77SFrank Van Der Linden static char **nopremap_driver_array;
12150200e77SFrank Van Der Linden static uint_t nnopremap;
1223a634bfcSVikram Hegde /* ###################### Utility routines ############################# */
1233a634bfcSVikram Hegde 
1243a634bfcSVikram Hegde /*
1253a634bfcSVikram Hegde  * Check if the device has mobile 4 chipset
1263a634bfcSVikram Hegde  */
1273a634bfcSVikram Hegde static int
check_mobile4(dev_info_t * dip,void * arg)1283a634bfcSVikram Hegde check_mobile4(dev_info_t *dip, void *arg)
1293a634bfcSVikram Hegde {
1303a634bfcSVikram Hegde 	_NOTE(ARGUNUSED(arg));
1313a634bfcSVikram Hegde 	int vendor, device;
1323a634bfcSVikram Hegde 	int *ip = (int *)arg;
1333a634bfcSVikram Hegde 
1343a634bfcSVikram Hegde 	vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1353a634bfcSVikram Hegde 	    "vendor-id", -1);
1363a634bfcSVikram Hegde 	device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1373a634bfcSVikram Hegde 	    "device-id", -1);
1383a634bfcSVikram Hegde 
1393a634bfcSVikram Hegde 	if (vendor == 0x8086 && device == 0x2a40) {
1403a634bfcSVikram Hegde 		*ip = B_TRUE;
14150200e77SFrank Van Der Linden 		ddi_err(DER_NOTE, dip, "iommu: Mobile 4 chipset detected. "
1423a634bfcSVikram Hegde 		    "Force setting IOMMU write buffer");
1433a634bfcSVikram Hegde 		return (DDI_WALK_TERMINATE);
1443a634bfcSVikram Hegde 	} else {
1453a634bfcSVikram Hegde 		return (DDI_WALK_CONTINUE);
1463a634bfcSVikram Hegde 	}
1473a634bfcSVikram Hegde }
1483a634bfcSVikram Hegde 
1493a634bfcSVikram Hegde static void
map_bios_rsvd_mem(dev_info_t * dip)1503a634bfcSVikram Hegde map_bios_rsvd_mem(dev_info_t *dip)
1513a634bfcSVikram Hegde {
1523a634bfcSVikram Hegde 	struct memlist *mp;
15350200e77SFrank Van Der Linden 
15450200e77SFrank Van Der Linden 	/*
15550200e77SFrank Van Der Linden 	 * Make sure the domain for the device is set up before
15650200e77SFrank Van Der Linden 	 * mapping anything.
15750200e77SFrank Van Der Linden 	 */
15850200e77SFrank Van Der Linden 	(void) immu_dvma_device_setup(dip, 0);
1593a634bfcSVikram Hegde 
1603a634bfcSVikram Hegde 	memlist_read_lock();
1613a634bfcSVikram Hegde 
1623a634bfcSVikram Hegde 	mp = bios_rsvd;
1633a634bfcSVikram Hegde 	while (mp != NULL) {
164e03dceedSVikram Hegde 		memrng_t mrng = {0};
1653a634bfcSVikram Hegde 
16650200e77SFrank Van Der Linden 		ddi_err(DER_LOG, dip, "iommu: Mapping BIOS rsvd range "
1673a634bfcSVikram Hegde 		    "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address,
1683a634bfcSVikram Hegde 		    mp->ml_address + mp->ml_size);
1693a634bfcSVikram Hegde 
170e03dceedSVikram Hegde 		mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address);
171e03dceedSVikram Hegde 		mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE;
1723a634bfcSVikram Hegde 
17350200e77SFrank Van Der Linden 		(void) immu_map_memrange(dip, &mrng);
1743a634bfcSVikram Hegde 
1753a634bfcSVikram Hegde 		mp = mp->ml_next;
1763a634bfcSVikram Hegde 	}
1773a634bfcSVikram Hegde 
1783a634bfcSVikram Hegde 	memlist_read_unlock();
1793a634bfcSVikram Hegde }
1803a634bfcSVikram Hegde 
181e03dceedSVikram Hegde 
182e03dceedSVikram Hegde /*
1839e986f0eSFrank Van Der Linden  * Check if the driver requests a specific type of mapping.
184e03dceedSVikram Hegde  */
185e03dceedSVikram Hegde /*ARGSUSED*/
186e03dceedSVikram Hegde static void
check_conf(dev_info_t * dip,void * arg)1879e986f0eSFrank Van Der Linden check_conf(dev_info_t *dip, void *arg)
188e03dceedSVikram Hegde {
1899e986f0eSFrank Van Der Linden 	immu_devi_t *immu_devi;
1909e986f0eSFrank Van Der Linden 	const char *dname;
1919e986f0eSFrank Van Der Linden 	uint_t i;
19250200e77SFrank Van Der Linden 	int hasmapprop = 0, haspreprop = 0;
19350200e77SFrank Van Der Linden 	boolean_t old_premap;
194e03dceedSVikram Hegde 
195e03dceedSVikram Hegde 	/*
1969e986f0eSFrank Van Der Linden 	 * Only PCI devices can use an IOMMU. Legacy ISA devices
1979e986f0eSFrank Van Der Linden 	 * are handled in check_lpc.
198e03dceedSVikram Hegde 	 */
1999e986f0eSFrank Van Der Linden 	if (!DEVI_IS_PCI(dip))
2009e986f0eSFrank Van Der Linden 		return;
2019e986f0eSFrank Van Der Linden 
2029e986f0eSFrank Van Der Linden 	dname = ddi_driver_name(dip);
2039e986f0eSFrank Van Der Linden 	if (dname == NULL)
2049e986f0eSFrank Van Der Linden 		return;
2059e986f0eSFrank Van Der Linden 	immu_devi = immu_devi_get(dip);
2069e986f0eSFrank Van Der Linden 
2079e986f0eSFrank Van Der Linden 	for (i = 0; i < nunity; i++) {
2089e986f0eSFrank Van Der Linden 		if (strcmp(unity_driver_array[i], dname) == 0) {
20950200e77SFrank Van Der Linden 			hasmapprop = 1;
2109e986f0eSFrank Van Der Linden 			immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY;
2119e986f0eSFrank Van Der Linden 		}
2129e986f0eSFrank Van Der Linden 	}
2139e986f0eSFrank Van Der Linden 
2149e986f0eSFrank Van Der Linden 	for (i = 0; i < nxlate; i++) {
2159e986f0eSFrank Van Der Linden 		if (strcmp(xlate_driver_array[i], dname) == 0) {
21650200e77SFrank Van Der Linden 			hasmapprop = 1;
2179e986f0eSFrank Van Der Linden 			immu_devi->imd_dvma_flags &= ~IMMU_FLAGS_UNITY;
218e03dceedSVikram Hegde 		}
219e03dceedSVikram Hegde 	}
2209e986f0eSFrank Van Der Linden 
22150200e77SFrank Van Der Linden 	old_premap = immu_devi->imd_use_premap;
22250200e77SFrank Van Der Linden 
22350200e77SFrank Van Der Linden 	for (i = 0; i < nnopremap; i++) {
22450200e77SFrank Van Der Linden 		if (strcmp(nopremap_driver_array[i], dname) == 0) {
22550200e77SFrank Van Der Linden 			haspreprop = 1;
22650200e77SFrank Van Der Linden 			immu_devi->imd_use_premap = B_FALSE;
22750200e77SFrank Van Der Linden 		}
22850200e77SFrank Van Der Linden 	}
22950200e77SFrank Van Der Linden 
23050200e77SFrank Van Der Linden 	for (i = 0; i < npremap; i++) {
23150200e77SFrank Van Der Linden 		if (strcmp(premap_driver_array[i], dname) == 0) {
23250200e77SFrank Van Der Linden 			haspreprop = 1;
23350200e77SFrank Van Der Linden 			immu_devi->imd_use_premap = B_TRUE;
23450200e77SFrank Van Der Linden 		}
23550200e77SFrank Van Der Linden 	}
23650200e77SFrank Van Der Linden 
2379e986f0eSFrank Van Der Linden 	/*
2389e986f0eSFrank Van Der Linden 	 * Report if we changed the value from the default.
2399e986f0eSFrank Van Der Linden 	 */
24050200e77SFrank Van Der Linden 	if (hasmapprop && (immu_devi->imd_dvma_flags ^ immu_global_dvma_flags))
2419e986f0eSFrank Van Der Linden 		ddi_err(DER_LOG, dip, "using %s DVMA mapping",
2429e986f0eSFrank Van Der Linden 		    immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY ?
2439e986f0eSFrank Van Der Linden 		    DDI_DVMA_MAPTYPE_UNITY : DDI_DVMA_MAPTYPE_XLATE);
24450200e77SFrank Van Der Linden 
24550200e77SFrank Van Der Linden 	if (haspreprop && (immu_devi->imd_use_premap != old_premap))
24650200e77SFrank Van Der Linden 		ddi_err(DER_LOG, dip, "%susing premapped DVMA space",
24750200e77SFrank Van Der Linden 		    immu_devi->imd_use_premap ? "" : "not ");
248e03dceedSVikram Hegde }
249e03dceedSVikram Hegde 
2503a634bfcSVikram Hegde /*
2513a634bfcSVikram Hegde  * Check if the device is USB controller
2523a634bfcSVikram Hegde  */
2533a634bfcSVikram Hegde /*ARGSUSED*/
2543a634bfcSVikram Hegde static void
check_usb(dev_info_t * dip,void * arg)2553a634bfcSVikram Hegde check_usb(dev_info_t *dip, void *arg)
2563a634bfcSVikram Hegde {
2573a634bfcSVikram Hegde 	const char *drv = ddi_driver_name(dip);
2589e986f0eSFrank Van Der Linden 	immu_devi_t *immu_devi;
2599e986f0eSFrank Van Der Linden 
2603a634bfcSVikram Hegde 
261993e3fafSRobert Mustacchi 	/*
262993e3fafSRobert Mustacchi 	 * It's not clear if xHCI really needs these quirks; however, to be on
263993e3fafSRobert Mustacchi 	 * the safe side until we know for certain we add it to the list below.
264993e3fafSRobert Mustacchi 	 */
2653a634bfcSVikram Hegde 	if (drv == NULL ||
2663a634bfcSVikram Hegde 	    (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 &&
267993e3fafSRobert Mustacchi 	    strcmp(drv, "ehci") != 0 && strcmp(drv, "xhci") != 0)) {
2683a634bfcSVikram Hegde 		return;
2693a634bfcSVikram Hegde 	}
2703a634bfcSVikram Hegde 
2719e986f0eSFrank Van Der Linden 	immu_devi = immu_devi_get(dip);
2729e986f0eSFrank Van Der Linden 
2739e986f0eSFrank Van Der Linden 	/*
2749e986f0eSFrank Van Der Linden 	 * If unit mappings are already specified, globally or
2759e986f0eSFrank Van Der Linden 	 * locally, we're done here, since that covers both
2769e986f0eSFrank Van Der Linden 	 * quirks below.
2779e986f0eSFrank Van Der Linden 	 */
2789e986f0eSFrank Van Der Linden 	if (immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY)
2799e986f0eSFrank Van Der Linden 		return;
2809e986f0eSFrank Van Der Linden 
2813a634bfcSVikram Hegde 	/* This must come first since it does unity mapping */
2823a634bfcSVikram Hegde 	if (immu_quirk_usbfullpa == B_TRUE) {
2839e986f0eSFrank Van Der Linden 		immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY;
2849e986f0eSFrank Van Der Linden 	} else if (immu_quirk_usbrmrr == B_TRUE) {
2853a634bfcSVikram Hegde 		ddi_err(DER_LOG, dip, "Applying USB RMRR quirk");
2863a634bfcSVikram Hegde 		map_bios_rsvd_mem(dip);
2873a634bfcSVikram Hegde 	}
2883a634bfcSVikram Hegde }
2893a634bfcSVikram Hegde 
2903a634bfcSVikram Hegde /*
2913a634bfcSVikram Hegde  * Check if the device is a LPC device
2923a634bfcSVikram Hegde  */
2933a634bfcSVikram Hegde /*ARGSUSED*/
2943a634bfcSVikram Hegde static void
check_lpc(dev_info_t * dip,void * arg)2953a634bfcSVikram Hegde check_lpc(dev_info_t *dip, void *arg)
2963a634bfcSVikram Hegde {
2973a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
2983a634bfcSVikram Hegde 
2993a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
3003a634bfcSVikram Hegde 	if (immu_devi->imd_lpc == B_TRUE) {
30150200e77SFrank Van Der Linden 		ddi_err(DER_LOG, dip, "iommu: Found LPC device");
3023a634bfcSVikram Hegde 		/* This will put the immu_devi on the LPC "specials" list */
30350200e77SFrank Van Der Linden 		(void) immu_dvma_device_setup(dip, IMMU_FLAGS_SLEEP);
3043a634bfcSVikram Hegde 	}
3053a634bfcSVikram Hegde }
3063a634bfcSVikram Hegde 
3073a634bfcSVikram Hegde /*
3083a634bfcSVikram Hegde  * Check if the device is a GFX device
3093a634bfcSVikram Hegde  */
3103a634bfcSVikram Hegde /*ARGSUSED*/
3113a634bfcSVikram Hegde static void
check_gfx(dev_info_t * dip,void * arg)3123a634bfcSVikram Hegde check_gfx(dev_info_t *dip, void *arg)
3133a634bfcSVikram Hegde {
3143a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
3153a634bfcSVikram Hegde 
3163a634bfcSVikram Hegde 	immu_devi = immu_devi_get(dip);
3173a634bfcSVikram Hegde 	if (immu_devi->imd_display == B_TRUE) {
3189e986f0eSFrank Van Der Linden 		immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY;
31950200e77SFrank Van Der Linden 		ddi_err(DER_LOG, dip, "iommu: Found GFX device");
3203a634bfcSVikram Hegde 		/* This will put the immu_devi on the GFX "specials" list */
3213a634bfcSVikram Hegde 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
3223a634bfcSVikram Hegde 	}
3233a634bfcSVikram Hegde }
3243a634bfcSVikram Hegde 
3253a634bfcSVikram Hegde static void
walk_tree(int (* f)(dev_info_t *,void *),void * arg)3263a634bfcSVikram Hegde walk_tree(int (*f)(dev_info_t *, void *), void *arg)
3273a634bfcSVikram Hegde {
3283fe80ca4SDan Cross 	ndi_devi_enter(root_devinfo);
3293a634bfcSVikram Hegde 	ddi_walk_devs(ddi_get_child(root_devinfo), f, arg);
3303fe80ca4SDan Cross 	ndi_devi_exit(root_devinfo);
3313a634bfcSVikram Hegde }
3323a634bfcSVikram Hegde 
3333a634bfcSVikram Hegde static int
check_pre_setup_quirks(dev_info_t * dip,void * arg)3343a634bfcSVikram Hegde check_pre_setup_quirks(dev_info_t *dip, void *arg)
3353a634bfcSVikram Hegde {
3363a634bfcSVikram Hegde 	/* just 1 check right now */
3373a634bfcSVikram Hegde 	return (check_mobile4(dip, arg));
3383a634bfcSVikram Hegde }
3393a634bfcSVikram Hegde 
3403a634bfcSVikram Hegde static int
check_pre_startup_quirks(dev_info_t * dip,void * arg)3413a634bfcSVikram Hegde check_pre_startup_quirks(dev_info_t *dip, void *arg)
3423a634bfcSVikram Hegde {
3433a634bfcSVikram Hegde 	if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) {
3443a634bfcSVikram Hegde 		ddi_err(DER_PANIC, dip, "Failed to get immu_devi");
3453a634bfcSVikram Hegde 	}
3463a634bfcSVikram Hegde 
3473a634bfcSVikram Hegde 	check_gfx(dip, arg);
3483a634bfcSVikram Hegde 
3493a634bfcSVikram Hegde 	check_lpc(dip, arg);
3503a634bfcSVikram Hegde 
3519e986f0eSFrank Van Der Linden 	check_conf(dip, arg);
3523a634bfcSVikram Hegde 
3539e986f0eSFrank Van Der Linden 	check_usb(dip, arg);
354e03dceedSVikram Hegde 
3553a634bfcSVikram Hegde 	return (DDI_WALK_CONTINUE);
3563a634bfcSVikram Hegde }
3573a634bfcSVikram Hegde 
3583a634bfcSVikram Hegde static void
pre_setup_quirks(void)3593a634bfcSVikram Hegde pre_setup_quirks(void)
3603a634bfcSVikram Hegde {
3613a634bfcSVikram Hegde 	walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4);
3623a634bfcSVikram Hegde }
3633a634bfcSVikram Hegde 
3643a634bfcSVikram Hegde static void
pre_startup_quirks(void)3653a634bfcSVikram Hegde pre_startup_quirks(void)
3663a634bfcSVikram Hegde {
3673a634bfcSVikram Hegde 	walk_tree(check_pre_startup_quirks, NULL);
3683a634bfcSVikram Hegde 
3693a634bfcSVikram Hegde 	immu_dmar_rmrr_map();
3703a634bfcSVikram Hegde }
3713a634bfcSVikram Hegde 
3729e986f0eSFrank Van Der Linden static int
get_conf_str(char * bopt,char ** val)3739e986f0eSFrank Van Der Linden get_conf_str(char *bopt, char **val)
3749e986f0eSFrank Van Der Linden {
3759e986f0eSFrank Van Der Linden 	int ret;
3769e986f0eSFrank Van Der Linden 
3779e986f0eSFrank Van Der Linden 	/*
3789e986f0eSFrank Van Der Linden 	 * Check the rootnex.conf property
3799e986f0eSFrank Van Der Linden 	 * Fake up a dev_t since searching the global
3809e986f0eSFrank Van Der Linden 	 * property list needs it
3819e986f0eSFrank Van Der Linden 	 */
3829e986f0eSFrank Van Der Linden 	ret = ddi_prop_lookup_string(
3839e986f0eSFrank Van Der Linden 	    makedevice(ddi_name_to_major("rootnex"), 0),
3849e986f0eSFrank Van Der Linden 	    root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
3859e986f0eSFrank Van Der Linden 	    bopt, val);
3869e986f0eSFrank Van Der Linden 
3879e986f0eSFrank Van Der Linden 	return (ret);
3889e986f0eSFrank Van Der Linden }
3899e986f0eSFrank Van Der Linden 
3903adb2334SVikram Hegde /*
3913adb2334SVikram Hegde  * get_conf_opt()
392*d5ebc493SDan Cross  *	get a rootnex.conf setting  (always a boolean)
3933adb2334SVikram Hegde  */
3943adb2334SVikram Hegde static void
get_conf_opt(char * bopt,boolean_t * kvar)3953adb2334SVikram Hegde get_conf_opt(char *bopt, boolean_t *kvar)
3963adb2334SVikram Hegde {
3973adb2334SVikram Hegde 	char *val = NULL;
3983adb2334SVikram Hegde 
3993adb2334SVikram Hegde 	/*
4003adb2334SVikram Hegde 	 * Check the rootnex.conf property
4013adb2334SVikram Hegde 	 * Fake up a dev_t since searching the global
4023adb2334SVikram Hegde 	 * property list needs it
4033adb2334SVikram Hegde 	 */
4049e986f0eSFrank Van Der Linden 
4059e986f0eSFrank Van Der Linden 	if (get_conf_str(bopt, &val) != DDI_PROP_SUCCESS)
4063adb2334SVikram Hegde 		return;
4073adb2334SVikram Hegde 
4083adb2334SVikram Hegde 	if (strcmp(val, "true") == 0) {
4093adb2334SVikram Hegde 		*kvar = B_TRUE;
4103adb2334SVikram Hegde 	} else if (strcmp(val, "false") == 0) {
4113adb2334SVikram Hegde 		*kvar = B_FALSE;
4123adb2334SVikram Hegde 	} else {
4133adb2334SVikram Hegde 		ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ",
4143adb2334SVikram Hegde 		    "is not set to true or false. Ignoring option.",
4153adb2334SVikram Hegde 		    bopt, val);
4163adb2334SVikram Hegde 	}
4173adb2334SVikram Hegde 	ddi_prop_free(val);
4183adb2334SVikram Hegde }
4193adb2334SVikram Hegde 
4203a634bfcSVikram Hegde /*
4213a634bfcSVikram Hegde  * get_bootopt()
422*d5ebc493SDan Cross  *	check a boot option  (always a boolean)
4233a634bfcSVikram Hegde  */
4249e986f0eSFrank Van Der Linden static int
get_boot_str(char * bopt,char ** val)4259e986f0eSFrank Van Der Linden get_boot_str(char *bopt, char **val)
4269e986f0eSFrank Van Der Linden {
4279e986f0eSFrank Van Der Linden 	int ret;
4289e986f0eSFrank Van Der Linden 
4299e986f0eSFrank Van Der Linden 	ret = ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo,
4309e986f0eSFrank Van Der Linden 	    DDI_PROP_DONTPASS, bopt, val);
4319e986f0eSFrank Van Der Linden 
4329e986f0eSFrank Van Der Linden 	return (ret);
4339e986f0eSFrank Van Der Linden }
4349e986f0eSFrank Van Der Linden 
4353a634bfcSVikram Hegde static void
get_bootopt(char * bopt,boolean_t * kvar)4363a634bfcSVikram Hegde get_bootopt(char *bopt, boolean_t *kvar)
4373a634bfcSVikram Hegde {
4383a634bfcSVikram Hegde 	char *val = NULL;
4393a634bfcSVikram Hegde 
4403a634bfcSVikram Hegde 	/*
4413a634bfcSVikram Hegde 	 * All boot options set at the GRUB menu become
4423a634bfcSVikram Hegde 	 * properties on the rootnex.
4433a634bfcSVikram Hegde 	 */
4449e986f0eSFrank Van Der Linden 	if (get_boot_str(bopt, &val) != DDI_PROP_SUCCESS)
4459e986f0eSFrank Van Der Linden 		return;
4469e986f0eSFrank Van Der Linden 
4479e986f0eSFrank Van Der Linden 	if (strcmp(val, "true") == 0) {
4489e986f0eSFrank Van Der Linden 		*kvar = B_TRUE;
4499e986f0eSFrank Van Der Linden 	} else if (strcmp(val, "false") == 0) {
4509e986f0eSFrank Van Der Linden 		*kvar = B_FALSE;
4519e986f0eSFrank Van Der Linden 	} else {
4529e986f0eSFrank Van Der Linden 		ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ",
4539e986f0eSFrank Van Der Linden 		    "is not set to true or false. Ignoring option.",
4549e986f0eSFrank Van Der Linden 		    bopt, val);
4559e986f0eSFrank Van Der Linden 	}
4569e986f0eSFrank Van Der Linden 	ddi_prop_free(val);
4579e986f0eSFrank Van Der Linden }
4589e986f0eSFrank Van Der Linden 
4599e986f0eSFrank Van Der Linden static void
get_boot_dvma_mode(void)4609e986f0eSFrank Van Der Linden get_boot_dvma_mode(void)
4619e986f0eSFrank Van Der Linden {
4629e986f0eSFrank Van Der Linden 	char *val = NULL;
4639e986f0eSFrank Van Der Linden 
4649e986f0eSFrank Van Der Linden 	if (get_boot_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val)
4659e986f0eSFrank Van Der Linden 	    != DDI_PROP_SUCCESS)
4669e986f0eSFrank Van Der Linden 		return;
4679e986f0eSFrank Van Der Linden 
4689e986f0eSFrank Van Der Linden 	if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) {
4699e986f0eSFrank Van Der Linden 		immu_global_dvma_flags |= IMMU_FLAGS_UNITY;
4709e986f0eSFrank Van Der Linden 	} else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) {
4719e986f0eSFrank Van Der Linden 		immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY;
4729e986f0eSFrank Van Der Linden 	} else {
4739e986f0eSFrank Van Der Linden 		ddi_err(DER_WARN, NULL, "bad value \"%s\" for boot option %s",
4749e986f0eSFrank Van Der Linden 		    val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP);
4753a634bfcSVikram Hegde 	}
4769e986f0eSFrank Van Der Linden 	ddi_prop_free(val);
4773a634bfcSVikram Hegde }
4783a634bfcSVikram Hegde 
4799e986f0eSFrank Van Der Linden static void
get_conf_dvma_mode(void)4809e986f0eSFrank Van Der Linden get_conf_dvma_mode(void)
4819e986f0eSFrank Van Der Linden {
4829e986f0eSFrank Van Der Linden 	char *val = NULL;
4839e986f0eSFrank Van Der Linden 
4849e986f0eSFrank Van Der Linden 	if (get_conf_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val)
4859e986f0eSFrank Van Der Linden 	    != DDI_PROP_SUCCESS)
4869e986f0eSFrank Van Der Linden 		return;
4879e986f0eSFrank Van Der Linden 
4889e986f0eSFrank Van Der Linden 	if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) {
4899e986f0eSFrank Van Der Linden 		immu_global_dvma_flags |= IMMU_FLAGS_UNITY;
4909e986f0eSFrank Van Der Linden 	} else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) {
4919e986f0eSFrank Van Der Linden 		immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY;
4929e986f0eSFrank Van Der Linden 	} else {
4939e986f0eSFrank Van Der Linden 		ddi_err(DER_WARN, NULL, "bad value \"%s\" for rootnex "
4949e986f0eSFrank Van Der Linden 		    "option %s", val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP);
4959e986f0eSFrank Van Der Linden 	}
4969e986f0eSFrank Van Der Linden 	ddi_prop_free(val);
4979e986f0eSFrank Van Der Linden }
4989e986f0eSFrank Van Der Linden 
4999e986f0eSFrank Van Der Linden 
500e03dceedSVikram Hegde static void
get_conf_tunables(char * bopt,int64_t * ivar)5013adb2334SVikram Hegde get_conf_tunables(char *bopt, int64_t *ivar)
502e03dceedSVikram Hegde {
503e03dceedSVikram Hegde 	int64_t	*iarray;
504e03dceedSVikram Hegde 	uint_t n;
505e03dceedSVikram Hegde 
506e03dceedSVikram Hegde 	/*
507e03dceedSVikram Hegde 	 * Check the rootnex.conf property
508e03dceedSVikram Hegde 	 * Fake up a dev_t since searching the global
509e03dceedSVikram Hegde 	 * property list needs it
510e03dceedSVikram Hegde 	 */
511e03dceedSVikram Hegde 	if (ddi_prop_lookup_int64_array(
512e03dceedSVikram Hegde 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
513e03dceedSVikram Hegde 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt,
514e03dceedSVikram Hegde 	    &iarray, &n) != DDI_PROP_SUCCESS) {
515e03dceedSVikram Hegde 		return;
516e03dceedSVikram Hegde 	}
517e03dceedSVikram Hegde 
518e03dceedSVikram Hegde 	if (n != 1) {
519e03dceedSVikram Hegde 		ddi_err(DER_WARN, NULL, "More than one value specified for "
520e03dceedSVikram Hegde 		    "%s property. Ignoring and using default",
521e03dceedSVikram Hegde 		    "immu-flush-gran");
522e03dceedSVikram Hegde 		ddi_prop_free(iarray);
523e03dceedSVikram Hegde 		return;
524e03dceedSVikram Hegde 	}
525e03dceedSVikram Hegde 
526e03dceedSVikram Hegde 	if (iarray[0] < 0) {
527e03dceedSVikram Hegde 		ddi_err(DER_WARN, NULL, "Negative value specified for "
528e03dceedSVikram Hegde 		    "%s property. Inoring and Using default value",
529e03dceedSVikram Hegde 		    "immu-flush-gran");
530e03dceedSVikram Hegde 		ddi_prop_free(iarray);
531e03dceedSVikram Hegde 		return;
532e03dceedSVikram Hegde 	}
533e03dceedSVikram Hegde 
534e03dceedSVikram Hegde 	*ivar = iarray[0];
535e03dceedSVikram Hegde 
536e03dceedSVikram Hegde 	ddi_prop_free(iarray);
537e03dceedSVikram Hegde }
538e03dceedSVikram Hegde 
5393adb2334SVikram Hegde static void
read_conf_options(void)5403adb2334SVikram Hegde read_conf_options(void)
5413adb2334SVikram Hegde {
5423adb2334SVikram Hegde 	/* enable/disable options */
5433adb2334SVikram Hegde 	get_conf_opt("immu-enable", &immu_enable);
5443adb2334SVikram Hegde 	get_conf_opt("immu-dvma-enable", &immu_dvma_enable);
5453adb2334SVikram Hegde 	get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
5463adb2334SVikram Hegde 	get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable);
5473adb2334SVikram Hegde 	get_conf_opt("immu-qinv-enable", &immu_qinv_enable);
5483adb2334SVikram Hegde 
5493adb2334SVikram Hegde 	/* workaround switches */
5503adb2334SVikram Hegde 	get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
5513adb2334SVikram Hegde 	get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
5523adb2334SVikram Hegde 	get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
5533adb2334SVikram Hegde 
5543adb2334SVikram Hegde 	/* debug printing */
5553adb2334SVikram Hegde 	get_conf_opt("immu-dmar-print", &immu_dmar_print);
5563adb2334SVikram Hegde 
5573adb2334SVikram Hegde 	/* get tunables */
5583adb2334SVikram Hegde 	get_conf_tunables("immu-flush-gran", &immu_flush_gran);
5599e986f0eSFrank Van Der Linden 
5609e986f0eSFrank Van Der Linden 	get_conf_dvma_mode();
5613adb2334SVikram Hegde }
5623adb2334SVikram Hegde 
5633a634bfcSVikram Hegde static void
read_boot_options(void)5643a634bfcSVikram Hegde read_boot_options(void)
5653a634bfcSVikram Hegde {
5663a634bfcSVikram Hegde 	/* enable/disable options */
5673a634bfcSVikram Hegde 	get_bootopt("immu-enable", &immu_enable);
5683a634bfcSVikram Hegde 	get_bootopt("immu-dvma-enable", &immu_dvma_enable);
5693a634bfcSVikram Hegde 	get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
5703a634bfcSVikram Hegde 	get_bootopt("immu-intrmap-enable", &immu_intrmap_enable);
5713a634bfcSVikram Hegde 	get_bootopt("immu-qinv-enable", &immu_qinv_enable);
5723a634bfcSVikram Hegde 
5733a634bfcSVikram Hegde 	/* workaround switches */
5743a634bfcSVikram Hegde 	get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
5753a634bfcSVikram Hegde 	get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
5763a634bfcSVikram Hegde 	get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
5773a634bfcSVikram Hegde 
5783a634bfcSVikram Hegde 	/* debug printing */
5793a634bfcSVikram Hegde 	get_bootopt("immu-dmar-print", &immu_dmar_print);
5809e986f0eSFrank Van Der Linden 
5819e986f0eSFrank Van Der Linden 	get_boot_dvma_mode();
5829e986f0eSFrank Van Der Linden }
5839e986f0eSFrank Van Der Linden 
5849e986f0eSFrank Van Der Linden static void
mapping_list_setup(void)5859e986f0eSFrank Van Der Linden mapping_list_setup(void)
5869e986f0eSFrank Van Der Linden {
5879e986f0eSFrank Van Der Linden 	char **string_array;
5889e986f0eSFrank Van Der Linden 	uint_t nstrings;
5899e986f0eSFrank Van Der Linden 
5909e986f0eSFrank Van Der Linden 	if (ddi_prop_lookup_string_array(
5919e986f0eSFrank Van Der Linden 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
5929e986f0eSFrank Van Der Linden 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
5939e986f0eSFrank Van Der Linden 	    "immu-dvma-unity-drivers",
5949e986f0eSFrank Van Der Linden 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
5959e986f0eSFrank Van Der Linden 		unity_driver_array = string_array;
5969e986f0eSFrank Van Der Linden 		nunity = nstrings;
5979e986f0eSFrank Van Der Linden 	}
5989e986f0eSFrank Van Der Linden 
5999e986f0eSFrank Van Der Linden 	if (ddi_prop_lookup_string_array(
6009e986f0eSFrank Van Der Linden 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
6019e986f0eSFrank Van Der Linden 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
6029e986f0eSFrank Van Der Linden 	    "immu-dvma-xlate-drivers",
6039e986f0eSFrank Van Der Linden 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
6049e986f0eSFrank Van Der Linden 		xlate_driver_array = string_array;
6059e986f0eSFrank Van Der Linden 		nxlate = nstrings;
6069e986f0eSFrank Van Der Linden 	}
60750200e77SFrank Van Der Linden 
60850200e77SFrank Van Der Linden 	if (ddi_prop_lookup_string_array(
60950200e77SFrank Van Der Linden 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
61050200e77SFrank Van Der Linden 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
61150200e77SFrank Van Der Linden 	    "immu-dvma-premap-drivers",
61250200e77SFrank Van Der Linden 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
61350200e77SFrank Van Der Linden 		premap_driver_array = string_array;
61450200e77SFrank Van Der Linden 		npremap = nstrings;
61550200e77SFrank Van Der Linden 	}
61650200e77SFrank Van Der Linden 
61750200e77SFrank Van Der Linden 	if (ddi_prop_lookup_string_array(
61850200e77SFrank Van Der Linden 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
61950200e77SFrank Van Der Linden 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
62050200e77SFrank Van Der Linden 	    "immu-dvma-nopremap-drivers",
62150200e77SFrank Van Der Linden 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
62250200e77SFrank Van Der Linden 		nopremap_driver_array = string_array;
62350200e77SFrank Van Der Linden 		nnopremap = nstrings;
62450200e77SFrank Van Der Linden 	}
6253a634bfcSVikram Hegde }
6263a634bfcSVikram Hegde 
6273a634bfcSVikram Hegde /*
6283a634bfcSVikram Hegde  * Note, this will not catch hardware not enumerated
6293a634bfcSVikram Hegde  * in early boot
6303a634bfcSVikram Hegde  */
6313a634bfcSVikram Hegde static boolean_t
blacklisted_driver(void)6323a634bfcSVikram Hegde blacklisted_driver(void)
6333a634bfcSVikram Hegde {
6343a634bfcSVikram Hegde 	char **strptr;
6353a634bfcSVikram Hegde 	int i;
6363a634bfcSVikram Hegde 	major_t maj;
6373a634bfcSVikram Hegde 
6383a634bfcSVikram Hegde 	/* need at least 2 strings */
6393a634bfcSVikram Hegde 	if (nblacks < 2) {
6403a634bfcSVikram Hegde 		return (B_FALSE);
6413a634bfcSVikram Hegde 	}
6423a634bfcSVikram Hegde 
6433a634bfcSVikram Hegde 	for (i = 0; nblacks - i > 1; i++) {
644e03dceedSVikram Hegde 		strptr = &black_array[i];
6453a634bfcSVikram Hegde 		if (strcmp(*strptr++, "DRIVER") == 0) {
6463a634bfcSVikram Hegde 			if ((maj = ddi_name_to_major(*strptr++))
6473a634bfcSVikram Hegde 			    != DDI_MAJOR_T_NONE) {
6483a634bfcSVikram Hegde 				/* is there hardware bound to this drvr */
6493a634bfcSVikram Hegde 				if (devnamesp[maj].dn_head != NULL) {
6503a634bfcSVikram Hegde 					return (B_TRUE);
6513a634bfcSVikram Hegde 				}
6523a634bfcSVikram Hegde 			}
6533a634bfcSVikram Hegde 			i += 1;   /* for loop adds 1, so add only 1 here */
6543a634bfcSVikram Hegde 		}
6553a634bfcSVikram Hegde 	}
6563a634bfcSVikram Hegde 
6573a634bfcSVikram Hegde 	return (B_FALSE);
6583a634bfcSVikram Hegde }
6593a634bfcSVikram Hegde 
6603a634bfcSVikram Hegde static boolean_t
blacklisted_smbios(void)6613a634bfcSVikram Hegde blacklisted_smbios(void)
6623a634bfcSVikram Hegde {
6633a634bfcSVikram Hegde 	id_t smid;
6643a634bfcSVikram Hegde 	smbios_hdl_t *smhdl;
6653a634bfcSVikram Hegde 	smbios_info_t sminf;
6663a634bfcSVikram Hegde 	smbios_system_t smsys;
6673a634bfcSVikram Hegde 	char *mfg, *product, *version;
6683a634bfcSVikram Hegde 	char **strptr;
6693a634bfcSVikram Hegde 	int i;
6703a634bfcSVikram Hegde 
6713a634bfcSVikram Hegde 	/* need at least 4 strings for this setting */
6723a634bfcSVikram Hegde 	if (nblacks < 4) {
6733a634bfcSVikram Hegde 		return (B_FALSE);
6743a634bfcSVikram Hegde 	}
6753a634bfcSVikram Hegde 
6763a634bfcSVikram Hegde 	smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
6773a634bfcSVikram Hegde 	if (smhdl == NULL ||
6783a634bfcSVikram Hegde 	    (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR ||
6793a634bfcSVikram Hegde 	    smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) {
6803a634bfcSVikram Hegde 		return (B_FALSE);
6813a634bfcSVikram Hegde 	}
6823a634bfcSVikram Hegde 
6833a634bfcSVikram Hegde 	mfg = (char *)sminf.smbi_manufacturer;
6843a634bfcSVikram Hegde 	product = (char *)sminf.smbi_product;
6853a634bfcSVikram Hegde 	version = (char *)sminf.smbi_version;
6863a634bfcSVikram Hegde 
6873a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n");
6883a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg);
6893a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product);
6903a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version);
6913a634bfcSVikram Hegde 
6923a634bfcSVikram Hegde 	for (i = 0; nblacks - i > 3; i++) {
693e03dceedSVikram Hegde 		strptr = &black_array[i];
6943a634bfcSVikram Hegde 		if (strcmp(*strptr++, "SMBIOS") == 0) {
6953a634bfcSVikram Hegde 			if (strcmp(*strptr++, mfg) == 0 &&
696243bebc0SToomas Soome 			    (*strptr[0] == '\0' ||
6973a634bfcSVikram Hegde 			    strcmp(*strptr++, product) == 0) &&
698243bebc0SToomas Soome 			    (*strptr[0] == '\0' ||
6993a634bfcSVikram Hegde 			    strcmp(*strptr++, version) == 0)) {
7003a634bfcSVikram Hegde 				return (B_TRUE);
7013a634bfcSVikram Hegde 			}
7023a634bfcSVikram Hegde 			i += 3;
7033a634bfcSVikram Hegde 		}
7043a634bfcSVikram Hegde 	}
7053a634bfcSVikram Hegde 
7063a634bfcSVikram Hegde 	return (B_FALSE);
7073a634bfcSVikram Hegde }
7083a634bfcSVikram Hegde 
7093a634bfcSVikram Hegde static boolean_t
blacklisted_acpi(void)7103a634bfcSVikram Hegde blacklisted_acpi(void)
7113a634bfcSVikram Hegde {
7123a634bfcSVikram Hegde 	if (nblacks == 0) {
7133a634bfcSVikram Hegde 		return (B_FALSE);
7143a634bfcSVikram Hegde 	}
7153a634bfcSVikram Hegde 
7163a634bfcSVikram Hegde 	return (immu_dmar_blacklisted(black_array, nblacks));
7173a634bfcSVikram Hegde }
7183a634bfcSVikram Hegde 
7193a634bfcSVikram Hegde /*
7203a634bfcSVikram Hegde  * Check if system is blacklisted by Intel IOMMU driver
7213a634bfcSVikram Hegde  * i.e. should Intel IOMMU be disabled on this system
7223a634bfcSVikram Hegde  * Currently a system can be blacklistd based on the
7233a634bfcSVikram Hegde  * following bases:
7243a634bfcSVikram Hegde  *
7253a634bfcSVikram Hegde  * 1. DMAR ACPI table information.
7263a634bfcSVikram Hegde  *    This information includes things like
7273a634bfcSVikram Hegde  *    manufacturer and revision number. If rootnex.conf
7283a634bfcSVikram Hegde  *    has matching info set in its blacklist property
7293a634bfcSVikram Hegde  *    then Intel IOMMu will be disabled
7303a634bfcSVikram Hegde  *
7313a634bfcSVikram Hegde  * 2. SMBIOS information
7323a634bfcSVikram Hegde  *
7333a634bfcSVikram Hegde  * 3. Driver installed - useful if a particular
7343a634bfcSVikram Hegde  *    driver or hardware is toxic if Intel IOMMU
7353a634bfcSVikram Hegde  *    is turned on.
7363a634bfcSVikram Hegde  */
7373a634bfcSVikram Hegde 
7383a634bfcSVikram Hegde static void
blacklist_setup(void)7393a634bfcSVikram Hegde blacklist_setup(void)
7403a634bfcSVikram Hegde {
7413a634bfcSVikram Hegde 	char **string_array;
7423a634bfcSVikram Hegde 	uint_t nstrings;
7433a634bfcSVikram Hegde 
7443a634bfcSVikram Hegde 	/*
7453a634bfcSVikram Hegde 	 * Check the rootnex.conf blacklist property.
7463a634bfcSVikram Hegde 	 * Fake up a dev_t since searching the global
7473a634bfcSVikram Hegde 	 * property list needs it
7483a634bfcSVikram Hegde 	 */
7493a634bfcSVikram Hegde 	if (ddi_prop_lookup_string_array(
7503a634bfcSVikram Hegde 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
7513a634bfcSVikram Hegde 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist",
7523a634bfcSVikram Hegde 	    &string_array, &nstrings) != DDI_PROP_SUCCESS) {
7533a634bfcSVikram Hegde 		return;
7543a634bfcSVikram Hegde 	}
7553a634bfcSVikram Hegde 
7563a634bfcSVikram Hegde 	/* smallest blacklist criteria works with multiples of 2 */
7573a634bfcSVikram Hegde 	if (nstrings % 2 != 0) {
7583a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist "
7593a634bfcSVikram Hegde 		    "rootnex.conf: number of strings must be a "
7603a634bfcSVikram Hegde 		    "multiple of 2");
7613a634bfcSVikram Hegde 		ddi_prop_free(string_array);
7623a634bfcSVikram Hegde 		return;
7633a634bfcSVikram Hegde 	}
7643a634bfcSVikram Hegde 
7653a634bfcSVikram Hegde 	black_array = string_array;
7663a634bfcSVikram Hegde 	nblacks = nstrings;
7673a634bfcSVikram Hegde }
7683a634bfcSVikram Hegde 
7693a634bfcSVikram Hegde static void
blacklist_destroy(void)7703a634bfcSVikram Hegde blacklist_destroy(void)
7713a634bfcSVikram Hegde {
7723a634bfcSVikram Hegde 	if (black_array) {
7733a634bfcSVikram Hegde 		ddi_prop_free(black_array);
7743a634bfcSVikram Hegde 		black_array = NULL;
7753a634bfcSVikram Hegde 		nblacks = 0;
7763a634bfcSVikram Hegde 	}
77750200e77SFrank Van Der Linden }
77850200e77SFrank Van Der Linden 
77950200e77SFrank Van Der Linden static char *
immu_alloc_name(const char * str,int instance)78050200e77SFrank Van Der Linden immu_alloc_name(const char *str, int instance)
78150200e77SFrank Van Der Linden {
78250200e77SFrank Van Der Linden 	size_t slen;
78350200e77SFrank Van Der Linden 	char *s;
7843a634bfcSVikram Hegde 
78550200e77SFrank Van Der Linden 	slen = strlen(str) + IMMU_ISTRLEN + 1;
78650200e77SFrank Van Der Linden 	s = kmem_zalloc(slen, VM_SLEEP);
78750200e77SFrank Van Der Linden 	if (s != NULL)
78850200e77SFrank Van Der Linden 		(void) snprintf(s, slen, "%s%d", str, instance);
78950200e77SFrank Van Der Linden 
79050200e77SFrank Van Der Linden 	return (s);
7913a634bfcSVikram Hegde }
7923a634bfcSVikram Hegde 
7933a634bfcSVikram Hegde 
7943a634bfcSVikram Hegde /*
7953a634bfcSVikram Hegde  * Now set all the fields in the order they are defined
7963a634bfcSVikram Hegde  * We do this only as a defensive-coding practice, it is
7973a634bfcSVikram Hegde  * not a correctness issue.
7983a634bfcSVikram Hegde  */
7993a634bfcSVikram Hegde static void *
immu_state_alloc(int seg,void * dmar_unit)8003a634bfcSVikram Hegde immu_state_alloc(int seg, void *dmar_unit)
8013a634bfcSVikram Hegde {
8023a634bfcSVikram Hegde 	immu_t *immu;
80350200e77SFrank Van Der Linden 	char *nodename, *hcachename, *pcachename;
80450200e77SFrank Van Der Linden 	int instance;
8053a634bfcSVikram Hegde 
8063a634bfcSVikram Hegde 	dmar_unit = immu_dmar_walk_units(seg, dmar_unit);
8073a634bfcSVikram Hegde 	if (dmar_unit == NULL) {
8083a634bfcSVikram Hegde 		/* No more IOMMUs in this segment */
8093a634bfcSVikram Hegde 		return (NULL);
8103a634bfcSVikram Hegde 	}
8113a634bfcSVikram Hegde 
8123a634bfcSVikram Hegde 	immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP);
8133a634bfcSVikram Hegde 
8143a634bfcSVikram Hegde 	mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL);
8153a634bfcSVikram Hegde 
8163a634bfcSVikram Hegde 	mutex_enter(&(immu->immu_lock));
8173a634bfcSVikram Hegde 
8183a634bfcSVikram Hegde 	immu->immu_dmar_unit = dmar_unit;
8193a634bfcSVikram Hegde 	immu->immu_dip = immu_dmar_unit_dip(dmar_unit);
8203a634bfcSVikram Hegde 
82150200e77SFrank Van Der Linden 	nodename = ddi_node_name(immu->immu_dip);
82250200e77SFrank Van Der Linden 	instance = ddi_get_instance(immu->immu_dip);
82350200e77SFrank Van Der Linden 
82450200e77SFrank Van Der Linden 	immu->immu_name = immu_alloc_name(nodename, instance);
82550200e77SFrank Van Der Linden 	if (immu->immu_name == NULL)
82650200e77SFrank Van Der Linden 		return (NULL);
82750200e77SFrank Van Der Linden 
8283a634bfcSVikram Hegde 	/*
8293a634bfcSVikram Hegde 	 * the immu_intr_lock mutex is grabbed by the IOMMU
8303a634bfcSVikram Hegde 	 * unit's interrupt handler so we need to use an
8313a634bfcSVikram Hegde 	 * interrupt cookie for the mutex
8323a634bfcSVikram Hegde 	 */
8333a634bfcSVikram Hegde 	mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER,
8343a634bfcSVikram Hegde 	    (void *)ipltospl(IMMU_INTR_IPL));
8353a634bfcSVikram Hegde 
8363a634bfcSVikram Hegde 	/* IOMMU regs related */
8373a634bfcSVikram Hegde 	mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL);
838e03dceedSVikram Hegde 	cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL);
839e03dceedSVikram Hegde 	immu->immu_regs_busy = B_FALSE;
8403a634bfcSVikram Hegde 
8413a634bfcSVikram Hegde 	/* DVMA related */
8423a634bfcSVikram Hegde 	immu->immu_dvma_coherent = B_FALSE;
8433a634bfcSVikram Hegde 
8443a634bfcSVikram Hegde 	/* DVMA context related */
8453a634bfcSVikram Hegde 	rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL);
8463a634bfcSVikram Hegde 
8473a634bfcSVikram Hegde 	/* DVMA domain related */
8483a634bfcSVikram Hegde 	list_create(&(immu->immu_domain_list), sizeof (domain_t),
8493a634bfcSVikram Hegde 	    offsetof(domain_t, dom_immu_node));
8503a634bfcSVikram Hegde 
8513a634bfcSVikram Hegde 	/* DVMA special device lists */
8523a634bfcSVikram Hegde 	immu->immu_dvma_gfx_only = B_FALSE;
8533a634bfcSVikram Hegde 	list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t),
8543a634bfcSVikram Hegde 	    offsetof(immu_devi_t, imd_spc_node));
8553a634bfcSVikram Hegde 	list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t),
8563a634bfcSVikram Hegde 	    offsetof(immu_devi_t, imd_spc_node));
8573a634bfcSVikram Hegde 
8583a634bfcSVikram Hegde 	/* interrupt remapping related */
8593a634bfcSVikram Hegde 	mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL);
8603a634bfcSVikram Hegde 
8613a634bfcSVikram Hegde 	/* qinv related */
8623a634bfcSVikram Hegde 	mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL);
8633a634bfcSVikram Hegde 
8643a634bfcSVikram Hegde 	/*
8653a634bfcSVikram Hegde 	 * insert this immu unit into the system-wide list
8663a634bfcSVikram Hegde 	 */
8673a634bfcSVikram Hegde 	list_insert_tail(&immu_list, immu);
8683a634bfcSVikram Hegde 
86950200e77SFrank Van Der Linden 	pcachename = immu_alloc_name("immu_pgtable_cache", instance);
87050200e77SFrank Van Der Linden 	if (pcachename == NULL)
87150200e77SFrank Van Der Linden 		return (NULL);
87250200e77SFrank Van Der Linden 
87350200e77SFrank Van Der Linden 	hcachename = immu_alloc_name("immu_hdl_cache", instance);
87450200e77SFrank Van Der Linden 	if (hcachename == NULL)
87550200e77SFrank Van Der Linden 		return (NULL);
87650200e77SFrank Van Der Linden 
87750200e77SFrank Van Der Linden 	immu->immu_pgtable_cache = kmem_cache_create(pcachename,
87850200e77SFrank Van Der Linden 	    sizeof (pgtable_t), 0, pgtable_ctor, pgtable_dtor, NULL, immu,
87950200e77SFrank Van Der Linden 	    NULL, 0);
88050200e77SFrank Van Der Linden 	immu->immu_hdl_cache = kmem_cache_create(hcachename,
88150200e77SFrank Van Der Linden 	    sizeof (immu_hdl_priv_t), 64, immu_hdl_priv_ctor,
88250200e77SFrank Van Der Linden 	    NULL, NULL, immu, NULL, 0);
88350200e77SFrank Van Der Linden 
8843a634bfcSVikram Hegde 	mutex_exit(&(immu->immu_lock));
8853a634bfcSVikram Hegde 
88650200e77SFrank Van Der Linden 	ddi_err(DER_LOG, immu->immu_dip, "unit setup");
8873a634bfcSVikram Hegde 
8883a634bfcSVikram Hegde 	immu_dmar_set_immu(dmar_unit, immu);
8893a634bfcSVikram Hegde 
8903a634bfcSVikram Hegde 	return (dmar_unit);
8913a634bfcSVikram Hegde }
8923a634bfcSVikram Hegde 
8933a634bfcSVikram Hegde static void
immu_subsystems_setup(void)8943a634bfcSVikram Hegde immu_subsystems_setup(void)
8953a634bfcSVikram Hegde {
8963a634bfcSVikram Hegde 	int seg;
8973a634bfcSVikram Hegde 	void *unit_hdl;
8983a634bfcSVikram Hegde 
8993a634bfcSVikram Hegde 	ddi_err(DER_VERB, NULL,
90050200e77SFrank Van Der Linden 	    "Creating state structures for Intel IOMMU units");
9013a634bfcSVikram Hegde 
9023a634bfcSVikram Hegde 	mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL);
9033a634bfcSVikram Hegde 	list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node));
9043a634bfcSVikram Hegde 
9053a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
9063a634bfcSVikram Hegde 
9073a634bfcSVikram Hegde 	unit_hdl = NULL;
9083a634bfcSVikram Hegde 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
9093a634bfcSVikram Hegde 		while (unit_hdl = immu_state_alloc(seg, unit_hdl)) {
9103a634bfcSVikram Hegde 			;
9113a634bfcSVikram Hegde 		}
9123a634bfcSVikram Hegde 	}
9133a634bfcSVikram Hegde 
9143a634bfcSVikram Hegde 	immu_regs_setup(&immu_list);	/* subsequent code needs this first */
9153a634bfcSVikram Hegde 	immu_dvma_setup(&immu_list);
916d2256d26SFrank Van Der Linden 	if (immu_qinv_setup(&immu_list) == DDI_SUCCESS)
917d2256d26SFrank Van Der Linden 		immu_intrmap_setup(&immu_list);
918d2256d26SFrank Van Der Linden 	else
919d2256d26SFrank Van Der Linden 		immu_intrmap_enable = B_FALSE;
9203a634bfcSVikram Hegde 
9213a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
9223a634bfcSVikram Hegde }
9233a634bfcSVikram Hegde 
9243a634bfcSVikram Hegde /*
9253a634bfcSVikram Hegde  * immu_subsystems_startup()
926*d5ebc493SDan Cross  *	startup all units that were setup
9273a634bfcSVikram Hegde  */
9283a634bfcSVikram Hegde static void
immu_subsystems_startup(void)9293a634bfcSVikram Hegde immu_subsystems_startup(void)
9303a634bfcSVikram Hegde {
9313a634bfcSVikram Hegde 	immu_t *immu;
93250200e77SFrank Van Der Linden 	iommulib_ops_t *iommulib_ops;
9333a634bfcSVikram Hegde 
9343a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
9353a634bfcSVikram Hegde 
9363a634bfcSVikram Hegde 	immu_dmar_startup();
9373a634bfcSVikram Hegde 
9383a634bfcSVikram Hegde 	immu = list_head(&immu_list);
9393a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
9403a634bfcSVikram Hegde 
9413a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
9423a634bfcSVikram Hegde 
9433a634bfcSVikram Hegde 		immu_intr_register(immu);
9443a634bfcSVikram Hegde 		immu_dvma_startup(immu);
9453a634bfcSVikram Hegde 		immu_intrmap_startup(immu);
9463a634bfcSVikram Hegde 		immu_qinv_startup(immu);
9473a634bfcSVikram Hegde 
9483a634bfcSVikram Hegde 		/*
9493a634bfcSVikram Hegde 		 * Set IOMMU unit's regs to do
9503a634bfcSVikram Hegde 		 * the actual startup. This will
9513a634bfcSVikram Hegde 		 * set immu->immu_running  field
9523a634bfcSVikram Hegde 		 * if the unit is successfully
9533a634bfcSVikram Hegde 		 * started
9543a634bfcSVikram Hegde 		 */
9553a634bfcSVikram Hegde 		immu_regs_startup(immu);
9563a634bfcSVikram Hegde 
9573a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
95850200e77SFrank Van Der Linden 
95950200e77SFrank Van Der Linden 		iommulib_ops = kmem_alloc(sizeof (iommulib_ops_t), KM_SLEEP);
96050200e77SFrank Van Der Linden 		*iommulib_ops = immulib_ops;
96150200e77SFrank Van Der Linden 		iommulib_ops->ilops_data = (void *)immu;
96250200e77SFrank Van Der Linden 		(void) iommulib_iommu_register(immu->immu_dip, iommulib_ops,
96350200e77SFrank Van Der Linden 		    &immu->immu_iommulib_handle);
9643a634bfcSVikram Hegde 	}
9653a634bfcSVikram Hegde 
9663a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
9673a634bfcSVikram Hegde }
9683a634bfcSVikram Hegde 
9693a634bfcSVikram Hegde /* ##################  Intel IOMMU internal interfaces ###################### */
9703a634bfcSVikram Hegde 
9713a634bfcSVikram Hegde /*
9723a634bfcSVikram Hegde  * Internal interfaces for IOMMU code (i.e. not exported to rootnex
9733a634bfcSVikram Hegde  * or rest of system)
9743a634bfcSVikram Hegde  */
9753a634bfcSVikram Hegde 
9763a634bfcSVikram Hegde /*
9773a634bfcSVikram Hegde  * ddip can be NULL, in which case we walk up until we find the root dip
9783a634bfcSVikram Hegde  * NOTE: We never visit the root dip since its not a hardware node
9793a634bfcSVikram Hegde  */
9803a634bfcSVikram Hegde int
immu_walk_ancestor(dev_info_t * rdip,dev_info_t * ddip,int (* func)(dev_info_t *,void * arg),void * arg,int * lvlp,immu_flags_t immu_flags)9813a634bfcSVikram Hegde immu_walk_ancestor(
9823a634bfcSVikram Hegde 	dev_info_t *rdip,
9833a634bfcSVikram Hegde 	dev_info_t *ddip,
9843a634bfcSVikram Hegde 	int (*func)(dev_info_t *, void *arg),
9853a634bfcSVikram Hegde 	void *arg,
9863a634bfcSVikram Hegde 	int *lvlp,
9873a634bfcSVikram Hegde 	immu_flags_t immu_flags)
9883a634bfcSVikram Hegde {
9893a634bfcSVikram Hegde 	dev_info_t *pdip;
9903a634bfcSVikram Hegde 	int level;
9913a634bfcSVikram Hegde 	int error = DDI_SUCCESS;
9923a634bfcSVikram Hegde 
9933a634bfcSVikram Hegde 	/* ddip and immu can be NULL */
9943a634bfcSVikram Hegde 
9953a634bfcSVikram Hegde 	/* Hold rdip so that branch is not detached */
9963a634bfcSVikram Hegde 	ndi_hold_devi(rdip);
9973a634bfcSVikram Hegde 	for (pdip = rdip, level = 1; pdip && pdip != root_devinfo;
9983a634bfcSVikram Hegde 	    pdip = ddi_get_parent(pdip), level++) {
9993a634bfcSVikram Hegde 
10003a634bfcSVikram Hegde 		if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) {
10013a634bfcSVikram Hegde 			error = DDI_FAILURE;
10023a634bfcSVikram Hegde 			break;
10033a634bfcSVikram Hegde 		}
10043a634bfcSVikram Hegde 		if (func(pdip, arg) == DDI_WALK_TERMINATE) {
10053a634bfcSVikram Hegde 			break;
10063a634bfcSVikram Hegde 		}
10073a634bfcSVikram Hegde 		if (immu_flags & IMMU_FLAGS_DONTPASS) {
10083a634bfcSVikram Hegde 			break;
10093a634bfcSVikram Hegde 		}
10103a634bfcSVikram Hegde 		if (pdip == ddip) {
10113a634bfcSVikram Hegde 			break;
10123a634bfcSVikram Hegde 		}
10133a634bfcSVikram Hegde 	}
10143a634bfcSVikram Hegde 
10153a634bfcSVikram Hegde 	ndi_rele_devi(rdip);
10163a634bfcSVikram Hegde 
10173a634bfcSVikram Hegde 	if (lvlp)
10183a634bfcSVikram Hegde 		*lvlp = level;
10193a634bfcSVikram Hegde 
10203a634bfcSVikram Hegde 	return (error);
10213a634bfcSVikram Hegde }
10223a634bfcSVikram Hegde 
10233a634bfcSVikram Hegde /* ########################  Intel IOMMU entry points ####################### */
10243a634bfcSVikram Hegde /*
10253a634bfcSVikram Hegde  * immu_init()
10263a634bfcSVikram Hegde  *	called from rootnex_attach(). setup but don't startup the Intel IOMMU
10273a634bfcSVikram Hegde  *      This is the first function called in Intel IOMMU code
10283a634bfcSVikram Hegde  */
10293a634bfcSVikram Hegde void
immu_init(void)10303a634bfcSVikram Hegde immu_init(void)
10313a634bfcSVikram Hegde {
10323a634bfcSVikram Hegde 	char *phony_reg = "A thing of beauty is a joy forever";
10333a634bfcSVikram Hegde 
10343a634bfcSVikram Hegde 	/* Set some global shorthands that are needed by all of IOMMU code */
10353a634bfcSVikram Hegde 	root_devinfo = ddi_root_node();
10363a634bfcSVikram Hegde 
10373a634bfcSVikram Hegde 	/*
10383a634bfcSVikram Hegde 	 * Intel IOMMU only supported only if MMU(CPU) page size is ==
10393a634bfcSVikram Hegde 	 * IOMMU pages size.
10403a634bfcSVikram Hegde 	 */
10413a634bfcSVikram Hegde 	/*LINTED*/
10423a634bfcSVikram Hegde 	if (MMU_PAGESIZE != IMMU_PAGESIZE) {
10433a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL,
10443a634bfcSVikram Hegde 		    "MMU page size (%d) is not equal to\n"
10453a634bfcSVikram Hegde 		    "IOMMU page size (%d). "
10463a634bfcSVikram Hegde 		    "Disabling Intel IOMMU. ",
10473a634bfcSVikram Hegde 		    MMU_PAGESIZE, IMMU_PAGESIZE);
10483a634bfcSVikram Hegde 		immu_enable = B_FALSE;
10493a634bfcSVikram Hegde 		return;
10503a634bfcSVikram Hegde 	}
10513a634bfcSVikram Hegde 
10523adb2334SVikram Hegde 	/*
10533adb2334SVikram Hegde 	 * Read rootnex.conf options. Do this before
10543adb2334SVikram Hegde 	 * boot options so boot options can override .conf options.
10553adb2334SVikram Hegde 	 */
10563adb2334SVikram Hegde 	read_conf_options();
10573adb2334SVikram Hegde 
10583a634bfcSVikram Hegde 	/*
10593a634bfcSVikram Hegde 	 * retrieve the Intel IOMMU boot options.
10603a634bfcSVikram Hegde 	 * Do this before parsing immu ACPI table
10613a634bfcSVikram Hegde 	 * as a boot option could potentially affect
10623a634bfcSVikram Hegde 	 * ACPI parsing.
10633a634bfcSVikram Hegde 	 */
10643a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n");
10653a634bfcSVikram Hegde 	read_boot_options();
10663a634bfcSVikram Hegde 
10673a634bfcSVikram Hegde 	/*
10683a634bfcSVikram Hegde 	 * Check the IOMMU enable boot-option first.
10693a634bfcSVikram Hegde 	 * This is so that we can skip parsing the ACPI table
10703a634bfcSVikram Hegde 	 * if necessary because that may cause problems in
10713a634bfcSVikram Hegde 	 * systems with buggy BIOS or ACPI tables
10723a634bfcSVikram Hegde 	 */
10733a634bfcSVikram Hegde 	if (immu_enable == B_FALSE) {
10743a634bfcSVikram Hegde 		return;
10753a634bfcSVikram Hegde 	}
10763a634bfcSVikram Hegde 
1077c94adbf9SFrank Van Der Linden 	if (immu_intrmap_enable == B_TRUE)
1078c94adbf9SFrank Van Der Linden 		immu_qinv_enable = B_TRUE;
1079c94adbf9SFrank Van Der Linden 
10803a634bfcSVikram Hegde 	/*
10813a634bfcSVikram Hegde 	 * Next, check if the system even has an Intel IOMMU
10823a634bfcSVikram Hegde 	 * We use the presence or absence of the IOMMU ACPI
10833a634bfcSVikram Hegde 	 * table to detect Intel IOMMU.
10843a634bfcSVikram Hegde 	 */
10853a634bfcSVikram Hegde 	if (immu_dmar_setup() != DDI_SUCCESS) {
10863a634bfcSVikram Hegde 		immu_enable = B_FALSE;
10873a634bfcSVikram Hegde 		return;
10883a634bfcSVikram Hegde 	}
10893a634bfcSVikram Hegde 
10909e986f0eSFrank Van Der Linden 	mapping_list_setup();
10919e986f0eSFrank Van Der Linden 
10923a634bfcSVikram Hegde 	/*
10933a634bfcSVikram Hegde 	 * Check blacklists
10943a634bfcSVikram Hegde 	 */
10953a634bfcSVikram Hegde 	blacklist_setup();
10963a634bfcSVikram Hegde 
10973a634bfcSVikram Hegde 	if (blacklisted_smbios() == B_TRUE) {
10983a634bfcSVikram Hegde 		blacklist_destroy();
10993a634bfcSVikram Hegde 		immu_enable = B_FALSE;
11003a634bfcSVikram Hegde 		return;
11013a634bfcSVikram Hegde 	}
11023a634bfcSVikram Hegde 
11033a634bfcSVikram Hegde 	if (blacklisted_driver() == B_TRUE) {
11043a634bfcSVikram Hegde 		blacklist_destroy();
11053a634bfcSVikram Hegde 		immu_enable = B_FALSE;
11063a634bfcSVikram Hegde 		return;
11073a634bfcSVikram Hegde 	}
11083a634bfcSVikram Hegde 
11093a634bfcSVikram Hegde 	/*
11103a634bfcSVikram Hegde 	 * Read the "raw" DMAR ACPI table to get information
11113a634bfcSVikram Hegde 	 * and convert into a form we can use.
11123a634bfcSVikram Hegde 	 */
11133a634bfcSVikram Hegde 	if (immu_dmar_parse() != DDI_SUCCESS) {
11143a634bfcSVikram Hegde 		blacklist_destroy();
11153a634bfcSVikram Hegde 		immu_enable = B_FALSE;
11163a634bfcSVikram Hegde 		return;
11173a634bfcSVikram Hegde 	}
11183a634bfcSVikram Hegde 
11193a634bfcSVikram Hegde 	/*
11203a634bfcSVikram Hegde 	 * now that we have processed the ACPI table
11213a634bfcSVikram Hegde 	 * check if we need to blacklist this system
11223a634bfcSVikram Hegde 	 * based on ACPI info
11233a634bfcSVikram Hegde 	 */
11243a634bfcSVikram Hegde 	if (blacklisted_acpi() == B_TRUE) {
11253a634bfcSVikram Hegde 		immu_dmar_destroy();
11263a634bfcSVikram Hegde 		blacklist_destroy();
11273a634bfcSVikram Hegde 		immu_enable = B_FALSE;
11283a634bfcSVikram Hegde 		return;
11293a634bfcSVikram Hegde 	}
11303a634bfcSVikram Hegde 
11313a634bfcSVikram Hegde 	blacklist_destroy();
11323a634bfcSVikram Hegde 
11333a634bfcSVikram Hegde 	/*
11343a634bfcSVikram Hegde 	 * Check if system has HW quirks.
11353a634bfcSVikram Hegde 	 */
11363a634bfcSVikram Hegde 	pre_setup_quirks();
11373a634bfcSVikram Hegde 
11383a634bfcSVikram Hegde 	/* Now do the rest of the setup */
11393a634bfcSVikram Hegde 	immu_subsystems_setup();
11403a634bfcSVikram Hegde 
11413a634bfcSVikram Hegde 	/*
11423a634bfcSVikram Hegde 	 * Now that the IMMU is setup, create a phony
11433a634bfcSVikram Hegde 	 * reg prop so that suspend/resume works
11443a634bfcSVikram Hegde 	 */
11453a634bfcSVikram Hegde 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg",
11463a634bfcSVikram Hegde 	    (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) {
11473a634bfcSVikram Hegde 		ddi_err(DER_PANIC, NULL, "Failed to create reg prop for "
11483a634bfcSVikram Hegde 		    "rootnex node");
11493a634bfcSVikram Hegde 		/*NOTREACHED*/
11503a634bfcSVikram Hegde 	}
11513a634bfcSVikram Hegde 
11523a634bfcSVikram Hegde 	immu_setup = B_TRUE;
11533a634bfcSVikram Hegde }
11543a634bfcSVikram Hegde 
11553a634bfcSVikram Hegde /*
11563a634bfcSVikram Hegde  * immu_startup()
1157*d5ebc493SDan Cross  *	called directly by boot code to startup
1158*d5ebc493SDan Cross  *	all units of the IOMMU
11593a634bfcSVikram Hegde  */
11603a634bfcSVikram Hegde void
immu_startup(void)11613a634bfcSVikram Hegde immu_startup(void)
11623a634bfcSVikram Hegde {
11633a634bfcSVikram Hegde 	/*
11643a634bfcSVikram Hegde 	 * If IOMMU is disabled, do nothing
11653a634bfcSVikram Hegde 	 */
11663a634bfcSVikram Hegde 	if (immu_enable == B_FALSE) {
11673a634bfcSVikram Hegde 		return;
11683a634bfcSVikram Hegde 	}
11693a634bfcSVikram Hegde 
11703a634bfcSVikram Hegde 	if (immu_setup == B_FALSE) {
11713a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, "
117250200e77SFrank Van Der Linden 		    "skipping IOMMU startup");
11733a634bfcSVikram Hegde 		return;
11743a634bfcSVikram Hegde 	}
11753a634bfcSVikram Hegde 
11763a634bfcSVikram Hegde 	pre_startup_quirks();
11773a634bfcSVikram Hegde 
11783a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL,
11793a634bfcSVikram Hegde 	    "?Starting Intel IOMMU (dmar) units...\n");
11803a634bfcSVikram Hegde 
11813a634bfcSVikram Hegde 	immu_subsystems_startup();
11823a634bfcSVikram Hegde 
11833a634bfcSVikram Hegde 	immu_running = B_TRUE;
11843a634bfcSVikram Hegde }
11853a634bfcSVikram Hegde 
11863a634bfcSVikram Hegde /*
11873a634bfcSVikram Hegde  * Hook to notify IOMMU code of device tree changes
11883a634bfcSVikram Hegde  */
11893a634bfcSVikram Hegde void
immu_device_tree_changed(void)11903a634bfcSVikram Hegde immu_device_tree_changed(void)
11913a634bfcSVikram Hegde {
11923a634bfcSVikram Hegde 	if (immu_setup == B_FALSE) {
11933a634bfcSVikram Hegde 		return;
11943a634bfcSVikram Hegde 	}
11953a634bfcSVikram Hegde 
11963a634bfcSVikram Hegde 	ddi_err(DER_WARN, NULL, "Intel IOMMU currently "
11973a634bfcSVikram Hegde 	    "does not use device tree updates");
11983a634bfcSVikram Hegde }
11993a634bfcSVikram Hegde 
12003a634bfcSVikram Hegde /*
12013a634bfcSVikram Hegde  * Hook to notify IOMMU code of memory changes
12023a634bfcSVikram Hegde  */
12033a634bfcSVikram Hegde void
immu_physmem_update(uint64_t addr,uint64_t size)12043a634bfcSVikram Hegde immu_physmem_update(uint64_t addr, uint64_t size)
12053a634bfcSVikram Hegde {
12063a634bfcSVikram Hegde 	if (immu_setup == B_FALSE) {
12073a634bfcSVikram Hegde 		return;
12083a634bfcSVikram Hegde 	}
12093a634bfcSVikram Hegde 	immu_dvma_physmem_update(addr, size);
12103a634bfcSVikram Hegde }
12113a634bfcSVikram Hegde 
12123a634bfcSVikram Hegde /*
12133a634bfcSVikram Hegde  * immu_quiesce()
1214*d5ebc493SDan Cross  *	quiesce all units that are running
12153a634bfcSVikram Hegde  */
12163a634bfcSVikram Hegde int
immu_quiesce(void)12173a634bfcSVikram Hegde immu_quiesce(void)
12183a634bfcSVikram Hegde {
12193a634bfcSVikram Hegde 	immu_t *immu;
12203a634bfcSVikram Hegde 	int ret = DDI_SUCCESS;
12213a634bfcSVikram Hegde 
12223a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
12233a634bfcSVikram Hegde 
122496992ee7SEthindra Ramamurthy 	if (immu_running == B_FALSE) {
122596992ee7SEthindra Ramamurthy 		mutex_exit(&immu_lock);
12263a634bfcSVikram Hegde 		return (DDI_SUCCESS);
122796992ee7SEthindra Ramamurthy 	}
12283a634bfcSVikram Hegde 
12293a634bfcSVikram Hegde 	immu = list_head(&immu_list);
12303a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
12313a634bfcSVikram Hegde 
12323a634bfcSVikram Hegde 		/* if immu is not running, we dont quiesce */
12333a634bfcSVikram Hegde 		if (immu->immu_regs_running == B_FALSE)
12343a634bfcSVikram Hegde 			continue;
12353a634bfcSVikram Hegde 
12363a634bfcSVikram Hegde 		/* flush caches */
12373a634bfcSVikram Hegde 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
123850200e77SFrank Van Der Linden 		immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait);
123950200e77SFrank Van Der Linden 		immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait);
12403a634bfcSVikram Hegde 		rw_exit(&(immu->immu_ctx_rwlock));
12413a634bfcSVikram Hegde 		immu_regs_wbf_flush(immu);
12423a634bfcSVikram Hegde 
12433a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
12443a634bfcSVikram Hegde 
12453a634bfcSVikram Hegde 		/*
12463a634bfcSVikram Hegde 		 * Set IOMMU unit's regs to do
12473a634bfcSVikram Hegde 		 * the actual shutdown.
12483a634bfcSVikram Hegde 		 */
12493a634bfcSVikram Hegde 		immu_regs_shutdown(immu);
12503a634bfcSVikram Hegde 		immu_regs_suspend(immu);
12513a634bfcSVikram Hegde 
12523a634bfcSVikram Hegde 		/* if immu is still running, we failed */
12533a634bfcSVikram Hegde 		if (immu->immu_regs_running == B_TRUE)
12543a634bfcSVikram Hegde 			ret = DDI_FAILURE;
12553a634bfcSVikram Hegde 		else
12563a634bfcSVikram Hegde 			immu->immu_regs_quiesced = B_TRUE;
12573a634bfcSVikram Hegde 
12583a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
12593a634bfcSVikram Hegde 	}
12603a634bfcSVikram Hegde 
12613a634bfcSVikram Hegde 	if (ret == DDI_SUCCESS) {
12623a634bfcSVikram Hegde 		immu_running = B_FALSE;
12633a634bfcSVikram Hegde 		immu_quiesced = B_TRUE;
12643a634bfcSVikram Hegde 	}
126596992ee7SEthindra Ramamurthy 	mutex_exit(&immu_lock);
12663a634bfcSVikram Hegde 
12673a634bfcSVikram Hegde 	return (ret);
12683a634bfcSVikram Hegde }
12693a634bfcSVikram Hegde 
12703a634bfcSVikram Hegde /*
12713a634bfcSVikram Hegde  * immu_unquiesce()
1272*d5ebc493SDan Cross  *	unquiesce all units
12733a634bfcSVikram Hegde  */
12743a634bfcSVikram Hegde int
immu_unquiesce(void)12753a634bfcSVikram Hegde immu_unquiesce(void)
12763a634bfcSVikram Hegde {
12773a634bfcSVikram Hegde 	immu_t *immu;
12783a634bfcSVikram Hegde 	int ret = DDI_SUCCESS;
12793a634bfcSVikram Hegde 
12803a634bfcSVikram Hegde 	mutex_enter(&immu_lock);
12813a634bfcSVikram Hegde 
128296992ee7SEthindra Ramamurthy 	if (immu_quiesced == B_FALSE) {
128396992ee7SEthindra Ramamurthy 		mutex_exit(&immu_lock);
12843a634bfcSVikram Hegde 		return (DDI_SUCCESS);
128596992ee7SEthindra Ramamurthy 	}
12863a634bfcSVikram Hegde 
12873a634bfcSVikram Hegde 	immu = list_head(&immu_list);
12883a634bfcSVikram Hegde 	for (; immu; immu = list_next(&immu_list, immu)) {
12893a634bfcSVikram Hegde 
12903a634bfcSVikram Hegde 		mutex_enter(&(immu->immu_lock));
12913a634bfcSVikram Hegde 
12923a634bfcSVikram Hegde 		/* if immu was not quiesced, i.e was not running before */
1293e03dceedSVikram Hegde 		if (immu->immu_regs_quiesced == B_FALSE) {
1294e03dceedSVikram Hegde 			mutex_exit(&(immu->immu_lock));
12953a634bfcSVikram Hegde 			continue;
1296e03dceedSVikram Hegde 		}
12973a634bfcSVikram Hegde 
12983a634bfcSVikram Hegde 		if (immu_regs_resume(immu) != DDI_SUCCESS) {
12993a634bfcSVikram Hegde 			ret = DDI_FAILURE;
1300e03dceedSVikram Hegde 			mutex_exit(&(immu->immu_lock));
13013a634bfcSVikram Hegde 			continue;
13023a634bfcSVikram Hegde 		}
13033a634bfcSVikram Hegde 
13043a634bfcSVikram Hegde 		/* flush caches before unquiesce */
13053a634bfcSVikram Hegde 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
130650200e77SFrank Van Der Linden 		immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait);
130750200e77SFrank Van Der Linden 		immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait);
13083a634bfcSVikram Hegde 		rw_exit(&(immu->immu_ctx_rwlock));
13093a634bfcSVikram Hegde 
13103a634bfcSVikram Hegde 		/*
13113a634bfcSVikram Hegde 		 * Set IOMMU unit's regs to do
13123a634bfcSVikram Hegde 		 * the actual startup. This will
13133a634bfcSVikram Hegde 		 * set immu->immu_regs_running  field
13143a634bfcSVikram Hegde 		 * if the unit is successfully
13153a634bfcSVikram Hegde 		 * started
13163a634bfcSVikram Hegde 		 */
13173a634bfcSVikram Hegde 		immu_regs_startup(immu);
13183a634bfcSVikram Hegde 
13193a634bfcSVikram Hegde 		if (immu->immu_regs_running == B_FALSE) {
13203a634bfcSVikram Hegde 			ret = DDI_FAILURE;
13213a634bfcSVikram Hegde 		} else {
13223a634bfcSVikram Hegde 			immu_quiesced = B_TRUE;
13233a634bfcSVikram Hegde 			immu_running = B_TRUE;
13243a634bfcSVikram Hegde 			immu->immu_regs_quiesced = B_FALSE;
13253a634bfcSVikram Hegde 		}
13263a634bfcSVikram Hegde 
13273a634bfcSVikram Hegde 		mutex_exit(&(immu->immu_lock));
13283a634bfcSVikram Hegde 	}
13293a634bfcSVikram Hegde 
13303a634bfcSVikram Hegde 	mutex_exit(&immu_lock);
13313a634bfcSVikram Hegde 
13323a634bfcSVikram Hegde 	return (ret);
13333a634bfcSVikram Hegde }
13343a634bfcSVikram Hegde 
133550200e77SFrank Van Der Linden void
immu_init_inv_wait(immu_inv_wait_t * iwp,const char * name,boolean_t sync)133650200e77SFrank Van Der Linden immu_init_inv_wait(immu_inv_wait_t *iwp, const char *name, boolean_t sync)
133750200e77SFrank Van Der Linden {
133850200e77SFrank Van Der Linden 	caddr_t vaddr;
133950200e77SFrank Van Der Linden 	uint64_t paddr;
134050200e77SFrank Van Der Linden 
134150200e77SFrank Van Der Linden 	iwp->iwp_sync = sync;
134250200e77SFrank Van Der Linden 
134350200e77SFrank Van Der Linden 	vaddr = (caddr_t)&iwp->iwp_vstatus;
134450200e77SFrank Van Der Linden 	paddr = pfn_to_pa(hat_getpfnum(kas.a_hat, vaddr));
134550200e77SFrank Van Der Linden 	paddr += ((uintptr_t)vaddr) & MMU_PAGEOFFSET;
134650200e77SFrank Van Der Linden 
134750200e77SFrank Van Der Linden 	iwp->iwp_pstatus = paddr;
134850200e77SFrank Van Der Linden 	iwp->iwp_name = name;
134950200e77SFrank Van Der Linden }
135050200e77SFrank Van Der Linden 
13513a634bfcSVikram Hegde /* ##############  END Intel IOMMU entry points ################## */
1352