xref: /onnv-gate/usr/src/uts/i86pc/io/immu.c (revision 13070:87f89233b883)
111600SVikram.Hegde@Sun.COM /*
211600SVikram.Hegde@Sun.COM  * CDDL HEADER START
311600SVikram.Hegde@Sun.COM  *
411600SVikram.Hegde@Sun.COM  * The contents of this file are subject to the terms of the
511600SVikram.Hegde@Sun.COM  * Common Development and Distribution License (the "License").
611600SVikram.Hegde@Sun.COM  * You may not use this file except in compliance with the License.
711600SVikram.Hegde@Sun.COM  *
811600SVikram.Hegde@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911600SVikram.Hegde@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011600SVikram.Hegde@Sun.COM  * See the License for the specific language governing permissions
1111600SVikram.Hegde@Sun.COM  * and limitations under the License.
1211600SVikram.Hegde@Sun.COM  *
1311600SVikram.Hegde@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411600SVikram.Hegde@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511600SVikram.Hegde@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611600SVikram.Hegde@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711600SVikram.Hegde@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811600SVikram.Hegde@Sun.COM  *
1911600SVikram.Hegde@Sun.COM  * CDDL HEADER END
2011600SVikram.Hegde@Sun.COM  */
2111600SVikram.Hegde@Sun.COM /*
2212465Sfrank.van.der.linden@oracle.com  * Portions Copyright (c) 2010, Oracle and/or its affiliates.
2312465Sfrank.van.der.linden@oracle.com  * All rights reserved.
2411600SVikram.Hegde@Sun.COM  */
2511600SVikram.Hegde@Sun.COM /*
2611600SVikram.Hegde@Sun.COM  * Copyright (c) 2009, Intel Corporation.
2711600SVikram.Hegde@Sun.COM  * All rights reserved.
2811600SVikram.Hegde@Sun.COM  */
2911600SVikram.Hegde@Sun.COM 
3011600SVikram.Hegde@Sun.COM /*
3111600SVikram.Hegde@Sun.COM  * Intel IOMMU implementation
3211600SVikram.Hegde@Sun.COM  * This file contains Intel IOMMU code exported
3311600SVikram.Hegde@Sun.COM  * to the rest of the system and code that deals
3411600SVikram.Hegde@Sun.COM  * with the Intel IOMMU as a whole.
3511600SVikram.Hegde@Sun.COM  */
3611600SVikram.Hegde@Sun.COM 
3711600SVikram.Hegde@Sun.COM #include <sys/conf.h>
3811600SVikram.Hegde@Sun.COM #include <sys/modctl.h>
3911600SVikram.Hegde@Sun.COM #include <sys/pci.h>
4011600SVikram.Hegde@Sun.COM #include <sys/pci_impl.h>
4111600SVikram.Hegde@Sun.COM #include <sys/sysmacros.h>
4211600SVikram.Hegde@Sun.COM #include <sys/ddi.h>
4311600SVikram.Hegde@Sun.COM #include <sys/ddidmareq.h>
4411600SVikram.Hegde@Sun.COM #include <sys/ddi_impldefs.h>
4511600SVikram.Hegde@Sun.COM #include <sys/ddifm.h>
4611600SVikram.Hegde@Sun.COM #include <sys/sunndi.h>
4711600SVikram.Hegde@Sun.COM #include <sys/debug.h>
4811600SVikram.Hegde@Sun.COM #include <sys/fm/protocol.h>
4911600SVikram.Hegde@Sun.COM #include <sys/note.h>
5011600SVikram.Hegde@Sun.COM #include <sys/apic.h>
5111600SVikram.Hegde@Sun.COM #include <vm/hat_i86.h>
5211600SVikram.Hegde@Sun.COM #include <sys/smp_impldefs.h>
5311600SVikram.Hegde@Sun.COM #include <sys/spl.h>
5411600SVikram.Hegde@Sun.COM #include <sys/archsystm.h>
5511600SVikram.Hegde@Sun.COM #include <sys/x86_archext.h>
5611600SVikram.Hegde@Sun.COM #include <sys/avl.h>
5711600SVikram.Hegde@Sun.COM #include <sys/bootconf.h>
5811600SVikram.Hegde@Sun.COM #include <sys/bootinfo.h>
5911600SVikram.Hegde@Sun.COM #include <sys/atomic.h>
6011600SVikram.Hegde@Sun.COM #include <sys/immu.h>
6111600SVikram.Hegde@Sun.COM /* ########################### Globals and tunables ######################## */
6211600SVikram.Hegde@Sun.COM /*
6311600SVikram.Hegde@Sun.COM  * Global switches (boolean) that can be toggled either via boot options
6411600SVikram.Hegde@Sun.COM  * or via /etc/system or kmdb
6511600SVikram.Hegde@Sun.COM  */
6611600SVikram.Hegde@Sun.COM 
6711600SVikram.Hegde@Sun.COM /* Various features */
6811749SVikram.Hegde@Sun.COM boolean_t immu_enable = B_TRUE;
6911600SVikram.Hegde@Sun.COM boolean_t immu_dvma_enable = B_TRUE;
7011600SVikram.Hegde@Sun.COM 
7111600SVikram.Hegde@Sun.COM /* accessed in other files so not static */
7211600SVikram.Hegde@Sun.COM boolean_t immu_gfxdvma_enable = B_TRUE;
7311600SVikram.Hegde@Sun.COM boolean_t immu_intrmap_enable = B_FALSE;
7413050Sfrank.van.der.linden@oracle.com boolean_t immu_qinv_enable = B_TRUE;
7511600SVikram.Hegde@Sun.COM 
7611600SVikram.Hegde@Sun.COM /* various quirks that need working around */
7711600SVikram.Hegde@Sun.COM 
7811600SVikram.Hegde@Sun.COM /* XXX We always map page 0 read/write for now */
7911600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbpage0 = B_TRUE;
8011600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbrmrr = B_TRUE;
8111600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbfullpa;
8211600SVikram.Hegde@Sun.COM boolean_t immu_quirk_mobile4;
8311600SVikram.Hegde@Sun.COM 
8411600SVikram.Hegde@Sun.COM /* debug messages */
8511600SVikram.Hegde@Sun.COM boolean_t immu_dmar_print;
8611600SVikram.Hegde@Sun.COM 
8711658SVikram.Hegde@Sun.COM /* Tunables */
8811658SVikram.Hegde@Sun.COM int64_t immu_flush_gran = 5;
8911658SVikram.Hegde@Sun.COM 
9012465Sfrank.van.der.linden@oracle.com immu_flags_t immu_global_dvma_flags;
9112465Sfrank.van.der.linden@oracle.com 
9211600SVikram.Hegde@Sun.COM /* ############  END OPTIONS section ################ */
9311600SVikram.Hegde@Sun.COM 
9411600SVikram.Hegde@Sun.COM /*
9511600SVikram.Hegde@Sun.COM  * Global used internally by Intel IOMMU code
9611600SVikram.Hegde@Sun.COM  */
9711600SVikram.Hegde@Sun.COM dev_info_t *root_devinfo;
9811600SVikram.Hegde@Sun.COM kmutex_t immu_lock;
9911600SVikram.Hegde@Sun.COM list_t immu_list;
10011600SVikram.Hegde@Sun.COM boolean_t immu_setup;
10111600SVikram.Hegde@Sun.COM boolean_t immu_running;
10211600SVikram.Hegde@Sun.COM boolean_t immu_quiesced;
10311600SVikram.Hegde@Sun.COM 
10411600SVikram.Hegde@Sun.COM /* ######################## END Globals and tunables ###################### */
10511600SVikram.Hegde@Sun.COM /* Globals used only in this file */
10611600SVikram.Hegde@Sun.COM static char **black_array;
10711600SVikram.Hegde@Sun.COM static uint_t nblacks;
10812465Sfrank.van.der.linden@oracle.com 
10912465Sfrank.van.der.linden@oracle.com static char **unity_driver_array;
11012465Sfrank.van.der.linden@oracle.com static uint_t nunity;
11112465Sfrank.van.der.linden@oracle.com static char **xlate_driver_array;
11212465Sfrank.van.der.linden@oracle.com static uint_t nxlate;
11313050Sfrank.van.der.linden@oracle.com 
11413050Sfrank.van.der.linden@oracle.com static char **premap_driver_array;
11513050Sfrank.van.der.linden@oracle.com static uint_t npremap;
11613050Sfrank.van.der.linden@oracle.com static char **nopremap_driver_array;
11713050Sfrank.van.der.linden@oracle.com static uint_t nnopremap;
11811600SVikram.Hegde@Sun.COM /* ###################### Utility routines ############################# */
11911600SVikram.Hegde@Sun.COM 
12011600SVikram.Hegde@Sun.COM /*
12111600SVikram.Hegde@Sun.COM  * Check if the device has mobile 4 chipset
12211600SVikram.Hegde@Sun.COM  */
12311600SVikram.Hegde@Sun.COM static int
check_mobile4(dev_info_t * dip,void * arg)12411600SVikram.Hegde@Sun.COM check_mobile4(dev_info_t *dip, void *arg)
12511600SVikram.Hegde@Sun.COM {
12611600SVikram.Hegde@Sun.COM 	_NOTE(ARGUNUSED(arg));
12711600SVikram.Hegde@Sun.COM 	int vendor, device;
12811600SVikram.Hegde@Sun.COM 	int *ip = (int *)arg;
12911600SVikram.Hegde@Sun.COM 
13011600SVikram.Hegde@Sun.COM 	vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
13111600SVikram.Hegde@Sun.COM 	    "vendor-id", -1);
13211600SVikram.Hegde@Sun.COM 	device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
13311600SVikram.Hegde@Sun.COM 	    "device-id", -1);
13411600SVikram.Hegde@Sun.COM 
13511600SVikram.Hegde@Sun.COM 	if (vendor == 0x8086 && device == 0x2a40) {
13611600SVikram.Hegde@Sun.COM 		*ip = B_TRUE;
13713050Sfrank.van.der.linden@oracle.com 		ddi_err(DER_NOTE, dip, "iommu: Mobile 4 chipset detected. "
13811600SVikram.Hegde@Sun.COM 		    "Force setting IOMMU write buffer");
13911600SVikram.Hegde@Sun.COM 		return (DDI_WALK_TERMINATE);
14011600SVikram.Hegde@Sun.COM 	} else {
14111600SVikram.Hegde@Sun.COM 		return (DDI_WALK_CONTINUE);
14211600SVikram.Hegde@Sun.COM 	}
14311600SVikram.Hegde@Sun.COM }
14411600SVikram.Hegde@Sun.COM 
14511600SVikram.Hegde@Sun.COM static void
map_bios_rsvd_mem(dev_info_t * dip)14611600SVikram.Hegde@Sun.COM map_bios_rsvd_mem(dev_info_t *dip)
14711600SVikram.Hegde@Sun.COM {
14811600SVikram.Hegde@Sun.COM 	struct memlist *mp;
14913050Sfrank.van.der.linden@oracle.com 
15013050Sfrank.van.der.linden@oracle.com 	/*
15113050Sfrank.van.der.linden@oracle.com 	 * Make sure the domain for the device is set up before
15213050Sfrank.van.der.linden@oracle.com 	 * mapping anything.
15313050Sfrank.van.der.linden@oracle.com 	 */
15413050Sfrank.van.der.linden@oracle.com 	(void) immu_dvma_device_setup(dip, 0);
15511600SVikram.Hegde@Sun.COM 
15611600SVikram.Hegde@Sun.COM 	memlist_read_lock();
15711600SVikram.Hegde@Sun.COM 
15811600SVikram.Hegde@Sun.COM 	mp = bios_rsvd;
15911600SVikram.Hegde@Sun.COM 	while (mp != NULL) {
16011658SVikram.Hegde@Sun.COM 		memrng_t mrng = {0};
16111600SVikram.Hegde@Sun.COM 
16213050Sfrank.van.der.linden@oracle.com 		ddi_err(DER_LOG, dip, "iommu: Mapping BIOS rsvd range "
16311600SVikram.Hegde@Sun.COM 		    "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address,
16411600SVikram.Hegde@Sun.COM 		    mp->ml_address + mp->ml_size);
16511600SVikram.Hegde@Sun.COM 
16611658SVikram.Hegde@Sun.COM 		mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address);
16711658SVikram.Hegde@Sun.COM 		mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE;
16811600SVikram.Hegde@Sun.COM 
16913050Sfrank.van.der.linden@oracle.com 		(void) immu_map_memrange(dip, &mrng);
17011600SVikram.Hegde@Sun.COM 
17111600SVikram.Hegde@Sun.COM 		mp = mp->ml_next;
17211600SVikram.Hegde@Sun.COM 	}
17311600SVikram.Hegde@Sun.COM 
17411600SVikram.Hegde@Sun.COM 	memlist_read_unlock();
17511600SVikram.Hegde@Sun.COM }
17611600SVikram.Hegde@Sun.COM 
17711658SVikram.Hegde@Sun.COM 
17811658SVikram.Hegde@Sun.COM /*
17912465Sfrank.van.der.linden@oracle.com  * Check if the driver requests a specific type of mapping.
18011658SVikram.Hegde@Sun.COM  */
18111658SVikram.Hegde@Sun.COM /*ARGSUSED*/
18211658SVikram.Hegde@Sun.COM static void
check_conf(dev_info_t * dip,void * arg)18312465Sfrank.van.der.linden@oracle.com check_conf(dev_info_t *dip, void *arg)
18411658SVikram.Hegde@Sun.COM {
18512465Sfrank.van.der.linden@oracle.com 	immu_devi_t *immu_devi;
18612465Sfrank.van.der.linden@oracle.com 	const char *dname;
18712465Sfrank.van.der.linden@oracle.com 	uint_t i;
18813050Sfrank.van.der.linden@oracle.com 	int hasmapprop = 0, haspreprop = 0;
18913050Sfrank.van.der.linden@oracle.com 	boolean_t old_premap;
19011658SVikram.Hegde@Sun.COM 
19111658SVikram.Hegde@Sun.COM 	/*
19212465Sfrank.van.der.linden@oracle.com 	 * Only PCI devices can use an IOMMU. Legacy ISA devices
19312465Sfrank.van.der.linden@oracle.com 	 * are handled in check_lpc.
19411658SVikram.Hegde@Sun.COM 	 */
19512465Sfrank.van.der.linden@oracle.com 	if (!DEVI_IS_PCI(dip))
19612465Sfrank.van.der.linden@oracle.com 		return;
19712465Sfrank.van.der.linden@oracle.com 
19812465Sfrank.van.der.linden@oracle.com 	dname = ddi_driver_name(dip);
19912465Sfrank.van.der.linden@oracle.com 	if (dname == NULL)
20012465Sfrank.van.der.linden@oracle.com 		return;
20112465Sfrank.van.der.linden@oracle.com 	immu_devi = immu_devi_get(dip);
20212465Sfrank.van.der.linden@oracle.com 
20312465Sfrank.van.der.linden@oracle.com 	for (i = 0; i < nunity; i++) {
20412465Sfrank.van.der.linden@oracle.com 		if (strcmp(unity_driver_array[i], dname) == 0) {
20513050Sfrank.van.der.linden@oracle.com 			hasmapprop = 1;
20612465Sfrank.van.der.linden@oracle.com 			immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY;
20712465Sfrank.van.der.linden@oracle.com 		}
20812465Sfrank.van.der.linden@oracle.com 	}
20911658SVikram.Hegde@Sun.COM 
21012465Sfrank.van.der.linden@oracle.com 	for (i = 0; i < nxlate; i++) {
21112465Sfrank.van.der.linden@oracle.com 		if (strcmp(xlate_driver_array[i], dname) == 0) {
21213050Sfrank.van.der.linden@oracle.com 			hasmapprop = 1;
21312465Sfrank.van.der.linden@oracle.com 			immu_devi->imd_dvma_flags &= ~IMMU_FLAGS_UNITY;
21411658SVikram.Hegde@Sun.COM 		}
21511658SVikram.Hegde@Sun.COM 	}
21612465Sfrank.van.der.linden@oracle.com 
21713050Sfrank.van.der.linden@oracle.com 	old_premap = immu_devi->imd_use_premap;
21813050Sfrank.van.der.linden@oracle.com 
21913050Sfrank.van.der.linden@oracle.com 	for (i = 0; i < nnopremap; i++) {
22013050Sfrank.van.der.linden@oracle.com 		if (strcmp(nopremap_driver_array[i], dname) == 0) {
22113050Sfrank.van.der.linden@oracle.com 			haspreprop = 1;
22213050Sfrank.van.der.linden@oracle.com 			immu_devi->imd_use_premap = B_FALSE;
22313050Sfrank.van.der.linden@oracle.com 		}
22413050Sfrank.van.der.linden@oracle.com 	}
22513050Sfrank.van.der.linden@oracle.com 
22613050Sfrank.van.der.linden@oracle.com 	for (i = 0; i < npremap; i++) {
22713050Sfrank.van.der.linden@oracle.com 		if (strcmp(premap_driver_array[i], dname) == 0) {
22813050Sfrank.van.der.linden@oracle.com 			haspreprop = 1;
22913050Sfrank.van.der.linden@oracle.com 			immu_devi->imd_use_premap = B_TRUE;
23013050Sfrank.van.der.linden@oracle.com 		}
23113050Sfrank.van.der.linden@oracle.com 	}
23213050Sfrank.van.der.linden@oracle.com 
23312465Sfrank.van.der.linden@oracle.com 	/*
23412465Sfrank.van.der.linden@oracle.com 	 * Report if we changed the value from the default.
23512465Sfrank.van.der.linden@oracle.com 	 */
23613050Sfrank.van.der.linden@oracle.com 	if (hasmapprop && (immu_devi->imd_dvma_flags ^ immu_global_dvma_flags))
23712465Sfrank.van.der.linden@oracle.com 		ddi_err(DER_LOG, dip, "using %s DVMA mapping",
23812465Sfrank.van.der.linden@oracle.com 		    immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY ?
23912465Sfrank.van.der.linden@oracle.com 		    DDI_DVMA_MAPTYPE_UNITY : DDI_DVMA_MAPTYPE_XLATE);
24013050Sfrank.van.der.linden@oracle.com 
24113050Sfrank.van.der.linden@oracle.com 	if (haspreprop && (immu_devi->imd_use_premap != old_premap))
24213050Sfrank.van.der.linden@oracle.com 		ddi_err(DER_LOG, dip, "%susing premapped DVMA space",
24313050Sfrank.van.der.linden@oracle.com 		    immu_devi->imd_use_premap ? "" : "not ");
24411658SVikram.Hegde@Sun.COM }
24511658SVikram.Hegde@Sun.COM 
24611600SVikram.Hegde@Sun.COM /*
24711600SVikram.Hegde@Sun.COM  * Check if the device is USB controller
24811600SVikram.Hegde@Sun.COM  */
24911600SVikram.Hegde@Sun.COM /*ARGSUSED*/
25011600SVikram.Hegde@Sun.COM static void
check_usb(dev_info_t * dip,void * arg)25111600SVikram.Hegde@Sun.COM check_usb(dev_info_t *dip, void *arg)
25211600SVikram.Hegde@Sun.COM {
25311600SVikram.Hegde@Sun.COM 	const char *drv = ddi_driver_name(dip);
25412465Sfrank.van.der.linden@oracle.com 	immu_devi_t *immu_devi;
25512465Sfrank.van.der.linden@oracle.com 
25611600SVikram.Hegde@Sun.COM 
25711600SVikram.Hegde@Sun.COM 	if (drv == NULL ||
25811600SVikram.Hegde@Sun.COM 	    (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 &&
25911600SVikram.Hegde@Sun.COM 	    strcmp(drv, "ehci") != 0)) {
26011600SVikram.Hegde@Sun.COM 		return;
26111600SVikram.Hegde@Sun.COM 	}
26211600SVikram.Hegde@Sun.COM 
26312465Sfrank.van.der.linden@oracle.com 	immu_devi = immu_devi_get(dip);
26412465Sfrank.van.der.linden@oracle.com 
26512465Sfrank.van.der.linden@oracle.com 	/*
26612465Sfrank.van.der.linden@oracle.com 	 * If unit mappings are already specified, globally or
26712465Sfrank.van.der.linden@oracle.com 	 * locally, we're done here, since that covers both
26812465Sfrank.van.der.linden@oracle.com 	 * quirks below.
26912465Sfrank.van.der.linden@oracle.com 	 */
27012465Sfrank.van.der.linden@oracle.com 	if (immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY)
27112465Sfrank.van.der.linden@oracle.com 		return;
27212465Sfrank.van.der.linden@oracle.com 
27311600SVikram.Hegde@Sun.COM 	/* This must come first since it does unity mapping */
27411600SVikram.Hegde@Sun.COM 	if (immu_quirk_usbfullpa == B_TRUE) {
27512465Sfrank.van.der.linden@oracle.com 		immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY;
27612465Sfrank.van.der.linden@oracle.com 	} else if (immu_quirk_usbrmrr == B_TRUE) {
27711600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "Applying USB RMRR quirk");
27811600SVikram.Hegde@Sun.COM 		map_bios_rsvd_mem(dip);
27911600SVikram.Hegde@Sun.COM 	}
28011600SVikram.Hegde@Sun.COM }
28111600SVikram.Hegde@Sun.COM 
28211600SVikram.Hegde@Sun.COM /*
28311600SVikram.Hegde@Sun.COM  * Check if the device is a LPC device
28411600SVikram.Hegde@Sun.COM  */
28511600SVikram.Hegde@Sun.COM /*ARGSUSED*/
28611600SVikram.Hegde@Sun.COM static void
check_lpc(dev_info_t * dip,void * arg)28711600SVikram.Hegde@Sun.COM check_lpc(dev_info_t *dip, void *arg)
28811600SVikram.Hegde@Sun.COM {
28911600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
29011600SVikram.Hegde@Sun.COM 
29111600SVikram.Hegde@Sun.COM 	immu_devi = immu_devi_get(dip);
29211600SVikram.Hegde@Sun.COM 	if (immu_devi->imd_lpc == B_TRUE) {
29313050Sfrank.van.der.linden@oracle.com 		ddi_err(DER_LOG, dip, "iommu: Found LPC device");
29411600SVikram.Hegde@Sun.COM 		/* This will put the immu_devi on the LPC "specials" list */
29513050Sfrank.van.der.linden@oracle.com 		(void) immu_dvma_device_setup(dip, IMMU_FLAGS_SLEEP);
29611600SVikram.Hegde@Sun.COM 	}
29711600SVikram.Hegde@Sun.COM }
29811600SVikram.Hegde@Sun.COM 
29911600SVikram.Hegde@Sun.COM /*
30011600SVikram.Hegde@Sun.COM  * Check if the device is a GFX device
30111600SVikram.Hegde@Sun.COM  */
30211600SVikram.Hegde@Sun.COM /*ARGSUSED*/
30311600SVikram.Hegde@Sun.COM static void
check_gfx(dev_info_t * dip,void * arg)30411600SVikram.Hegde@Sun.COM check_gfx(dev_info_t *dip, void *arg)
30511600SVikram.Hegde@Sun.COM {
30611600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
30711600SVikram.Hegde@Sun.COM 
30811600SVikram.Hegde@Sun.COM 	immu_devi = immu_devi_get(dip);
30911600SVikram.Hegde@Sun.COM 	if (immu_devi->imd_display == B_TRUE) {
31012465Sfrank.van.der.linden@oracle.com 		immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY;
31113050Sfrank.van.der.linden@oracle.com 		ddi_err(DER_LOG, dip, "iommu: Found GFX device");
31211600SVikram.Hegde@Sun.COM 		/* This will put the immu_devi on the GFX "specials" list */
31311600SVikram.Hegde@Sun.COM 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
31411600SVikram.Hegde@Sun.COM 	}
31511600SVikram.Hegde@Sun.COM }
31611600SVikram.Hegde@Sun.COM 
31711600SVikram.Hegde@Sun.COM static void
walk_tree(int (* f)(dev_info_t *,void *),void * arg)31811600SVikram.Hegde@Sun.COM walk_tree(int (*f)(dev_info_t *, void *), void *arg)
31911600SVikram.Hegde@Sun.COM {
32011600SVikram.Hegde@Sun.COM 	int count;
32111600SVikram.Hegde@Sun.COM 
32211600SVikram.Hegde@Sun.COM 	ndi_devi_enter(root_devinfo, &count);
32311600SVikram.Hegde@Sun.COM 	ddi_walk_devs(ddi_get_child(root_devinfo), f, arg);
32411600SVikram.Hegde@Sun.COM 	ndi_devi_exit(root_devinfo, count);
32511600SVikram.Hegde@Sun.COM }
32611600SVikram.Hegde@Sun.COM 
32711600SVikram.Hegde@Sun.COM static int
check_pre_setup_quirks(dev_info_t * dip,void * arg)32811600SVikram.Hegde@Sun.COM check_pre_setup_quirks(dev_info_t *dip, void *arg)
32911600SVikram.Hegde@Sun.COM {
33011600SVikram.Hegde@Sun.COM 	/* just 1 check right now */
33111600SVikram.Hegde@Sun.COM 	return (check_mobile4(dip, arg));
33211600SVikram.Hegde@Sun.COM }
33311600SVikram.Hegde@Sun.COM 
33411600SVikram.Hegde@Sun.COM static int
check_pre_startup_quirks(dev_info_t * dip,void * arg)33511600SVikram.Hegde@Sun.COM check_pre_startup_quirks(dev_info_t *dip, void *arg)
33611600SVikram.Hegde@Sun.COM {
33711600SVikram.Hegde@Sun.COM 	if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) {
33811600SVikram.Hegde@Sun.COM 		ddi_err(DER_PANIC, dip, "Failed to get immu_devi");
33911600SVikram.Hegde@Sun.COM 	}
34011600SVikram.Hegde@Sun.COM 
34111600SVikram.Hegde@Sun.COM 	check_gfx(dip, arg);
34211600SVikram.Hegde@Sun.COM 
34311600SVikram.Hegde@Sun.COM 	check_lpc(dip, arg);
34411600SVikram.Hegde@Sun.COM 
34512465Sfrank.van.der.linden@oracle.com 	check_conf(dip, arg);
34611600SVikram.Hegde@Sun.COM 
34712465Sfrank.van.der.linden@oracle.com 	check_usb(dip, arg);
34811658SVikram.Hegde@Sun.COM 
34911600SVikram.Hegde@Sun.COM 	return (DDI_WALK_CONTINUE);
35011600SVikram.Hegde@Sun.COM }
35111600SVikram.Hegde@Sun.COM 
35211600SVikram.Hegde@Sun.COM static void
pre_setup_quirks(void)35311600SVikram.Hegde@Sun.COM pre_setup_quirks(void)
35411600SVikram.Hegde@Sun.COM {
35511600SVikram.Hegde@Sun.COM 	walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4);
35611600SVikram.Hegde@Sun.COM }
35711600SVikram.Hegde@Sun.COM 
35811600SVikram.Hegde@Sun.COM static void
pre_startup_quirks(void)35911600SVikram.Hegde@Sun.COM pre_startup_quirks(void)
36011600SVikram.Hegde@Sun.COM {
36111600SVikram.Hegde@Sun.COM 	walk_tree(check_pre_startup_quirks, NULL);
36211600SVikram.Hegde@Sun.COM 
36311600SVikram.Hegde@Sun.COM 	immu_dmar_rmrr_map();
36411600SVikram.Hegde@Sun.COM }
36511600SVikram.Hegde@Sun.COM 
36612465Sfrank.van.der.linden@oracle.com static int
get_conf_str(char * bopt,char ** val)36712465Sfrank.van.der.linden@oracle.com get_conf_str(char *bopt, char **val)
36812465Sfrank.van.der.linden@oracle.com {
36912465Sfrank.van.der.linden@oracle.com 	int ret;
37012465Sfrank.van.der.linden@oracle.com 
37112465Sfrank.van.der.linden@oracle.com 	/*
37212465Sfrank.van.der.linden@oracle.com 	 * Check the rootnex.conf property
37312465Sfrank.van.der.linden@oracle.com 	 * Fake up a dev_t since searching the global
37412465Sfrank.van.der.linden@oracle.com 	 * property list needs it
37512465Sfrank.van.der.linden@oracle.com 	 */
37612465Sfrank.van.der.linden@oracle.com 	ret = ddi_prop_lookup_string(
37712465Sfrank.van.der.linden@oracle.com 	    makedevice(ddi_name_to_major("rootnex"), 0),
37812465Sfrank.van.der.linden@oracle.com 	    root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
37912465Sfrank.van.der.linden@oracle.com 	    bopt, val);
38012465Sfrank.van.der.linden@oracle.com 
38112465Sfrank.van.der.linden@oracle.com 	return (ret);
38212465Sfrank.van.der.linden@oracle.com }
38312465Sfrank.van.der.linden@oracle.com 
38411600SVikram.Hegde@Sun.COM /*
38511749SVikram.Hegde@Sun.COM  * get_conf_opt()
38611749SVikram.Hegde@Sun.COM  * 	get a rootnex.conf setting  (always a boolean)
38711749SVikram.Hegde@Sun.COM  */
38811749SVikram.Hegde@Sun.COM static void
get_conf_opt(char * bopt,boolean_t * kvar)38911749SVikram.Hegde@Sun.COM get_conf_opt(char *bopt, boolean_t *kvar)
39011749SVikram.Hegde@Sun.COM {
39111749SVikram.Hegde@Sun.COM 	char *val = NULL;
39211749SVikram.Hegde@Sun.COM 
39311749SVikram.Hegde@Sun.COM 	/*
39411749SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf property
39511749SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
39611749SVikram.Hegde@Sun.COM 	 * property list needs it
39711749SVikram.Hegde@Sun.COM 	 */
39812465Sfrank.van.der.linden@oracle.com 
39912465Sfrank.van.der.linden@oracle.com 	if (get_conf_str(bopt, &val) != DDI_PROP_SUCCESS)
40011749SVikram.Hegde@Sun.COM 		return;
40111749SVikram.Hegde@Sun.COM 
40211749SVikram.Hegde@Sun.COM 	if (strcmp(val, "true") == 0) {
40311749SVikram.Hegde@Sun.COM 		*kvar = B_TRUE;
40411749SVikram.Hegde@Sun.COM 	} else if (strcmp(val, "false") == 0) {
40511749SVikram.Hegde@Sun.COM 		*kvar = B_FALSE;
40611749SVikram.Hegde@Sun.COM 	} else {
40711749SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ",
40811749SVikram.Hegde@Sun.COM 		    "is not set to true or false. Ignoring option.",
40911749SVikram.Hegde@Sun.COM 		    bopt, val);
41011749SVikram.Hegde@Sun.COM 	}
41111749SVikram.Hegde@Sun.COM 	ddi_prop_free(val);
41211749SVikram.Hegde@Sun.COM }
41311749SVikram.Hegde@Sun.COM 
41411749SVikram.Hegde@Sun.COM /*
41511600SVikram.Hegde@Sun.COM  * get_bootopt()
41611600SVikram.Hegde@Sun.COM  * 	check a boot option  (always a boolean)
41711600SVikram.Hegde@Sun.COM  */
41812465Sfrank.van.der.linden@oracle.com static int
get_boot_str(char * bopt,char ** val)41912465Sfrank.van.der.linden@oracle.com get_boot_str(char *bopt, char **val)
42012465Sfrank.van.der.linden@oracle.com {
42112465Sfrank.van.der.linden@oracle.com 	int ret;
42212465Sfrank.van.der.linden@oracle.com 
42312465Sfrank.van.der.linden@oracle.com 	ret = ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo,
42412465Sfrank.van.der.linden@oracle.com 	    DDI_PROP_DONTPASS, bopt, val);
42512465Sfrank.van.der.linden@oracle.com 
42612465Sfrank.van.der.linden@oracle.com 	return (ret);
42712465Sfrank.van.der.linden@oracle.com }
42812465Sfrank.van.der.linden@oracle.com 
42911600SVikram.Hegde@Sun.COM static void
get_bootopt(char * bopt,boolean_t * kvar)43011600SVikram.Hegde@Sun.COM get_bootopt(char *bopt, boolean_t *kvar)
43111600SVikram.Hegde@Sun.COM {
43211600SVikram.Hegde@Sun.COM 	char *val = NULL;
43311600SVikram.Hegde@Sun.COM 
43411600SVikram.Hegde@Sun.COM 	/*
43511600SVikram.Hegde@Sun.COM 	 * All boot options set at the GRUB menu become
43611600SVikram.Hegde@Sun.COM 	 * properties on the rootnex.
43711600SVikram.Hegde@Sun.COM 	 */
43812465Sfrank.van.der.linden@oracle.com 	if (get_boot_str(bopt, &val) != DDI_PROP_SUCCESS)
43912465Sfrank.van.der.linden@oracle.com 		return;
44012465Sfrank.van.der.linden@oracle.com 
44112465Sfrank.van.der.linden@oracle.com 	if (strcmp(val, "true") == 0) {
44212465Sfrank.van.der.linden@oracle.com 		*kvar = B_TRUE;
44312465Sfrank.van.der.linden@oracle.com 	} else if (strcmp(val, "false") == 0) {
44412465Sfrank.van.der.linden@oracle.com 		*kvar = B_FALSE;
44512465Sfrank.van.der.linden@oracle.com 	} else {
44612465Sfrank.van.der.linden@oracle.com 		ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ",
44712465Sfrank.van.der.linden@oracle.com 		    "is not set to true or false. Ignoring option.",
44812465Sfrank.van.der.linden@oracle.com 		    bopt, val);
44911600SVikram.Hegde@Sun.COM 	}
45012465Sfrank.van.der.linden@oracle.com 	ddi_prop_free(val);
45111600SVikram.Hegde@Sun.COM }
45211600SVikram.Hegde@Sun.COM 
45311600SVikram.Hegde@Sun.COM static void
get_boot_dvma_mode(void)45412465Sfrank.van.der.linden@oracle.com get_boot_dvma_mode(void)
45512465Sfrank.van.der.linden@oracle.com {
45612465Sfrank.van.der.linden@oracle.com 	char *val = NULL;
45712465Sfrank.van.der.linden@oracle.com 
45812465Sfrank.van.der.linden@oracle.com 	if (get_boot_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val)
45912465Sfrank.van.der.linden@oracle.com 	    != DDI_PROP_SUCCESS)
46012465Sfrank.van.der.linden@oracle.com 		return;
46112465Sfrank.van.der.linden@oracle.com 
46212465Sfrank.van.der.linden@oracle.com 	if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) {
46312465Sfrank.van.der.linden@oracle.com 		immu_global_dvma_flags |= IMMU_FLAGS_UNITY;
46412465Sfrank.van.der.linden@oracle.com 	} else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) {
46512465Sfrank.van.der.linden@oracle.com 		immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY;
46612465Sfrank.van.der.linden@oracle.com 	} else {
46712465Sfrank.van.der.linden@oracle.com 		ddi_err(DER_WARN, NULL, "bad value \"%s\" for boot option %s",
46812465Sfrank.van.der.linden@oracle.com 		    val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP);
46912465Sfrank.van.der.linden@oracle.com 	}
47012465Sfrank.van.der.linden@oracle.com 	ddi_prop_free(val);
47112465Sfrank.van.der.linden@oracle.com }
47212465Sfrank.van.der.linden@oracle.com 
47312465Sfrank.van.der.linden@oracle.com static void
get_conf_dvma_mode(void)47412465Sfrank.van.der.linden@oracle.com get_conf_dvma_mode(void)
47512465Sfrank.van.der.linden@oracle.com {
47612465Sfrank.van.der.linden@oracle.com 	char *val = NULL;
47712465Sfrank.van.der.linden@oracle.com 
47812465Sfrank.van.der.linden@oracle.com 	if (get_conf_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val)
47912465Sfrank.van.der.linden@oracle.com 	    != DDI_PROP_SUCCESS)
48012465Sfrank.van.der.linden@oracle.com 		return;
48112465Sfrank.van.der.linden@oracle.com 
48212465Sfrank.van.der.linden@oracle.com 	if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) {
48312465Sfrank.van.der.linden@oracle.com 		immu_global_dvma_flags |= IMMU_FLAGS_UNITY;
48412465Sfrank.van.der.linden@oracle.com 	} else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) {
48512465Sfrank.van.der.linden@oracle.com 		immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY;
48612465Sfrank.van.der.linden@oracle.com 	} else {
48712465Sfrank.van.der.linden@oracle.com 		ddi_err(DER_WARN, NULL, "bad value \"%s\" for rootnex "
48812465Sfrank.van.der.linden@oracle.com 		    "option %s", val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP);
48912465Sfrank.van.der.linden@oracle.com 	}
49012465Sfrank.van.der.linden@oracle.com 	ddi_prop_free(val);
49112465Sfrank.van.der.linden@oracle.com }
49212465Sfrank.van.der.linden@oracle.com 
49312465Sfrank.van.der.linden@oracle.com 
49412465Sfrank.van.der.linden@oracle.com static void
get_conf_tunables(char * bopt,int64_t * ivar)49511749SVikram.Hegde@Sun.COM get_conf_tunables(char *bopt, int64_t *ivar)
49611658SVikram.Hegde@Sun.COM {
49711658SVikram.Hegde@Sun.COM 	int64_t	*iarray;
49811658SVikram.Hegde@Sun.COM 	uint_t n;
49911658SVikram.Hegde@Sun.COM 
50011658SVikram.Hegde@Sun.COM 	/*
50111658SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf property
50211658SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
50311658SVikram.Hegde@Sun.COM 	 * property list needs it
50411658SVikram.Hegde@Sun.COM 	 */
50511658SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_int64_array(
50611658SVikram.Hegde@Sun.COM 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
50711658SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt,
50811658SVikram.Hegde@Sun.COM 	    &iarray, &n) != DDI_PROP_SUCCESS) {
50911658SVikram.Hegde@Sun.COM 		return;
51011658SVikram.Hegde@Sun.COM 	}
51111658SVikram.Hegde@Sun.COM 
51211658SVikram.Hegde@Sun.COM 	if (n != 1) {
51311658SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "More than one value specified for "
51411658SVikram.Hegde@Sun.COM 		    "%s property. Ignoring and using default",
51511658SVikram.Hegde@Sun.COM 		    "immu-flush-gran");
51611658SVikram.Hegde@Sun.COM 		ddi_prop_free(iarray);
51711658SVikram.Hegde@Sun.COM 		return;
51811658SVikram.Hegde@Sun.COM 	}
51911658SVikram.Hegde@Sun.COM 
52011658SVikram.Hegde@Sun.COM 	if (iarray[0] < 0) {
52111658SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Negative value specified for "
52211658SVikram.Hegde@Sun.COM 		    "%s property. Inoring and Using default value",
52311658SVikram.Hegde@Sun.COM 		    "immu-flush-gran");
52411658SVikram.Hegde@Sun.COM 		ddi_prop_free(iarray);
52511658SVikram.Hegde@Sun.COM 		return;
52611658SVikram.Hegde@Sun.COM 	}
52711658SVikram.Hegde@Sun.COM 
52811658SVikram.Hegde@Sun.COM 	*ivar = iarray[0];
52911658SVikram.Hegde@Sun.COM 
53011658SVikram.Hegde@Sun.COM 	ddi_prop_free(iarray);
53111658SVikram.Hegde@Sun.COM }
53211658SVikram.Hegde@Sun.COM 
53311658SVikram.Hegde@Sun.COM static void
read_conf_options(void)53411749SVikram.Hegde@Sun.COM read_conf_options(void)
53511749SVikram.Hegde@Sun.COM {
53611749SVikram.Hegde@Sun.COM 	/* enable/disable options */
53711749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-enable", &immu_enable);
53811749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-dvma-enable", &immu_dvma_enable);
53911749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
54011749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable);
54111749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-qinv-enable", &immu_qinv_enable);
54211749SVikram.Hegde@Sun.COM 
54311749SVikram.Hegde@Sun.COM 	/* workaround switches */
54411749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
54511749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
54611749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
54711749SVikram.Hegde@Sun.COM 
54811749SVikram.Hegde@Sun.COM 	/* debug printing */
54911749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-dmar-print", &immu_dmar_print);
55011749SVikram.Hegde@Sun.COM 
55111749SVikram.Hegde@Sun.COM 	/* get tunables */
55211749SVikram.Hegde@Sun.COM 	get_conf_tunables("immu-flush-gran", &immu_flush_gran);
55312465Sfrank.van.der.linden@oracle.com 
55412465Sfrank.van.der.linden@oracle.com 	get_conf_dvma_mode();
55511749SVikram.Hegde@Sun.COM }
55611749SVikram.Hegde@Sun.COM 
55711749SVikram.Hegde@Sun.COM static void
read_boot_options(void)55811600SVikram.Hegde@Sun.COM read_boot_options(void)
55911600SVikram.Hegde@Sun.COM {
56011600SVikram.Hegde@Sun.COM 	/* enable/disable options */
56111600SVikram.Hegde@Sun.COM 	get_bootopt("immu-enable", &immu_enable);
56211600SVikram.Hegde@Sun.COM 	get_bootopt("immu-dvma-enable", &immu_dvma_enable);
56311600SVikram.Hegde@Sun.COM 	get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
56411600SVikram.Hegde@Sun.COM 	get_bootopt("immu-intrmap-enable", &immu_intrmap_enable);
56511600SVikram.Hegde@Sun.COM 	get_bootopt("immu-qinv-enable", &immu_qinv_enable);
56611600SVikram.Hegde@Sun.COM 
56711600SVikram.Hegde@Sun.COM 	/* workaround switches */
56811600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
56911600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
57011600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
57111600SVikram.Hegde@Sun.COM 
57211600SVikram.Hegde@Sun.COM 	/* debug printing */
57311600SVikram.Hegde@Sun.COM 	get_bootopt("immu-dmar-print", &immu_dmar_print);
57412465Sfrank.van.der.linden@oracle.com 
57512465Sfrank.van.der.linden@oracle.com 	get_boot_dvma_mode();
57612465Sfrank.van.der.linden@oracle.com }
57712465Sfrank.van.der.linden@oracle.com 
57812465Sfrank.van.der.linden@oracle.com static void
mapping_list_setup(void)57912465Sfrank.van.der.linden@oracle.com mapping_list_setup(void)
58012465Sfrank.van.der.linden@oracle.com {
58112465Sfrank.van.der.linden@oracle.com 	char **string_array;
58212465Sfrank.van.der.linden@oracle.com 	uint_t nstrings;
58312465Sfrank.van.der.linden@oracle.com 
58412465Sfrank.van.der.linden@oracle.com 	if (ddi_prop_lookup_string_array(
58512465Sfrank.van.der.linden@oracle.com 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
58612465Sfrank.van.der.linden@oracle.com 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
58712465Sfrank.van.der.linden@oracle.com 	    "immu-dvma-unity-drivers",
58812465Sfrank.van.der.linden@oracle.com 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
58912465Sfrank.van.der.linden@oracle.com 		unity_driver_array = string_array;
59012465Sfrank.van.der.linden@oracle.com 		nunity = nstrings;
59112465Sfrank.van.der.linden@oracle.com 	}
59212465Sfrank.van.der.linden@oracle.com 
59312465Sfrank.van.der.linden@oracle.com 	if (ddi_prop_lookup_string_array(
59412465Sfrank.van.der.linden@oracle.com 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
59512465Sfrank.van.der.linden@oracle.com 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
59612465Sfrank.van.der.linden@oracle.com 	    "immu-dvma-xlate-drivers",
59712465Sfrank.van.der.linden@oracle.com 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
59812465Sfrank.van.der.linden@oracle.com 		xlate_driver_array = string_array;
59912465Sfrank.van.der.linden@oracle.com 		nxlate = nstrings;
60012465Sfrank.van.der.linden@oracle.com 	}
60113050Sfrank.van.der.linden@oracle.com 
60213050Sfrank.van.der.linden@oracle.com 	if (ddi_prop_lookup_string_array(
60313050Sfrank.van.der.linden@oracle.com 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
60413050Sfrank.van.der.linden@oracle.com 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
60513050Sfrank.van.der.linden@oracle.com 	    "immu-dvma-premap-drivers",
60613050Sfrank.van.der.linden@oracle.com 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
60713050Sfrank.van.der.linden@oracle.com 		premap_driver_array = string_array;
60813050Sfrank.van.der.linden@oracle.com 		npremap = nstrings;
60913050Sfrank.van.der.linden@oracle.com 	}
61013050Sfrank.van.der.linden@oracle.com 
61113050Sfrank.van.der.linden@oracle.com 	if (ddi_prop_lookup_string_array(
61213050Sfrank.van.der.linden@oracle.com 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
61313050Sfrank.van.der.linden@oracle.com 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
61413050Sfrank.van.der.linden@oracle.com 	    "immu-dvma-nopremap-drivers",
61513050Sfrank.van.der.linden@oracle.com 	    &string_array, &nstrings) == DDI_PROP_SUCCESS) {
61613050Sfrank.van.der.linden@oracle.com 		nopremap_driver_array = string_array;
61713050Sfrank.van.der.linden@oracle.com 		nnopremap = nstrings;
61813050Sfrank.van.der.linden@oracle.com 	}
61911600SVikram.Hegde@Sun.COM }
62011600SVikram.Hegde@Sun.COM 
62111600SVikram.Hegde@Sun.COM /*
62211600SVikram.Hegde@Sun.COM  * Note, this will not catch hardware not enumerated
62311600SVikram.Hegde@Sun.COM  * in early boot
62411600SVikram.Hegde@Sun.COM  */
62511600SVikram.Hegde@Sun.COM static boolean_t
blacklisted_driver(void)62611600SVikram.Hegde@Sun.COM blacklisted_driver(void)
62711600SVikram.Hegde@Sun.COM {
62811600SVikram.Hegde@Sun.COM 	char **strptr;
62911600SVikram.Hegde@Sun.COM 	int i;
63011600SVikram.Hegde@Sun.COM 	major_t maj;
63111600SVikram.Hegde@Sun.COM 
63211600SVikram.Hegde@Sun.COM 	/* need at least 2 strings */
63311600SVikram.Hegde@Sun.COM 	if (nblacks < 2) {
63411600SVikram.Hegde@Sun.COM 		return (B_FALSE);
63511600SVikram.Hegde@Sun.COM 	}
63611600SVikram.Hegde@Sun.COM 
63711600SVikram.Hegde@Sun.COM 	for (i = 0; nblacks - i > 1; i++) {
63811658SVikram.Hegde@Sun.COM 		strptr = &black_array[i];
63911600SVikram.Hegde@Sun.COM 		if (strcmp(*strptr++, "DRIVER") == 0) {
64011600SVikram.Hegde@Sun.COM 			if ((maj = ddi_name_to_major(*strptr++))
64111600SVikram.Hegde@Sun.COM 			    != DDI_MAJOR_T_NONE) {
64211600SVikram.Hegde@Sun.COM 				/* is there hardware bound to this drvr */
64311600SVikram.Hegde@Sun.COM 				if (devnamesp[maj].dn_head != NULL) {
64411600SVikram.Hegde@Sun.COM 					return (B_TRUE);
64511600SVikram.Hegde@Sun.COM 				}
64611600SVikram.Hegde@Sun.COM 			}
64711600SVikram.Hegde@Sun.COM 			i += 1;   /* for loop adds 1, so add only 1 here */
64811600SVikram.Hegde@Sun.COM 		}
64911600SVikram.Hegde@Sun.COM 	}
65011600SVikram.Hegde@Sun.COM 
65111600SVikram.Hegde@Sun.COM 	return (B_FALSE);
65211600SVikram.Hegde@Sun.COM }
65311600SVikram.Hegde@Sun.COM 
65411600SVikram.Hegde@Sun.COM static boolean_t
blacklisted_smbios(void)65511600SVikram.Hegde@Sun.COM blacklisted_smbios(void)
65611600SVikram.Hegde@Sun.COM {
65711600SVikram.Hegde@Sun.COM 	id_t smid;
65811600SVikram.Hegde@Sun.COM 	smbios_hdl_t *smhdl;
65911600SVikram.Hegde@Sun.COM 	smbios_info_t sminf;
66011600SVikram.Hegde@Sun.COM 	smbios_system_t smsys;
66111600SVikram.Hegde@Sun.COM 	char *mfg, *product, *version;
66211600SVikram.Hegde@Sun.COM 	char **strptr;
66311600SVikram.Hegde@Sun.COM 	int i;
66411600SVikram.Hegde@Sun.COM 
66511600SVikram.Hegde@Sun.COM 	/* need at least 4 strings for this setting */
66611600SVikram.Hegde@Sun.COM 	if (nblacks < 4) {
66711600SVikram.Hegde@Sun.COM 		return (B_FALSE);
66811600SVikram.Hegde@Sun.COM 	}
66911600SVikram.Hegde@Sun.COM 
67011600SVikram.Hegde@Sun.COM 	smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
67111600SVikram.Hegde@Sun.COM 	if (smhdl == NULL ||
67211600SVikram.Hegde@Sun.COM 	    (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR ||
67311600SVikram.Hegde@Sun.COM 	    smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) {
67411600SVikram.Hegde@Sun.COM 		return (B_FALSE);
67511600SVikram.Hegde@Sun.COM 	}
67611600SVikram.Hegde@Sun.COM 
67711600SVikram.Hegde@Sun.COM 	mfg = (char *)sminf.smbi_manufacturer;
67811600SVikram.Hegde@Sun.COM 	product = (char *)sminf.smbi_product;
67911600SVikram.Hegde@Sun.COM 	version = (char *)sminf.smbi_version;
68011600SVikram.Hegde@Sun.COM 
68111600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n");
68211600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg);
68311600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product);
68411600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version);
68511600SVikram.Hegde@Sun.COM 
68611600SVikram.Hegde@Sun.COM 	for (i = 0; nblacks - i > 3; i++) {
68711658SVikram.Hegde@Sun.COM 		strptr = &black_array[i];
68811600SVikram.Hegde@Sun.COM 		if (strcmp(*strptr++, "SMBIOS") == 0) {
68911600SVikram.Hegde@Sun.COM 			if (strcmp(*strptr++, mfg) == 0 &&
69011600SVikram.Hegde@Sun.COM 			    ((char *)strptr == '\0' ||
69111600SVikram.Hegde@Sun.COM 			    strcmp(*strptr++, product) == 0) &&
69211600SVikram.Hegde@Sun.COM 			    ((char *)strptr == '\0' ||
69311600SVikram.Hegde@Sun.COM 			    strcmp(*strptr++, version) == 0)) {
69411600SVikram.Hegde@Sun.COM 				return (B_TRUE);
69511600SVikram.Hegde@Sun.COM 			}
69611600SVikram.Hegde@Sun.COM 			i += 3;
69711600SVikram.Hegde@Sun.COM 		}
69811600SVikram.Hegde@Sun.COM 	}
69911600SVikram.Hegde@Sun.COM 
70011600SVikram.Hegde@Sun.COM 	return (B_FALSE);
70111600SVikram.Hegde@Sun.COM }
70211600SVikram.Hegde@Sun.COM 
70311600SVikram.Hegde@Sun.COM static boolean_t
blacklisted_acpi(void)70411600SVikram.Hegde@Sun.COM blacklisted_acpi(void)
70511600SVikram.Hegde@Sun.COM {
70611600SVikram.Hegde@Sun.COM 	if (nblacks == 0) {
70711600SVikram.Hegde@Sun.COM 		return (B_FALSE);
70811600SVikram.Hegde@Sun.COM 	}
70911600SVikram.Hegde@Sun.COM 
71011600SVikram.Hegde@Sun.COM 	return (immu_dmar_blacklisted(black_array, nblacks));
71111600SVikram.Hegde@Sun.COM }
71211600SVikram.Hegde@Sun.COM 
71311600SVikram.Hegde@Sun.COM /*
71411600SVikram.Hegde@Sun.COM  * Check if system is blacklisted by Intel IOMMU driver
71511600SVikram.Hegde@Sun.COM  * i.e. should Intel IOMMU be disabled on this system
71611600SVikram.Hegde@Sun.COM  * Currently a system can be blacklistd based on the
71711600SVikram.Hegde@Sun.COM  * following bases:
71811600SVikram.Hegde@Sun.COM  *
71911600SVikram.Hegde@Sun.COM  * 1. DMAR ACPI table information.
72011600SVikram.Hegde@Sun.COM  *    This information includes things like
72111600SVikram.Hegde@Sun.COM  *    manufacturer and revision number. If rootnex.conf
72211600SVikram.Hegde@Sun.COM  *    has matching info set in its blacklist property
72311600SVikram.Hegde@Sun.COM  *    then Intel IOMMu will be disabled
72411600SVikram.Hegde@Sun.COM  *
72511600SVikram.Hegde@Sun.COM  * 2. SMBIOS information
72611600SVikram.Hegde@Sun.COM  *
72711600SVikram.Hegde@Sun.COM  * 3. Driver installed - useful if a particular
72811600SVikram.Hegde@Sun.COM  *    driver or hardware is toxic if Intel IOMMU
72911600SVikram.Hegde@Sun.COM  *    is turned on.
73011600SVikram.Hegde@Sun.COM  */
73111600SVikram.Hegde@Sun.COM 
73211600SVikram.Hegde@Sun.COM static void
blacklist_setup(void)73311600SVikram.Hegde@Sun.COM blacklist_setup(void)
73411600SVikram.Hegde@Sun.COM {
73511600SVikram.Hegde@Sun.COM 	char **string_array;
73611600SVikram.Hegde@Sun.COM 	uint_t nstrings;
73711600SVikram.Hegde@Sun.COM 
73811600SVikram.Hegde@Sun.COM 	/*
73911600SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf blacklist property.
74011600SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
74111600SVikram.Hegde@Sun.COM 	 * property list needs it
74211600SVikram.Hegde@Sun.COM 	 */
74311600SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string_array(
74411600SVikram.Hegde@Sun.COM 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
74511600SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist",
74611600SVikram.Hegde@Sun.COM 	    &string_array, &nstrings) != DDI_PROP_SUCCESS) {
74711600SVikram.Hegde@Sun.COM 		return;
74811600SVikram.Hegde@Sun.COM 	}
74911600SVikram.Hegde@Sun.COM 
75011600SVikram.Hegde@Sun.COM 	/* smallest blacklist criteria works with multiples of 2 */
75111600SVikram.Hegde@Sun.COM 	if (nstrings % 2 != 0) {
75211600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist "
75311600SVikram.Hegde@Sun.COM 		    "rootnex.conf: number of strings must be a "
75411600SVikram.Hegde@Sun.COM 		    "multiple of 2");
75511600SVikram.Hegde@Sun.COM 		ddi_prop_free(string_array);
75611600SVikram.Hegde@Sun.COM 		return;
75711600SVikram.Hegde@Sun.COM 	}
75811600SVikram.Hegde@Sun.COM 
75911600SVikram.Hegde@Sun.COM 	black_array = string_array;
76011600SVikram.Hegde@Sun.COM 	nblacks = nstrings;
76111600SVikram.Hegde@Sun.COM }
76211600SVikram.Hegde@Sun.COM 
76311600SVikram.Hegde@Sun.COM static void
blacklist_destroy(void)76411600SVikram.Hegde@Sun.COM blacklist_destroy(void)
76511600SVikram.Hegde@Sun.COM {
76611600SVikram.Hegde@Sun.COM 	if (black_array) {
76711600SVikram.Hegde@Sun.COM 		ddi_prop_free(black_array);
76811600SVikram.Hegde@Sun.COM 		black_array = NULL;
76911600SVikram.Hegde@Sun.COM 		nblacks = 0;
77011600SVikram.Hegde@Sun.COM 	}
77113050Sfrank.van.der.linden@oracle.com }
77211600SVikram.Hegde@Sun.COM 
77313050Sfrank.van.der.linden@oracle.com static char *
immu_alloc_name(const char * str,int instance)77413050Sfrank.van.der.linden@oracle.com immu_alloc_name(const char *str, int instance)
77513050Sfrank.van.der.linden@oracle.com {
77613050Sfrank.van.der.linden@oracle.com 	size_t slen;
77713050Sfrank.van.der.linden@oracle.com 	char *s;
77813050Sfrank.van.der.linden@oracle.com 
77913050Sfrank.van.der.linden@oracle.com 	slen = strlen(str) + IMMU_ISTRLEN + 1;
78013050Sfrank.van.der.linden@oracle.com 	s = kmem_zalloc(slen, VM_SLEEP);
78113050Sfrank.van.der.linden@oracle.com 	if (s != NULL)
78213050Sfrank.van.der.linden@oracle.com 		(void) snprintf(s, slen, "%s%d", str, instance);
78313050Sfrank.van.der.linden@oracle.com 
78413050Sfrank.van.der.linden@oracle.com 	return (s);
78511600SVikram.Hegde@Sun.COM }
78611600SVikram.Hegde@Sun.COM 
78711600SVikram.Hegde@Sun.COM 
78811600SVikram.Hegde@Sun.COM /*
78911600SVikram.Hegde@Sun.COM  * Now set all the fields in the order they are defined
79011600SVikram.Hegde@Sun.COM  * We do this only as a defensive-coding practice, it is
79111600SVikram.Hegde@Sun.COM  * not a correctness issue.
79211600SVikram.Hegde@Sun.COM  */
79311600SVikram.Hegde@Sun.COM static void *
immu_state_alloc(int seg,void * dmar_unit)79411600SVikram.Hegde@Sun.COM immu_state_alloc(int seg, void *dmar_unit)
79511600SVikram.Hegde@Sun.COM {
79611600SVikram.Hegde@Sun.COM 	immu_t *immu;
79713050Sfrank.van.der.linden@oracle.com 	char *nodename, *hcachename, *pcachename;
79813050Sfrank.van.der.linden@oracle.com 	int instance;
79911600SVikram.Hegde@Sun.COM 
80011600SVikram.Hegde@Sun.COM 	dmar_unit = immu_dmar_walk_units(seg, dmar_unit);
80111600SVikram.Hegde@Sun.COM 	if (dmar_unit == NULL) {
80211600SVikram.Hegde@Sun.COM 		/* No more IOMMUs in this segment */
80311600SVikram.Hegde@Sun.COM 		return (NULL);
80411600SVikram.Hegde@Sun.COM 	}
80511600SVikram.Hegde@Sun.COM 
80611600SVikram.Hegde@Sun.COM 	immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP);
80711600SVikram.Hegde@Sun.COM 
80811600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL);
80911600SVikram.Hegde@Sun.COM 
81011600SVikram.Hegde@Sun.COM 	mutex_enter(&(immu->immu_lock));
81111600SVikram.Hegde@Sun.COM 
81211600SVikram.Hegde@Sun.COM 	immu->immu_dmar_unit = dmar_unit;
81311600SVikram.Hegde@Sun.COM 	immu->immu_dip = immu_dmar_unit_dip(dmar_unit);
81411600SVikram.Hegde@Sun.COM 
81513050Sfrank.van.der.linden@oracle.com 	nodename = ddi_node_name(immu->immu_dip);
81613050Sfrank.van.der.linden@oracle.com 	instance = ddi_get_instance(immu->immu_dip);
81713050Sfrank.van.der.linden@oracle.com 
81813050Sfrank.van.der.linden@oracle.com 	immu->immu_name = immu_alloc_name(nodename, instance);
81913050Sfrank.van.der.linden@oracle.com 	if (immu->immu_name == NULL)
82013050Sfrank.van.der.linden@oracle.com 		return (NULL);
82113050Sfrank.van.der.linden@oracle.com 
82211600SVikram.Hegde@Sun.COM 	/*
82311600SVikram.Hegde@Sun.COM 	 * the immu_intr_lock mutex is grabbed by the IOMMU
82411600SVikram.Hegde@Sun.COM 	 * unit's interrupt handler so we need to use an
82511600SVikram.Hegde@Sun.COM 	 * interrupt cookie for the mutex
82611600SVikram.Hegde@Sun.COM 	 */
82711600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER,
82811600SVikram.Hegde@Sun.COM 	    (void *)ipltospl(IMMU_INTR_IPL));
82911600SVikram.Hegde@Sun.COM 
83011600SVikram.Hegde@Sun.COM 	/* IOMMU regs related */
83111600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL);
83211658SVikram.Hegde@Sun.COM 	cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL);
83311658SVikram.Hegde@Sun.COM 	immu->immu_regs_busy = B_FALSE;
83411600SVikram.Hegde@Sun.COM 
83511600SVikram.Hegde@Sun.COM 	/* DVMA related */
83611600SVikram.Hegde@Sun.COM 	immu->immu_dvma_coherent = B_FALSE;
83711600SVikram.Hegde@Sun.COM 
83811600SVikram.Hegde@Sun.COM 	/* DVMA context related */
83911600SVikram.Hegde@Sun.COM 	rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL);
84011600SVikram.Hegde@Sun.COM 
84111600SVikram.Hegde@Sun.COM 	/* DVMA domain related */
84211600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_domain_list), sizeof (domain_t),
84311600SVikram.Hegde@Sun.COM 	    offsetof(domain_t, dom_immu_node));
84411600SVikram.Hegde@Sun.COM 
84511600SVikram.Hegde@Sun.COM 	/* DVMA special device lists */
84611600SVikram.Hegde@Sun.COM 	immu->immu_dvma_gfx_only = B_FALSE;
84711600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t),
84811600SVikram.Hegde@Sun.COM 	    offsetof(immu_devi_t, imd_spc_node));
84911600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t),
85011600SVikram.Hegde@Sun.COM 	    offsetof(immu_devi_t, imd_spc_node));
85111600SVikram.Hegde@Sun.COM 
85211600SVikram.Hegde@Sun.COM 	/* interrupt remapping related */
85311600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL);
85411600SVikram.Hegde@Sun.COM 
85511600SVikram.Hegde@Sun.COM 	/* qinv related */
85611600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL);
85711600SVikram.Hegde@Sun.COM 
85811600SVikram.Hegde@Sun.COM 	/*
85911600SVikram.Hegde@Sun.COM 	 * insert this immu unit into the system-wide list
86011600SVikram.Hegde@Sun.COM 	 */
86111600SVikram.Hegde@Sun.COM 	list_insert_tail(&immu_list, immu);
86211600SVikram.Hegde@Sun.COM 
86313050Sfrank.van.der.linden@oracle.com 	pcachename = immu_alloc_name("immu_pgtable_cache", instance);
86413050Sfrank.van.der.linden@oracle.com 	if (pcachename == NULL)
86513050Sfrank.van.der.linden@oracle.com 		return (NULL);
86613050Sfrank.van.der.linden@oracle.com 
86713050Sfrank.van.der.linden@oracle.com 	hcachename = immu_alloc_name("immu_hdl_cache", instance);
86813050Sfrank.van.der.linden@oracle.com 	if (hcachename == NULL)
86913050Sfrank.van.der.linden@oracle.com 		return (NULL);
87013050Sfrank.van.der.linden@oracle.com 
87113050Sfrank.van.der.linden@oracle.com 	immu->immu_pgtable_cache = kmem_cache_create(pcachename,
87213050Sfrank.van.der.linden@oracle.com 	    sizeof (pgtable_t), 0, pgtable_ctor, pgtable_dtor, NULL, immu,
87313050Sfrank.van.der.linden@oracle.com 	    NULL, 0);
87413050Sfrank.van.der.linden@oracle.com 	immu->immu_hdl_cache = kmem_cache_create(hcachename,
87513050Sfrank.van.der.linden@oracle.com 	    sizeof (immu_hdl_priv_t), 64, immu_hdl_priv_ctor,
87613050Sfrank.van.der.linden@oracle.com 	    NULL, NULL, immu, NULL, 0);
87713050Sfrank.van.der.linden@oracle.com 
87811600SVikram.Hegde@Sun.COM 	mutex_exit(&(immu->immu_lock));
87911600SVikram.Hegde@Sun.COM 
88013050Sfrank.van.der.linden@oracle.com 	ddi_err(DER_LOG, immu->immu_dip, "unit setup");
88111600SVikram.Hegde@Sun.COM 
88211600SVikram.Hegde@Sun.COM 	immu_dmar_set_immu(dmar_unit, immu);
88311600SVikram.Hegde@Sun.COM 
88411600SVikram.Hegde@Sun.COM 	return (dmar_unit);
88511600SVikram.Hegde@Sun.COM }
88611600SVikram.Hegde@Sun.COM 
88711600SVikram.Hegde@Sun.COM static void
immu_subsystems_setup(void)88811600SVikram.Hegde@Sun.COM immu_subsystems_setup(void)
88911600SVikram.Hegde@Sun.COM {
89011600SVikram.Hegde@Sun.COM 	int seg;
89111600SVikram.Hegde@Sun.COM 	void *unit_hdl;
89211600SVikram.Hegde@Sun.COM 
89311600SVikram.Hegde@Sun.COM 	ddi_err(DER_VERB, NULL,
89413050Sfrank.van.der.linden@oracle.com 	    "Creating state structures for Intel IOMMU units");
89511600SVikram.Hegde@Sun.COM 
89611600SVikram.Hegde@Sun.COM 	mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL);
89711600SVikram.Hegde@Sun.COM 	list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node));
89811600SVikram.Hegde@Sun.COM 
89911600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
90011600SVikram.Hegde@Sun.COM 
90111600SVikram.Hegde@Sun.COM 	unit_hdl = NULL;
90211600SVikram.Hegde@Sun.COM 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
90311600SVikram.Hegde@Sun.COM 		while (unit_hdl = immu_state_alloc(seg, unit_hdl)) {
90411600SVikram.Hegde@Sun.COM 			;
90511600SVikram.Hegde@Sun.COM 		}
90611600SVikram.Hegde@Sun.COM 	}
90711600SVikram.Hegde@Sun.COM 
90811600SVikram.Hegde@Sun.COM 	immu_regs_setup(&immu_list);	/* subsequent code needs this first */
90911600SVikram.Hegde@Sun.COM 	immu_dvma_setup(&immu_list);
91012716Sfrank.van.der.linden@oracle.com 	if (immu_qinv_setup(&immu_list) == DDI_SUCCESS)
91112716Sfrank.van.der.linden@oracle.com 		immu_intrmap_setup(&immu_list);
91212716Sfrank.van.der.linden@oracle.com 	else
91312716Sfrank.van.der.linden@oracle.com 		immu_intrmap_enable = B_FALSE;
91411600SVikram.Hegde@Sun.COM 
91511600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
91611600SVikram.Hegde@Sun.COM }
91711600SVikram.Hegde@Sun.COM 
91811600SVikram.Hegde@Sun.COM /*
91911600SVikram.Hegde@Sun.COM  * immu_subsystems_startup()
92011600SVikram.Hegde@Sun.COM  * 	startup all units that were setup
92111600SVikram.Hegde@Sun.COM  */
92211600SVikram.Hegde@Sun.COM static void
immu_subsystems_startup(void)92311600SVikram.Hegde@Sun.COM immu_subsystems_startup(void)
92411600SVikram.Hegde@Sun.COM {
92511600SVikram.Hegde@Sun.COM 	immu_t *immu;
92613050Sfrank.van.der.linden@oracle.com 	iommulib_ops_t *iommulib_ops;
92711600SVikram.Hegde@Sun.COM 
92811600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
92911600SVikram.Hegde@Sun.COM 
93011600SVikram.Hegde@Sun.COM 	immu_dmar_startup();
93111600SVikram.Hegde@Sun.COM 
93211600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
93311600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
93411600SVikram.Hegde@Sun.COM 
93511600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
93611600SVikram.Hegde@Sun.COM 
93711600SVikram.Hegde@Sun.COM 		immu_intr_register(immu);
93811600SVikram.Hegde@Sun.COM 		immu_dvma_startup(immu);
93911600SVikram.Hegde@Sun.COM 		immu_intrmap_startup(immu);
94011600SVikram.Hegde@Sun.COM 		immu_qinv_startup(immu);
94111600SVikram.Hegde@Sun.COM 
94211600SVikram.Hegde@Sun.COM 		/*
94311600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
94411600SVikram.Hegde@Sun.COM 		 * the actual startup. This will
94511600SVikram.Hegde@Sun.COM 		 * set immu->immu_running  field
94611600SVikram.Hegde@Sun.COM 		 * if the unit is successfully
94711600SVikram.Hegde@Sun.COM 		 * started
94811600SVikram.Hegde@Sun.COM 		 */
94911600SVikram.Hegde@Sun.COM 		immu_regs_startup(immu);
95011600SVikram.Hegde@Sun.COM 
95111600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
95213050Sfrank.van.der.linden@oracle.com 
95313050Sfrank.van.der.linden@oracle.com 		iommulib_ops = kmem_alloc(sizeof (iommulib_ops_t), KM_SLEEP);
95413050Sfrank.van.der.linden@oracle.com 		*iommulib_ops = immulib_ops;
95513050Sfrank.van.der.linden@oracle.com 		iommulib_ops->ilops_data = (void *)immu;
95613050Sfrank.van.der.linden@oracle.com 		(void) iommulib_iommu_register(immu->immu_dip, iommulib_ops,
95713050Sfrank.van.der.linden@oracle.com 		    &immu->immu_iommulib_handle);
95811600SVikram.Hegde@Sun.COM 	}
95911600SVikram.Hegde@Sun.COM 
96011600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
96111600SVikram.Hegde@Sun.COM }
96211600SVikram.Hegde@Sun.COM 
96311600SVikram.Hegde@Sun.COM /* ##################  Intel IOMMU internal interfaces ###################### */
96411600SVikram.Hegde@Sun.COM 
96511600SVikram.Hegde@Sun.COM /*
96611600SVikram.Hegde@Sun.COM  * Internal interfaces for IOMMU code (i.e. not exported to rootnex
96711600SVikram.Hegde@Sun.COM  * or rest of system)
96811600SVikram.Hegde@Sun.COM  */
96911600SVikram.Hegde@Sun.COM 
97011600SVikram.Hegde@Sun.COM /*
97111600SVikram.Hegde@Sun.COM  * ddip can be NULL, in which case we walk up until we find the root dip
97211600SVikram.Hegde@Sun.COM  * NOTE: We never visit the root dip since its not a hardware node
97311600SVikram.Hegde@Sun.COM  */
97411600SVikram.Hegde@Sun.COM 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)97511600SVikram.Hegde@Sun.COM immu_walk_ancestor(
97611600SVikram.Hegde@Sun.COM 	dev_info_t *rdip,
97711600SVikram.Hegde@Sun.COM 	dev_info_t *ddip,
97811600SVikram.Hegde@Sun.COM 	int (*func)(dev_info_t *, void *arg),
97911600SVikram.Hegde@Sun.COM 	void *arg,
98011600SVikram.Hegde@Sun.COM 	int *lvlp,
98111600SVikram.Hegde@Sun.COM 	immu_flags_t immu_flags)
98211600SVikram.Hegde@Sun.COM {
98311600SVikram.Hegde@Sun.COM 	dev_info_t *pdip;
98411600SVikram.Hegde@Sun.COM 	int level;
98511600SVikram.Hegde@Sun.COM 	int error = DDI_SUCCESS;
98611600SVikram.Hegde@Sun.COM 
98711600SVikram.Hegde@Sun.COM 	/* ddip and immu can be NULL */
98811600SVikram.Hegde@Sun.COM 
98911600SVikram.Hegde@Sun.COM 	/* Hold rdip so that branch is not detached */
99011600SVikram.Hegde@Sun.COM 	ndi_hold_devi(rdip);
99111600SVikram.Hegde@Sun.COM 	for (pdip = rdip, level = 1; pdip && pdip != root_devinfo;
99211600SVikram.Hegde@Sun.COM 	    pdip = ddi_get_parent(pdip), level++) {
99311600SVikram.Hegde@Sun.COM 
99411600SVikram.Hegde@Sun.COM 		if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) {
99511600SVikram.Hegde@Sun.COM 			error = DDI_FAILURE;
99611600SVikram.Hegde@Sun.COM 			break;
99711600SVikram.Hegde@Sun.COM 		}
99811600SVikram.Hegde@Sun.COM 		if (func(pdip, arg) == DDI_WALK_TERMINATE) {
99911600SVikram.Hegde@Sun.COM 			break;
100011600SVikram.Hegde@Sun.COM 		}
100111600SVikram.Hegde@Sun.COM 		if (immu_flags & IMMU_FLAGS_DONTPASS) {
100211600SVikram.Hegde@Sun.COM 			break;
100311600SVikram.Hegde@Sun.COM 		}
100411600SVikram.Hegde@Sun.COM 		if (pdip == ddip) {
100511600SVikram.Hegde@Sun.COM 			break;
100611600SVikram.Hegde@Sun.COM 		}
100711600SVikram.Hegde@Sun.COM 	}
100811600SVikram.Hegde@Sun.COM 
100911600SVikram.Hegde@Sun.COM 	ndi_rele_devi(rdip);
101011600SVikram.Hegde@Sun.COM 
101111600SVikram.Hegde@Sun.COM 	if (lvlp)
101211600SVikram.Hegde@Sun.COM 		*lvlp = level;
101311600SVikram.Hegde@Sun.COM 
101411600SVikram.Hegde@Sun.COM 	return (error);
101511600SVikram.Hegde@Sun.COM }
101611600SVikram.Hegde@Sun.COM 
101711600SVikram.Hegde@Sun.COM /* ########################  Intel IOMMU entry points ####################### */
101811600SVikram.Hegde@Sun.COM /*
101911600SVikram.Hegde@Sun.COM  * immu_init()
102011600SVikram.Hegde@Sun.COM  *	called from rootnex_attach(). setup but don't startup the Intel IOMMU
102111600SVikram.Hegde@Sun.COM  *      This is the first function called in Intel IOMMU code
102211600SVikram.Hegde@Sun.COM  */
102311600SVikram.Hegde@Sun.COM void
immu_init(void)102411600SVikram.Hegde@Sun.COM immu_init(void)
102511600SVikram.Hegde@Sun.COM {
102611600SVikram.Hegde@Sun.COM 	char *phony_reg = "A thing of beauty is a joy forever";
102711600SVikram.Hegde@Sun.COM 
102811600SVikram.Hegde@Sun.COM 	/* Set some global shorthands that are needed by all of IOMMU code */
102911600SVikram.Hegde@Sun.COM 	root_devinfo = ddi_root_node();
103011600SVikram.Hegde@Sun.COM 
103111600SVikram.Hegde@Sun.COM 	/*
103211600SVikram.Hegde@Sun.COM 	 * Intel IOMMU only supported only if MMU(CPU) page size is ==
103311600SVikram.Hegde@Sun.COM 	 * IOMMU pages size.
103411600SVikram.Hegde@Sun.COM 	 */
103511600SVikram.Hegde@Sun.COM 	/*LINTED*/
103611600SVikram.Hegde@Sun.COM 	if (MMU_PAGESIZE != IMMU_PAGESIZE) {
103711600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL,
103811600SVikram.Hegde@Sun.COM 		    "MMU page size (%d) is not equal to\n"
103911600SVikram.Hegde@Sun.COM 		    "IOMMU page size (%d). "
104011600SVikram.Hegde@Sun.COM 		    "Disabling Intel IOMMU. ",
104111600SVikram.Hegde@Sun.COM 		    MMU_PAGESIZE, IMMU_PAGESIZE);
104211600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
104311600SVikram.Hegde@Sun.COM 		return;
104411600SVikram.Hegde@Sun.COM 	}
104511600SVikram.Hegde@Sun.COM 
104611600SVikram.Hegde@Sun.COM 	/*
104711749SVikram.Hegde@Sun.COM 	 * Read rootnex.conf options. Do this before
104811749SVikram.Hegde@Sun.COM 	 * boot options so boot options can override .conf options.
104911749SVikram.Hegde@Sun.COM 	 */
105011749SVikram.Hegde@Sun.COM 	read_conf_options();
105111749SVikram.Hegde@Sun.COM 
105211749SVikram.Hegde@Sun.COM 	/*
105311600SVikram.Hegde@Sun.COM 	 * retrieve the Intel IOMMU boot options.
105411600SVikram.Hegde@Sun.COM 	 * Do this before parsing immu ACPI table
105511600SVikram.Hegde@Sun.COM 	 * as a boot option could potentially affect
105611600SVikram.Hegde@Sun.COM 	 * ACPI parsing.
105711600SVikram.Hegde@Sun.COM 	 */
105811600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n");
105911600SVikram.Hegde@Sun.COM 	read_boot_options();
106011600SVikram.Hegde@Sun.COM 
106111600SVikram.Hegde@Sun.COM 	/*
106211600SVikram.Hegde@Sun.COM 	 * Check the IOMMU enable boot-option first.
106311600SVikram.Hegde@Sun.COM 	 * This is so that we can skip parsing the ACPI table
106411600SVikram.Hegde@Sun.COM 	 * if necessary because that may cause problems in
106511600SVikram.Hegde@Sun.COM 	 * systems with buggy BIOS or ACPI tables
106611600SVikram.Hegde@Sun.COM 	 */
106711600SVikram.Hegde@Sun.COM 	if (immu_enable == B_FALSE) {
106811600SVikram.Hegde@Sun.COM 		return;
106911600SVikram.Hegde@Sun.COM 	}
107011600SVikram.Hegde@Sun.COM 
107112513Sfrank.van.der.linden@oracle.com 	if (immu_intrmap_enable == B_TRUE)
107212513Sfrank.van.der.linden@oracle.com 		immu_qinv_enable = B_TRUE;
107312513Sfrank.van.der.linden@oracle.com 
107411600SVikram.Hegde@Sun.COM 	/*
107511600SVikram.Hegde@Sun.COM 	 * Next, check if the system even has an Intel IOMMU
107611600SVikram.Hegde@Sun.COM 	 * We use the presence or absence of the IOMMU ACPI
107711600SVikram.Hegde@Sun.COM 	 * table to detect Intel IOMMU.
107811600SVikram.Hegde@Sun.COM 	 */
107911600SVikram.Hegde@Sun.COM 	if (immu_dmar_setup() != DDI_SUCCESS) {
108011600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
108111600SVikram.Hegde@Sun.COM 		return;
108211600SVikram.Hegde@Sun.COM 	}
108311600SVikram.Hegde@Sun.COM 
108412465Sfrank.van.der.linden@oracle.com 	mapping_list_setup();
108512465Sfrank.van.der.linden@oracle.com 
108611600SVikram.Hegde@Sun.COM 	/*
108711600SVikram.Hegde@Sun.COM 	 * Check blacklists
108811600SVikram.Hegde@Sun.COM 	 */
108911600SVikram.Hegde@Sun.COM 	blacklist_setup();
109011600SVikram.Hegde@Sun.COM 
109111600SVikram.Hegde@Sun.COM 	if (blacklisted_smbios() == B_TRUE) {
109211600SVikram.Hegde@Sun.COM 		blacklist_destroy();
109311600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
109411600SVikram.Hegde@Sun.COM 		return;
109511600SVikram.Hegde@Sun.COM 	}
109611600SVikram.Hegde@Sun.COM 
109711600SVikram.Hegde@Sun.COM 	if (blacklisted_driver() == B_TRUE) {
109811600SVikram.Hegde@Sun.COM 		blacklist_destroy();
109911600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
110011600SVikram.Hegde@Sun.COM 		return;
110111600SVikram.Hegde@Sun.COM 	}
110211600SVikram.Hegde@Sun.COM 
110311600SVikram.Hegde@Sun.COM 	/*
110411600SVikram.Hegde@Sun.COM 	 * Read the "raw" DMAR ACPI table to get information
110511600SVikram.Hegde@Sun.COM 	 * and convert into a form we can use.
110611600SVikram.Hegde@Sun.COM 	 */
110711600SVikram.Hegde@Sun.COM 	if (immu_dmar_parse() != DDI_SUCCESS) {
110811600SVikram.Hegde@Sun.COM 		blacklist_destroy();
110911600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
111011600SVikram.Hegde@Sun.COM 		return;
111111600SVikram.Hegde@Sun.COM 	}
111211600SVikram.Hegde@Sun.COM 
111311600SVikram.Hegde@Sun.COM 	/*
111411600SVikram.Hegde@Sun.COM 	 * now that we have processed the ACPI table
111511600SVikram.Hegde@Sun.COM 	 * check if we need to blacklist this system
111611600SVikram.Hegde@Sun.COM 	 * based on ACPI info
111711600SVikram.Hegde@Sun.COM 	 */
111811600SVikram.Hegde@Sun.COM 	if (blacklisted_acpi() == B_TRUE) {
111911600SVikram.Hegde@Sun.COM 		immu_dmar_destroy();
112011600SVikram.Hegde@Sun.COM 		blacklist_destroy();
112111600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
112211600SVikram.Hegde@Sun.COM 		return;
112311600SVikram.Hegde@Sun.COM 	}
112411600SVikram.Hegde@Sun.COM 
112511600SVikram.Hegde@Sun.COM 	blacklist_destroy();
112611600SVikram.Hegde@Sun.COM 
112711600SVikram.Hegde@Sun.COM 	/*
112811600SVikram.Hegde@Sun.COM 	 * Check if system has HW quirks.
112911600SVikram.Hegde@Sun.COM 	 */
113011600SVikram.Hegde@Sun.COM 	pre_setup_quirks();
113111600SVikram.Hegde@Sun.COM 
113211600SVikram.Hegde@Sun.COM 	/* Now do the rest of the setup */
113311600SVikram.Hegde@Sun.COM 	immu_subsystems_setup();
113411600SVikram.Hegde@Sun.COM 
113511600SVikram.Hegde@Sun.COM 	/*
113611600SVikram.Hegde@Sun.COM 	 * Now that the IMMU is setup, create a phony
113711600SVikram.Hegde@Sun.COM 	 * reg prop so that suspend/resume works
113811600SVikram.Hegde@Sun.COM 	 */
113911600SVikram.Hegde@Sun.COM 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg",
114011600SVikram.Hegde@Sun.COM 	    (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) {
114111600SVikram.Hegde@Sun.COM 		ddi_err(DER_PANIC, NULL, "Failed to create reg prop for "
114211600SVikram.Hegde@Sun.COM 		    "rootnex node");
114311600SVikram.Hegde@Sun.COM 		/*NOTREACHED*/
114411600SVikram.Hegde@Sun.COM 	}
114511600SVikram.Hegde@Sun.COM 
114611600SVikram.Hegde@Sun.COM 	immu_setup = B_TRUE;
114711600SVikram.Hegde@Sun.COM }
114811600SVikram.Hegde@Sun.COM 
114911600SVikram.Hegde@Sun.COM /*
115011600SVikram.Hegde@Sun.COM  * immu_startup()
115111600SVikram.Hegde@Sun.COM  * 	called directly by boot code to startup
115211600SVikram.Hegde@Sun.COM  *      all units of the IOMMU
115311600SVikram.Hegde@Sun.COM  */
115411600SVikram.Hegde@Sun.COM void
immu_startup(void)115511600SVikram.Hegde@Sun.COM immu_startup(void)
115611600SVikram.Hegde@Sun.COM {
115711600SVikram.Hegde@Sun.COM 	/*
115811600SVikram.Hegde@Sun.COM 	 * If IOMMU is disabled, do nothing
115911600SVikram.Hegde@Sun.COM 	 */
116011600SVikram.Hegde@Sun.COM 	if (immu_enable == B_FALSE) {
116111600SVikram.Hegde@Sun.COM 		return;
116211600SVikram.Hegde@Sun.COM 	}
116311600SVikram.Hegde@Sun.COM 
116411600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
116511600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, "
116613050Sfrank.van.der.linden@oracle.com 		    "skipping IOMMU startup");
116711600SVikram.Hegde@Sun.COM 		return;
116811600SVikram.Hegde@Sun.COM 	}
116911600SVikram.Hegde@Sun.COM 
117011600SVikram.Hegde@Sun.COM 	pre_startup_quirks();
117111600SVikram.Hegde@Sun.COM 
117211600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL,
117311600SVikram.Hegde@Sun.COM 	    "?Starting Intel IOMMU (dmar) units...\n");
117411600SVikram.Hegde@Sun.COM 
117511600SVikram.Hegde@Sun.COM 	immu_subsystems_startup();
117611600SVikram.Hegde@Sun.COM 
117711600SVikram.Hegde@Sun.COM 	immu_running = B_TRUE;
117811600SVikram.Hegde@Sun.COM }
117911600SVikram.Hegde@Sun.COM 
118011600SVikram.Hegde@Sun.COM /*
118111600SVikram.Hegde@Sun.COM  * Hook to notify IOMMU code of device tree changes
118211600SVikram.Hegde@Sun.COM  */
118311600SVikram.Hegde@Sun.COM void
immu_device_tree_changed(void)118411600SVikram.Hegde@Sun.COM immu_device_tree_changed(void)
118511600SVikram.Hegde@Sun.COM {
118611600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
118711600SVikram.Hegde@Sun.COM 		return;
118811600SVikram.Hegde@Sun.COM 	}
118911600SVikram.Hegde@Sun.COM 
119011600SVikram.Hegde@Sun.COM 	ddi_err(DER_WARN, NULL, "Intel IOMMU currently "
119111600SVikram.Hegde@Sun.COM 	    "does not use device tree updates");
119211600SVikram.Hegde@Sun.COM }
119311600SVikram.Hegde@Sun.COM 
119411600SVikram.Hegde@Sun.COM /*
119511600SVikram.Hegde@Sun.COM  * Hook to notify IOMMU code of memory changes
119611600SVikram.Hegde@Sun.COM  */
119711600SVikram.Hegde@Sun.COM void
immu_physmem_update(uint64_t addr,uint64_t size)119811600SVikram.Hegde@Sun.COM immu_physmem_update(uint64_t addr, uint64_t size)
119911600SVikram.Hegde@Sun.COM {
120011600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
120111600SVikram.Hegde@Sun.COM 		return;
120211600SVikram.Hegde@Sun.COM 	}
120311600SVikram.Hegde@Sun.COM 	immu_dvma_physmem_update(addr, size);
120411600SVikram.Hegde@Sun.COM }
120511600SVikram.Hegde@Sun.COM 
120611600SVikram.Hegde@Sun.COM /*
120711600SVikram.Hegde@Sun.COM  * immu_quiesce()
120811600SVikram.Hegde@Sun.COM  * 	quiesce all units that are running
120911600SVikram.Hegde@Sun.COM  */
121011600SVikram.Hegde@Sun.COM int
immu_quiesce(void)121111600SVikram.Hegde@Sun.COM immu_quiesce(void)
121211600SVikram.Hegde@Sun.COM {
121311600SVikram.Hegde@Sun.COM 	immu_t *immu;
121411600SVikram.Hegde@Sun.COM 	int ret = DDI_SUCCESS;
121511600SVikram.Hegde@Sun.COM 
121611600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
121711600SVikram.Hegde@Sun.COM 
1218*13070SEthindra.Ramamurthy@Sun.COM 	if (immu_running == B_FALSE) {
1219*13070SEthindra.Ramamurthy@Sun.COM 		mutex_exit(&immu_lock);
122011600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
1221*13070SEthindra.Ramamurthy@Sun.COM 	}
122211600SVikram.Hegde@Sun.COM 
122311600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
122411600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
122511600SVikram.Hegde@Sun.COM 
122611600SVikram.Hegde@Sun.COM 		/* if immu is not running, we dont quiesce */
122711600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_FALSE)
122811600SVikram.Hegde@Sun.COM 			continue;
122911600SVikram.Hegde@Sun.COM 
123011600SVikram.Hegde@Sun.COM 		/* flush caches */
123111600SVikram.Hegde@Sun.COM 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
123213050Sfrank.van.der.linden@oracle.com 		immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait);
123313050Sfrank.van.der.linden@oracle.com 		immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait);
123411600SVikram.Hegde@Sun.COM 		rw_exit(&(immu->immu_ctx_rwlock));
123511600SVikram.Hegde@Sun.COM 		immu_regs_wbf_flush(immu);
123611600SVikram.Hegde@Sun.COM 
123711600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
123811600SVikram.Hegde@Sun.COM 
123911600SVikram.Hegde@Sun.COM 		/*
124011600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
124111600SVikram.Hegde@Sun.COM 		 * the actual shutdown.
124211600SVikram.Hegde@Sun.COM 		 */
124311600SVikram.Hegde@Sun.COM 		immu_regs_shutdown(immu);
124411600SVikram.Hegde@Sun.COM 		immu_regs_suspend(immu);
124511600SVikram.Hegde@Sun.COM 
124611600SVikram.Hegde@Sun.COM 		/* if immu is still running, we failed */
124711600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_TRUE)
124811600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
124911600SVikram.Hegde@Sun.COM 		else
125011600SVikram.Hegde@Sun.COM 			immu->immu_regs_quiesced = B_TRUE;
125111600SVikram.Hegde@Sun.COM 
125211600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
125311600SVikram.Hegde@Sun.COM 	}
125411600SVikram.Hegde@Sun.COM 
125511600SVikram.Hegde@Sun.COM 	if (ret == DDI_SUCCESS) {
125611600SVikram.Hegde@Sun.COM 		immu_running = B_FALSE;
125711600SVikram.Hegde@Sun.COM 		immu_quiesced = B_TRUE;
125811600SVikram.Hegde@Sun.COM 	}
1259*13070SEthindra.Ramamurthy@Sun.COM 	mutex_exit(&immu_lock);
126011600SVikram.Hegde@Sun.COM 
126111600SVikram.Hegde@Sun.COM 	return (ret);
126211600SVikram.Hegde@Sun.COM }
126311600SVikram.Hegde@Sun.COM 
126411600SVikram.Hegde@Sun.COM /*
126511600SVikram.Hegde@Sun.COM  * immu_unquiesce()
126611600SVikram.Hegde@Sun.COM  * 	unquiesce all units
126711600SVikram.Hegde@Sun.COM  */
126811600SVikram.Hegde@Sun.COM int
immu_unquiesce(void)126911600SVikram.Hegde@Sun.COM immu_unquiesce(void)
127011600SVikram.Hegde@Sun.COM {
127111600SVikram.Hegde@Sun.COM 	immu_t *immu;
127211600SVikram.Hegde@Sun.COM 	int ret = DDI_SUCCESS;
127311600SVikram.Hegde@Sun.COM 
127411600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
127511600SVikram.Hegde@Sun.COM 
1276*13070SEthindra.Ramamurthy@Sun.COM 	if (immu_quiesced == B_FALSE) {
1277*13070SEthindra.Ramamurthy@Sun.COM 		mutex_exit(&immu_lock);
127811600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
1279*13070SEthindra.Ramamurthy@Sun.COM 	}
128011600SVikram.Hegde@Sun.COM 
128111600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
128211600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
128311600SVikram.Hegde@Sun.COM 
128411600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
128511600SVikram.Hegde@Sun.COM 
128611600SVikram.Hegde@Sun.COM 		/* if immu was not quiesced, i.e was not running before */
128711658SVikram.Hegde@Sun.COM 		if (immu->immu_regs_quiesced == B_FALSE) {
128811658SVikram.Hegde@Sun.COM 			mutex_exit(&(immu->immu_lock));
128911600SVikram.Hegde@Sun.COM 			continue;
129011658SVikram.Hegde@Sun.COM 		}
129111600SVikram.Hegde@Sun.COM 
129211600SVikram.Hegde@Sun.COM 		if (immu_regs_resume(immu) != DDI_SUCCESS) {
129311600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
129411658SVikram.Hegde@Sun.COM 			mutex_exit(&(immu->immu_lock));
129511600SVikram.Hegde@Sun.COM 			continue;
129611600SVikram.Hegde@Sun.COM 		}
129711600SVikram.Hegde@Sun.COM 
129811600SVikram.Hegde@Sun.COM 		/* flush caches before unquiesce */
129911600SVikram.Hegde@Sun.COM 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
130013050Sfrank.van.der.linden@oracle.com 		immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait);
130113050Sfrank.van.der.linden@oracle.com 		immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait);
130211600SVikram.Hegde@Sun.COM 		rw_exit(&(immu->immu_ctx_rwlock));
130311600SVikram.Hegde@Sun.COM 
130411600SVikram.Hegde@Sun.COM 		/*
130511600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
130611600SVikram.Hegde@Sun.COM 		 * the actual startup. This will
130711600SVikram.Hegde@Sun.COM 		 * set immu->immu_regs_running  field
130811600SVikram.Hegde@Sun.COM 		 * if the unit is successfully
130911600SVikram.Hegde@Sun.COM 		 * started
131011600SVikram.Hegde@Sun.COM 		 */
131111600SVikram.Hegde@Sun.COM 		immu_regs_startup(immu);
131211600SVikram.Hegde@Sun.COM 
131311600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_FALSE) {
131411600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
131511600SVikram.Hegde@Sun.COM 		} else {
131611600SVikram.Hegde@Sun.COM 			immu_quiesced = B_TRUE;
131711600SVikram.Hegde@Sun.COM 			immu_running = B_TRUE;
131811600SVikram.Hegde@Sun.COM 			immu->immu_regs_quiesced = B_FALSE;
131911600SVikram.Hegde@Sun.COM 		}
132011600SVikram.Hegde@Sun.COM 
132111600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
132211600SVikram.Hegde@Sun.COM 	}
132311600SVikram.Hegde@Sun.COM 
132411600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
132511600SVikram.Hegde@Sun.COM 
132611600SVikram.Hegde@Sun.COM 	return (ret);
132711600SVikram.Hegde@Sun.COM }
132811600SVikram.Hegde@Sun.COM 
132913050Sfrank.van.der.linden@oracle.com void
immu_init_inv_wait(immu_inv_wait_t * iwp,const char * name,boolean_t sync)133013050Sfrank.van.der.linden@oracle.com immu_init_inv_wait(immu_inv_wait_t *iwp, const char *name, boolean_t sync)
133113050Sfrank.van.der.linden@oracle.com {
133213050Sfrank.van.der.linden@oracle.com 	caddr_t vaddr;
133313050Sfrank.van.der.linden@oracle.com 	uint64_t paddr;
133413050Sfrank.van.der.linden@oracle.com 
133513050Sfrank.van.der.linden@oracle.com 	iwp->iwp_sync = sync;
133613050Sfrank.van.der.linden@oracle.com 
133713050Sfrank.van.der.linden@oracle.com 	vaddr = (caddr_t)&iwp->iwp_vstatus;
133813050Sfrank.van.der.linden@oracle.com 	paddr = pfn_to_pa(hat_getpfnum(kas.a_hat, vaddr));
133913050Sfrank.van.der.linden@oracle.com 	paddr += ((uintptr_t)vaddr) & MMU_PAGEOFFSET;
134013050Sfrank.van.der.linden@oracle.com 
134113050Sfrank.van.der.linden@oracle.com 	iwp->iwp_pstatus = paddr;
134213050Sfrank.van.der.linden@oracle.com 	iwp->iwp_name = name;
134313050Sfrank.van.der.linden@oracle.com }
134413050Sfrank.van.der.linden@oracle.com 
134511600SVikram.Hegde@Sun.COM /* ##############  END Intel IOMMU entry points ################## */
1346