xref: /onnv-gate/usr/src/uts/i86pc/io/immu.c (revision 11749:74c8afe8dc9c)
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 /*
2211600SVikram.Hegde@Sun.COM  * Portions Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2311600SVikram.Hegde@Sun.COM  * Use is subject to license terms.
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/rootnex.h>
5711600SVikram.Hegde@Sun.COM #include <sys/avl.h>
5811600SVikram.Hegde@Sun.COM #include <sys/bootconf.h>
5911600SVikram.Hegde@Sun.COM #include <sys/bootinfo.h>
6011600SVikram.Hegde@Sun.COM #include <sys/atomic.h>
6111600SVikram.Hegde@Sun.COM #include <sys/immu.h>
6211600SVikram.Hegde@Sun.COM /* ########################### Globals and tunables ######################## */
6311600SVikram.Hegde@Sun.COM /*
6411600SVikram.Hegde@Sun.COM  * Global switches (boolean) that can be toggled either via boot options
6511600SVikram.Hegde@Sun.COM  * or via /etc/system or kmdb
6611600SVikram.Hegde@Sun.COM  */
6711600SVikram.Hegde@Sun.COM 
6811600SVikram.Hegde@Sun.COM /* Various features */
69*11749SVikram.Hegde@Sun.COM boolean_t immu_enable = B_TRUE;
7011600SVikram.Hegde@Sun.COM boolean_t immu_dvma_enable = B_TRUE;
7111600SVikram.Hegde@Sun.COM 
7211600SVikram.Hegde@Sun.COM /* accessed in other files so not static */
7311600SVikram.Hegde@Sun.COM boolean_t immu_gfxdvma_enable = B_TRUE;
7411600SVikram.Hegde@Sun.COM boolean_t immu_intrmap_enable = B_FALSE;
7511600SVikram.Hegde@Sun.COM boolean_t immu_qinv_enable = B_FALSE;
7611600SVikram.Hegde@Sun.COM 
7711600SVikram.Hegde@Sun.COM /* various quirks that need working around */
7811600SVikram.Hegde@Sun.COM 
7911600SVikram.Hegde@Sun.COM /* XXX We always map page 0 read/write for now */
8011600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbpage0 = B_TRUE;
8111600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbrmrr = B_TRUE;
8211600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbfullpa;
8311600SVikram.Hegde@Sun.COM boolean_t immu_quirk_mobile4;
8411600SVikram.Hegde@Sun.COM 
8511600SVikram.Hegde@Sun.COM /* debug messages */
8611600SVikram.Hegde@Sun.COM boolean_t immu_dmar_print;
8711600SVikram.Hegde@Sun.COM 
8811658SVikram.Hegde@Sun.COM /* Tunables */
8911658SVikram.Hegde@Sun.COM int64_t immu_flush_gran = 5;
9011658SVikram.Hegde@Sun.COM 
9111600SVikram.Hegde@Sun.COM /* ############  END OPTIONS section ################ */
9211600SVikram.Hegde@Sun.COM 
9311600SVikram.Hegde@Sun.COM /*
9411600SVikram.Hegde@Sun.COM  * Global used internally by Intel IOMMU code
9511600SVikram.Hegde@Sun.COM  */
9611600SVikram.Hegde@Sun.COM dev_info_t *root_devinfo;
9711600SVikram.Hegde@Sun.COM kmutex_t immu_lock;
9811600SVikram.Hegde@Sun.COM list_t immu_list;
9911658SVikram.Hegde@Sun.COM void *immu_pgtable_cache;
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;
10811600SVikram.Hegde@Sun.COM /* ###################### Utility routines ############################# */
10911600SVikram.Hegde@Sun.COM 
11011600SVikram.Hegde@Sun.COM /*
11111600SVikram.Hegde@Sun.COM  * Check if the device has mobile 4 chipset
11211600SVikram.Hegde@Sun.COM  */
11311600SVikram.Hegde@Sun.COM static int
11411600SVikram.Hegde@Sun.COM check_mobile4(dev_info_t *dip, void *arg)
11511600SVikram.Hegde@Sun.COM {
11611600SVikram.Hegde@Sun.COM 	_NOTE(ARGUNUSED(arg));
11711600SVikram.Hegde@Sun.COM 	int vendor, device;
11811600SVikram.Hegde@Sun.COM 	int *ip = (int *)arg;
11911600SVikram.Hegde@Sun.COM 
12011600SVikram.Hegde@Sun.COM 	ASSERT(arg);
12111600SVikram.Hegde@Sun.COM 
12211600SVikram.Hegde@Sun.COM 	vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
12311600SVikram.Hegde@Sun.COM 	    "vendor-id", -1);
12411600SVikram.Hegde@Sun.COM 	device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
12511600SVikram.Hegde@Sun.COM 	    "device-id", -1);
12611600SVikram.Hegde@Sun.COM 
12711600SVikram.Hegde@Sun.COM 	if (vendor == 0x8086 && device == 0x2a40) {
12811600SVikram.Hegde@Sun.COM 		*ip = B_TRUE;
12911600SVikram.Hegde@Sun.COM 		ddi_err(DER_NOTE, dip, "IMMU: Mobile 4 chipset detected. "
13011600SVikram.Hegde@Sun.COM 		    "Force setting IOMMU write buffer");
13111600SVikram.Hegde@Sun.COM 		return (DDI_WALK_TERMINATE);
13211600SVikram.Hegde@Sun.COM 	} else {
13311600SVikram.Hegde@Sun.COM 		return (DDI_WALK_CONTINUE);
13411600SVikram.Hegde@Sun.COM 	}
13511600SVikram.Hegde@Sun.COM }
13611600SVikram.Hegde@Sun.COM 
13711600SVikram.Hegde@Sun.COM static void
13811600SVikram.Hegde@Sun.COM map_bios_rsvd_mem(dev_info_t *dip)
13911600SVikram.Hegde@Sun.COM {
14011600SVikram.Hegde@Sun.COM 	struct memlist *mp;
14111600SVikram.Hegde@Sun.COM 	int e;
14211600SVikram.Hegde@Sun.COM 
14311600SVikram.Hegde@Sun.COM 	memlist_read_lock();
14411600SVikram.Hegde@Sun.COM 
14511600SVikram.Hegde@Sun.COM 	mp = bios_rsvd;
14611600SVikram.Hegde@Sun.COM 	while (mp != NULL) {
14711658SVikram.Hegde@Sun.COM 		memrng_t mrng = {0};
14811600SVikram.Hegde@Sun.COM 
14911600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "IMMU: Mapping BIOS rsvd range "
15011600SVikram.Hegde@Sun.COM 		    "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address,
15111600SVikram.Hegde@Sun.COM 		    mp->ml_address + mp->ml_size);
15211600SVikram.Hegde@Sun.COM 
15311658SVikram.Hegde@Sun.COM 		mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address);
15411658SVikram.Hegde@Sun.COM 		mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE;
15511600SVikram.Hegde@Sun.COM 
15611658SVikram.Hegde@Sun.COM 		e = immu_dvma_map(NULL, NULL, &mrng, 0, dip, IMMU_FLAGS_MEMRNG);
15711600SVikram.Hegde@Sun.COM 		ASSERT(e == DDI_DMA_MAPPED || e == DDI_DMA_USE_PHYSICAL);
15811600SVikram.Hegde@Sun.COM 
15911600SVikram.Hegde@Sun.COM 		mp = mp->ml_next;
16011600SVikram.Hegde@Sun.COM 	}
16111600SVikram.Hegde@Sun.COM 
16211600SVikram.Hegde@Sun.COM 	memlist_read_unlock();
16311600SVikram.Hegde@Sun.COM }
16411600SVikram.Hegde@Sun.COM 
16511658SVikram.Hegde@Sun.COM 
16611658SVikram.Hegde@Sun.COM /*
16711658SVikram.Hegde@Sun.COM  * Check if the driver requests physical mapping
16811658SVikram.Hegde@Sun.COM  */
16911658SVikram.Hegde@Sun.COM /*ARGSUSED*/
17011658SVikram.Hegde@Sun.COM static void
17111658SVikram.Hegde@Sun.COM check_physical(dev_info_t *dip, void *arg)
17211658SVikram.Hegde@Sun.COM {
17311658SVikram.Hegde@Sun.COM 	char *val;
17411658SVikram.Hegde@Sun.COM 
17511658SVikram.Hegde@Sun.COM 	/*
17611658SVikram.Hegde@Sun.COM 	 * Check for the DVMA unity mapping property on the device
17711658SVikram.Hegde@Sun.COM 	 */
17811658SVikram.Hegde@Sun.COM 	val = NULL;
17911658SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
18011658SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS, DDI_DVMA_MAPTYPE_PROP, &val) == DDI_SUCCESS) {
18111658SVikram.Hegde@Sun.COM 		ASSERT(val);
18211658SVikram.Hegde@Sun.COM 		if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) != 0) {
18311658SVikram.Hegde@Sun.COM 			ddi_err(DER_WARN, dip, "%s value \"%s\" is not valid",
18411658SVikram.Hegde@Sun.COM 			    DDI_DVMA_MAPTYPE_PROP, val);
18511658SVikram.Hegde@Sun.COM 		} else {
18611658SVikram.Hegde@Sun.COM 			int e;
18711658SVikram.Hegde@Sun.COM 
18811658SVikram.Hegde@Sun.COM 			ddi_err(DER_NOTE, dip,
18911658SVikram.Hegde@Sun.COM 			    "Using unity DVMA mapping for device");
19011658SVikram.Hegde@Sun.COM 			e = immu_dvma_map(NULL, NULL, NULL, 0, dip,
19111658SVikram.Hegde@Sun.COM 			    IMMU_FLAGS_UNITY);
19211658SVikram.Hegde@Sun.COM 			/* for unity mode, map will return USE_PHYSICAL */
19311658SVikram.Hegde@Sun.COM 			ASSERT(e == DDI_DMA_USE_PHYSICAL);
19411658SVikram.Hegde@Sun.COM 		}
19511658SVikram.Hegde@Sun.COM 		ddi_prop_free(val);
19611658SVikram.Hegde@Sun.COM 	}
19711658SVikram.Hegde@Sun.COM }
19811658SVikram.Hegde@Sun.COM 
19911600SVikram.Hegde@Sun.COM /*
20011600SVikram.Hegde@Sun.COM  * Check if the device is USB controller
20111600SVikram.Hegde@Sun.COM  */
20211600SVikram.Hegde@Sun.COM /*ARGSUSED*/
20311600SVikram.Hegde@Sun.COM static void
20411600SVikram.Hegde@Sun.COM check_usb(dev_info_t *dip, void *arg)
20511600SVikram.Hegde@Sun.COM {
20611600SVikram.Hegde@Sun.COM 	const char *drv = ddi_driver_name(dip);
20711600SVikram.Hegde@Sun.COM 
20811600SVikram.Hegde@Sun.COM 	if (drv == NULL ||
20911600SVikram.Hegde@Sun.COM 	    (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 &&
21011600SVikram.Hegde@Sun.COM 	    strcmp(drv, "ehci") != 0)) {
21111600SVikram.Hegde@Sun.COM 		return;
21211600SVikram.Hegde@Sun.COM 	}
21311600SVikram.Hegde@Sun.COM 
21411600SVikram.Hegde@Sun.COM 	/* This must come first since it does unity mapping */
21511600SVikram.Hegde@Sun.COM 	if (immu_quirk_usbfullpa == B_TRUE) {
21611600SVikram.Hegde@Sun.COM 		int e;
21711600SVikram.Hegde@Sun.COM 		ddi_err(DER_NOTE, dip, "Applying USB FULL PA quirk");
21811600SVikram.Hegde@Sun.COM 		e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY);
21911600SVikram.Hegde@Sun.COM 		/* for unity mode, map will return USE_PHYSICAL */
22011600SVikram.Hegde@Sun.COM 		ASSERT(e == DDI_DMA_USE_PHYSICAL);
22111600SVikram.Hegde@Sun.COM 	}
22211600SVikram.Hegde@Sun.COM 
22311600SVikram.Hegde@Sun.COM 	if (immu_quirk_usbrmrr == B_TRUE) {
22411600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "Applying USB RMRR quirk");
22511600SVikram.Hegde@Sun.COM 		map_bios_rsvd_mem(dip);
22611600SVikram.Hegde@Sun.COM 	}
22711600SVikram.Hegde@Sun.COM }
22811600SVikram.Hegde@Sun.COM 
22911600SVikram.Hegde@Sun.COM /*
23011600SVikram.Hegde@Sun.COM  * Check if the device is a LPC device
23111600SVikram.Hegde@Sun.COM  */
23211600SVikram.Hegde@Sun.COM /*ARGSUSED*/
23311600SVikram.Hegde@Sun.COM static void
23411600SVikram.Hegde@Sun.COM check_lpc(dev_info_t *dip, void *arg)
23511600SVikram.Hegde@Sun.COM {
23611600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
23711600SVikram.Hegde@Sun.COM 
23811600SVikram.Hegde@Sun.COM 	immu_devi = immu_devi_get(dip);
23911600SVikram.Hegde@Sun.COM 	ASSERT(immu_devi);
24011600SVikram.Hegde@Sun.COM 	if (immu_devi->imd_lpc == B_TRUE) {
24111600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "IMMU: Found LPC device");
24211600SVikram.Hegde@Sun.COM 		/* This will put the immu_devi on the LPC "specials" list */
24311600SVikram.Hegde@Sun.COM 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
24411600SVikram.Hegde@Sun.COM 	}
24511600SVikram.Hegde@Sun.COM }
24611600SVikram.Hegde@Sun.COM 
24711600SVikram.Hegde@Sun.COM /*
24811600SVikram.Hegde@Sun.COM  * Check if the device is a GFX device
24911600SVikram.Hegde@Sun.COM  */
25011600SVikram.Hegde@Sun.COM /*ARGSUSED*/
25111600SVikram.Hegde@Sun.COM static void
25211600SVikram.Hegde@Sun.COM check_gfx(dev_info_t *dip, void *arg)
25311600SVikram.Hegde@Sun.COM {
25411600SVikram.Hegde@Sun.COM 	immu_devi_t *immu_devi;
25511600SVikram.Hegde@Sun.COM 	int e;
25611600SVikram.Hegde@Sun.COM 
25711600SVikram.Hegde@Sun.COM 	immu_devi = immu_devi_get(dip);
25811600SVikram.Hegde@Sun.COM 	ASSERT(immu_devi);
25911600SVikram.Hegde@Sun.COM 	if (immu_devi->imd_display == B_TRUE) {
26011600SVikram.Hegde@Sun.COM 		ddi_err(DER_LOG, dip, "IMMU: Found GFX device");
26111600SVikram.Hegde@Sun.COM 		/* This will put the immu_devi on the GFX "specials" list */
26211600SVikram.Hegde@Sun.COM 		(void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP);
26311600SVikram.Hegde@Sun.COM 		e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY);
26411600SVikram.Hegde@Sun.COM 		/* for unity mode, map will return USE_PHYSICAL */
26511600SVikram.Hegde@Sun.COM 		ASSERT(e == DDI_DMA_USE_PHYSICAL);
26611600SVikram.Hegde@Sun.COM 	}
26711600SVikram.Hegde@Sun.COM }
26811600SVikram.Hegde@Sun.COM 
26911600SVikram.Hegde@Sun.COM static void
27011600SVikram.Hegde@Sun.COM walk_tree(int (*f)(dev_info_t *, void *), void *arg)
27111600SVikram.Hegde@Sun.COM {
27211600SVikram.Hegde@Sun.COM 	int count;
27311600SVikram.Hegde@Sun.COM 
27411600SVikram.Hegde@Sun.COM 	ndi_devi_enter(root_devinfo, &count);
27511600SVikram.Hegde@Sun.COM 	ddi_walk_devs(ddi_get_child(root_devinfo), f, arg);
27611600SVikram.Hegde@Sun.COM 	ndi_devi_exit(root_devinfo, count);
27711600SVikram.Hegde@Sun.COM }
27811600SVikram.Hegde@Sun.COM 
27911600SVikram.Hegde@Sun.COM static int
28011600SVikram.Hegde@Sun.COM check_pre_setup_quirks(dev_info_t *dip, void *arg)
28111600SVikram.Hegde@Sun.COM {
28211600SVikram.Hegde@Sun.COM 	/* just 1 check right now */
28311600SVikram.Hegde@Sun.COM 	return (check_mobile4(dip, arg));
28411600SVikram.Hegde@Sun.COM }
28511600SVikram.Hegde@Sun.COM 
28611600SVikram.Hegde@Sun.COM static int
28711600SVikram.Hegde@Sun.COM check_pre_startup_quirks(dev_info_t *dip, void *arg)
28811600SVikram.Hegde@Sun.COM {
28911600SVikram.Hegde@Sun.COM 	if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) {
29011600SVikram.Hegde@Sun.COM 		ddi_err(DER_PANIC, dip, "Failed to get immu_devi");
29111600SVikram.Hegde@Sun.COM 	}
29211600SVikram.Hegde@Sun.COM 
29311600SVikram.Hegde@Sun.COM 	check_gfx(dip, arg);
29411600SVikram.Hegde@Sun.COM 
29511600SVikram.Hegde@Sun.COM 	check_lpc(dip, arg);
29611600SVikram.Hegde@Sun.COM 
29711600SVikram.Hegde@Sun.COM 	check_usb(dip, arg);
29811600SVikram.Hegde@Sun.COM 
29911658SVikram.Hegde@Sun.COM 	check_physical(dip, arg);
30011658SVikram.Hegde@Sun.COM 
30111600SVikram.Hegde@Sun.COM 	return (DDI_WALK_CONTINUE);
30211600SVikram.Hegde@Sun.COM }
30311600SVikram.Hegde@Sun.COM 
30411600SVikram.Hegde@Sun.COM static void
30511600SVikram.Hegde@Sun.COM pre_setup_quirks(void)
30611600SVikram.Hegde@Sun.COM {
30711600SVikram.Hegde@Sun.COM 	walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4);
30811600SVikram.Hegde@Sun.COM }
30911600SVikram.Hegde@Sun.COM 
31011600SVikram.Hegde@Sun.COM static void
31111600SVikram.Hegde@Sun.COM pre_startup_quirks(void)
31211600SVikram.Hegde@Sun.COM {
31311600SVikram.Hegde@Sun.COM 	walk_tree(check_pre_startup_quirks, NULL);
31411600SVikram.Hegde@Sun.COM 
31511600SVikram.Hegde@Sun.COM 	immu_dmar_rmrr_map();
31611600SVikram.Hegde@Sun.COM }
31711600SVikram.Hegde@Sun.COM 
31811600SVikram.Hegde@Sun.COM /*
319*11749SVikram.Hegde@Sun.COM  * get_conf_opt()
320*11749SVikram.Hegde@Sun.COM  * 	get a rootnex.conf setting  (always a boolean)
321*11749SVikram.Hegde@Sun.COM  */
322*11749SVikram.Hegde@Sun.COM static void
323*11749SVikram.Hegde@Sun.COM get_conf_opt(char *bopt, boolean_t *kvar)
324*11749SVikram.Hegde@Sun.COM {
325*11749SVikram.Hegde@Sun.COM 	char *val = NULL;
326*11749SVikram.Hegde@Sun.COM 
327*11749SVikram.Hegde@Sun.COM 	ASSERT(bopt);
328*11749SVikram.Hegde@Sun.COM 	ASSERT(kvar);
329*11749SVikram.Hegde@Sun.COM 
330*11749SVikram.Hegde@Sun.COM 	/*
331*11749SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf property
332*11749SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
333*11749SVikram.Hegde@Sun.COM 	 * property list needs it
334*11749SVikram.Hegde@Sun.COM 	 */
335*11749SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(makedevice(ddi_name_to_major("rootnex"), 0),
336*11749SVikram.Hegde@Sun.COM 	    root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL,
337*11749SVikram.Hegde@Sun.COM 	    bopt, &val) != DDI_PROP_SUCCESS) {
338*11749SVikram.Hegde@Sun.COM 		return;
339*11749SVikram.Hegde@Sun.COM 	}
340*11749SVikram.Hegde@Sun.COM 
341*11749SVikram.Hegde@Sun.COM 	ASSERT(val);
342*11749SVikram.Hegde@Sun.COM 	if (strcmp(val, "true") == 0) {
343*11749SVikram.Hegde@Sun.COM 		*kvar = B_TRUE;
344*11749SVikram.Hegde@Sun.COM 	} else if (strcmp(val, "false") == 0) {
345*11749SVikram.Hegde@Sun.COM 		*kvar = B_FALSE;
346*11749SVikram.Hegde@Sun.COM 	} else {
347*11749SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ",
348*11749SVikram.Hegde@Sun.COM 		    "is not set to true or false. Ignoring option.",
349*11749SVikram.Hegde@Sun.COM 		    bopt, val);
350*11749SVikram.Hegde@Sun.COM 	}
351*11749SVikram.Hegde@Sun.COM 	ddi_prop_free(val);
352*11749SVikram.Hegde@Sun.COM }
353*11749SVikram.Hegde@Sun.COM 
354*11749SVikram.Hegde@Sun.COM /*
35511600SVikram.Hegde@Sun.COM  * get_bootopt()
35611600SVikram.Hegde@Sun.COM  * 	check a boot option  (always a boolean)
35711600SVikram.Hegde@Sun.COM  */
35811600SVikram.Hegde@Sun.COM static void
35911600SVikram.Hegde@Sun.COM get_bootopt(char *bopt, boolean_t *kvar)
36011600SVikram.Hegde@Sun.COM {
36111600SVikram.Hegde@Sun.COM 	char *val = NULL;
36211600SVikram.Hegde@Sun.COM 
36311600SVikram.Hegde@Sun.COM 	ASSERT(bopt);
36411600SVikram.Hegde@Sun.COM 	ASSERT(kvar);
36511600SVikram.Hegde@Sun.COM 
36611600SVikram.Hegde@Sun.COM 	/*
36711600SVikram.Hegde@Sun.COM 	 * All boot options set at the GRUB menu become
36811600SVikram.Hegde@Sun.COM 	 * properties on the rootnex.
36911600SVikram.Hegde@Sun.COM 	 */
37011600SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo,
37111600SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS, bopt, &val) == DDI_SUCCESS) {
37211600SVikram.Hegde@Sun.COM 		ASSERT(val);
37311600SVikram.Hegde@Sun.COM 		if (strcmp(val, "true") == 0) {
37411600SVikram.Hegde@Sun.COM 			*kvar = B_TRUE;
37511600SVikram.Hegde@Sun.COM 		} else if (strcmp(val, "false") == 0) {
37611600SVikram.Hegde@Sun.COM 			*kvar = B_FALSE;
37711600SVikram.Hegde@Sun.COM 		} else {
37811600SVikram.Hegde@Sun.COM 			ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ",
37911600SVikram.Hegde@Sun.COM 			    "is not set to true or false. Ignoring option.",
38011600SVikram.Hegde@Sun.COM 			    bopt, val);
38111600SVikram.Hegde@Sun.COM 		}
38211600SVikram.Hegde@Sun.COM 		ddi_prop_free(val);
38311600SVikram.Hegde@Sun.COM 	}
38411600SVikram.Hegde@Sun.COM }
38511600SVikram.Hegde@Sun.COM 
38611600SVikram.Hegde@Sun.COM static void
387*11749SVikram.Hegde@Sun.COM get_conf_tunables(char *bopt, int64_t *ivar)
38811658SVikram.Hegde@Sun.COM {
38911658SVikram.Hegde@Sun.COM 	int64_t	*iarray;
39011658SVikram.Hegde@Sun.COM 	uint_t n;
39111658SVikram.Hegde@Sun.COM 
39211658SVikram.Hegde@Sun.COM 	/*
39311658SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf property
39411658SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
39511658SVikram.Hegde@Sun.COM 	 * property list needs it
39611658SVikram.Hegde@Sun.COM 	 */
39711658SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_int64_array(
39811658SVikram.Hegde@Sun.COM 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
39911658SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt,
40011658SVikram.Hegde@Sun.COM 	    &iarray, &n) != DDI_PROP_SUCCESS) {
40111658SVikram.Hegde@Sun.COM 		return;
40211658SVikram.Hegde@Sun.COM 	}
40311658SVikram.Hegde@Sun.COM 
40411658SVikram.Hegde@Sun.COM 	if (n != 1) {
40511658SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "More than one value specified for "
40611658SVikram.Hegde@Sun.COM 		    "%s property. Ignoring and using default",
40711658SVikram.Hegde@Sun.COM 		    "immu-flush-gran");
40811658SVikram.Hegde@Sun.COM 		ddi_prop_free(iarray);
40911658SVikram.Hegde@Sun.COM 		return;
41011658SVikram.Hegde@Sun.COM 	}
41111658SVikram.Hegde@Sun.COM 
41211658SVikram.Hegde@Sun.COM 	if (iarray[0] < 0) {
41311658SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Negative value specified for "
41411658SVikram.Hegde@Sun.COM 		    "%s property. Inoring and Using default value",
41511658SVikram.Hegde@Sun.COM 		    "immu-flush-gran");
41611658SVikram.Hegde@Sun.COM 		ddi_prop_free(iarray);
41711658SVikram.Hegde@Sun.COM 		return;
41811658SVikram.Hegde@Sun.COM 	}
41911658SVikram.Hegde@Sun.COM 
42011658SVikram.Hegde@Sun.COM 	*ivar = iarray[0];
42111658SVikram.Hegde@Sun.COM 
42211658SVikram.Hegde@Sun.COM 	ddi_prop_free(iarray);
42311658SVikram.Hegde@Sun.COM }
42411658SVikram.Hegde@Sun.COM 
42511658SVikram.Hegde@Sun.COM static void
426*11749SVikram.Hegde@Sun.COM read_conf_options(void)
427*11749SVikram.Hegde@Sun.COM {
428*11749SVikram.Hegde@Sun.COM 	/* enable/disable options */
429*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-enable", &immu_enable);
430*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-dvma-enable", &immu_dvma_enable);
431*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
432*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable);
433*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-qinv-enable", &immu_qinv_enable);
434*11749SVikram.Hegde@Sun.COM 
435*11749SVikram.Hegde@Sun.COM 	/* workaround switches */
436*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
437*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
438*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
439*11749SVikram.Hegde@Sun.COM 
440*11749SVikram.Hegde@Sun.COM 	/* debug printing */
441*11749SVikram.Hegde@Sun.COM 	get_conf_opt("immu-dmar-print", &immu_dmar_print);
442*11749SVikram.Hegde@Sun.COM 
443*11749SVikram.Hegde@Sun.COM 	/* get tunables */
444*11749SVikram.Hegde@Sun.COM 	get_conf_tunables("immu-flush-gran", &immu_flush_gran);
445*11749SVikram.Hegde@Sun.COM }
446*11749SVikram.Hegde@Sun.COM 
447*11749SVikram.Hegde@Sun.COM static void
44811600SVikram.Hegde@Sun.COM read_boot_options(void)
44911600SVikram.Hegde@Sun.COM {
45011600SVikram.Hegde@Sun.COM 	/* enable/disable options */
45111600SVikram.Hegde@Sun.COM 	get_bootopt("immu-enable", &immu_enable);
45211600SVikram.Hegde@Sun.COM 	get_bootopt("immu-dvma-enable", &immu_dvma_enable);
45311600SVikram.Hegde@Sun.COM 	get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable);
45411600SVikram.Hegde@Sun.COM 	get_bootopt("immu-intrmap-enable", &immu_intrmap_enable);
45511600SVikram.Hegde@Sun.COM 	get_bootopt("immu-qinv-enable", &immu_qinv_enable);
45611600SVikram.Hegde@Sun.COM 
45711600SVikram.Hegde@Sun.COM 	/* workaround switches */
45811600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0);
45911600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa);
46011600SVikram.Hegde@Sun.COM 	get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr);
46111600SVikram.Hegde@Sun.COM 
46211600SVikram.Hegde@Sun.COM 	/* debug printing */
46311600SVikram.Hegde@Sun.COM 	get_bootopt("immu-dmar-print", &immu_dmar_print);
46411600SVikram.Hegde@Sun.COM }
46511600SVikram.Hegde@Sun.COM 
46611600SVikram.Hegde@Sun.COM /*
46711600SVikram.Hegde@Sun.COM  * Note, this will not catch hardware not enumerated
46811600SVikram.Hegde@Sun.COM  * in early boot
46911600SVikram.Hegde@Sun.COM  */
47011600SVikram.Hegde@Sun.COM static boolean_t
47111600SVikram.Hegde@Sun.COM blacklisted_driver(void)
47211600SVikram.Hegde@Sun.COM {
47311600SVikram.Hegde@Sun.COM 	char **strptr;
47411600SVikram.Hegde@Sun.COM 	int i;
47511600SVikram.Hegde@Sun.COM 	major_t maj;
47611600SVikram.Hegde@Sun.COM 
47711600SVikram.Hegde@Sun.COM 	ASSERT((black_array == NULL) ^ (nblacks != 0));
47811600SVikram.Hegde@Sun.COM 
47911600SVikram.Hegde@Sun.COM 	/* need at least 2 strings */
48011600SVikram.Hegde@Sun.COM 	if (nblacks < 2) {
48111600SVikram.Hegde@Sun.COM 		return (B_FALSE);
48211600SVikram.Hegde@Sun.COM 	}
48311600SVikram.Hegde@Sun.COM 
48411600SVikram.Hegde@Sun.COM 	for (i = 0; nblacks - i > 1; i++) {
48511658SVikram.Hegde@Sun.COM 		strptr = &black_array[i];
48611600SVikram.Hegde@Sun.COM 		if (strcmp(*strptr++, "DRIVER") == 0) {
48711600SVikram.Hegde@Sun.COM 			if ((maj = ddi_name_to_major(*strptr++))
48811600SVikram.Hegde@Sun.COM 			    != DDI_MAJOR_T_NONE) {
48911600SVikram.Hegde@Sun.COM 				/* is there hardware bound to this drvr */
49011600SVikram.Hegde@Sun.COM 				if (devnamesp[maj].dn_head != NULL) {
49111600SVikram.Hegde@Sun.COM 					return (B_TRUE);
49211600SVikram.Hegde@Sun.COM 				}
49311600SVikram.Hegde@Sun.COM 			}
49411600SVikram.Hegde@Sun.COM 			i += 1;   /* for loop adds 1, so add only 1 here */
49511600SVikram.Hegde@Sun.COM 		}
49611600SVikram.Hegde@Sun.COM 	}
49711600SVikram.Hegde@Sun.COM 
49811600SVikram.Hegde@Sun.COM 	return (B_FALSE);
49911600SVikram.Hegde@Sun.COM }
50011600SVikram.Hegde@Sun.COM 
50111600SVikram.Hegde@Sun.COM static boolean_t
50211600SVikram.Hegde@Sun.COM blacklisted_smbios(void)
50311600SVikram.Hegde@Sun.COM {
50411600SVikram.Hegde@Sun.COM 	id_t smid;
50511600SVikram.Hegde@Sun.COM 	smbios_hdl_t *smhdl;
50611600SVikram.Hegde@Sun.COM 	smbios_info_t sminf;
50711600SVikram.Hegde@Sun.COM 	smbios_system_t smsys;
50811600SVikram.Hegde@Sun.COM 	char *mfg, *product, *version;
50911600SVikram.Hegde@Sun.COM 	char **strptr;
51011600SVikram.Hegde@Sun.COM 	int i;
51111600SVikram.Hegde@Sun.COM 
51211600SVikram.Hegde@Sun.COM 	ASSERT((black_array == NULL) ^ (nblacks != 0));
51311600SVikram.Hegde@Sun.COM 
51411600SVikram.Hegde@Sun.COM 	/* need at least 4 strings for this setting */
51511600SVikram.Hegde@Sun.COM 	if (nblacks < 4) {
51611600SVikram.Hegde@Sun.COM 		return (B_FALSE);
51711600SVikram.Hegde@Sun.COM 	}
51811600SVikram.Hegde@Sun.COM 
51911600SVikram.Hegde@Sun.COM 	smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
52011600SVikram.Hegde@Sun.COM 	if (smhdl == NULL ||
52111600SVikram.Hegde@Sun.COM 	    (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR ||
52211600SVikram.Hegde@Sun.COM 	    smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) {
52311600SVikram.Hegde@Sun.COM 		return (B_FALSE);
52411600SVikram.Hegde@Sun.COM 	}
52511600SVikram.Hegde@Sun.COM 
52611600SVikram.Hegde@Sun.COM 	mfg = (char *)sminf.smbi_manufacturer;
52711600SVikram.Hegde@Sun.COM 	product = (char *)sminf.smbi_product;
52811600SVikram.Hegde@Sun.COM 	version = (char *)sminf.smbi_version;
52911600SVikram.Hegde@Sun.COM 
53011600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n");
53111600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg);
53211600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product);
53311600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version);
53411600SVikram.Hegde@Sun.COM 
53511600SVikram.Hegde@Sun.COM 	for (i = 0; nblacks - i > 3; i++) {
53611658SVikram.Hegde@Sun.COM 		strptr = &black_array[i];
53711600SVikram.Hegde@Sun.COM 		if (strcmp(*strptr++, "SMBIOS") == 0) {
53811600SVikram.Hegde@Sun.COM 			if (strcmp(*strptr++, mfg) == 0 &&
53911600SVikram.Hegde@Sun.COM 			    ((char *)strptr == '\0' ||
54011600SVikram.Hegde@Sun.COM 			    strcmp(*strptr++, product) == 0) &&
54111600SVikram.Hegde@Sun.COM 			    ((char *)strptr == '\0' ||
54211600SVikram.Hegde@Sun.COM 			    strcmp(*strptr++, version) == 0)) {
54311600SVikram.Hegde@Sun.COM 				return (B_TRUE);
54411600SVikram.Hegde@Sun.COM 			}
54511600SVikram.Hegde@Sun.COM 			i += 3;
54611600SVikram.Hegde@Sun.COM 		}
54711600SVikram.Hegde@Sun.COM 	}
54811600SVikram.Hegde@Sun.COM 
54911600SVikram.Hegde@Sun.COM 	return (B_FALSE);
55011600SVikram.Hegde@Sun.COM }
55111600SVikram.Hegde@Sun.COM 
55211600SVikram.Hegde@Sun.COM static boolean_t
55311600SVikram.Hegde@Sun.COM blacklisted_acpi(void)
55411600SVikram.Hegde@Sun.COM {
55511600SVikram.Hegde@Sun.COM 	ASSERT((black_array == NULL) ^ (nblacks != 0));
55611600SVikram.Hegde@Sun.COM 	if (nblacks == 0) {
55711600SVikram.Hegde@Sun.COM 		return (B_FALSE);
55811600SVikram.Hegde@Sun.COM 	}
55911600SVikram.Hegde@Sun.COM 
56011600SVikram.Hegde@Sun.COM 	return (immu_dmar_blacklisted(black_array, nblacks));
56111600SVikram.Hegde@Sun.COM }
56211600SVikram.Hegde@Sun.COM 
56311600SVikram.Hegde@Sun.COM /*
56411600SVikram.Hegde@Sun.COM  * Check if system is blacklisted by Intel IOMMU driver
56511600SVikram.Hegde@Sun.COM  * i.e. should Intel IOMMU be disabled on this system
56611600SVikram.Hegde@Sun.COM  * Currently a system can be blacklistd based on the
56711600SVikram.Hegde@Sun.COM  * following bases:
56811600SVikram.Hegde@Sun.COM  *
56911600SVikram.Hegde@Sun.COM  * 1. DMAR ACPI table information.
57011600SVikram.Hegde@Sun.COM  *    This information includes things like
57111600SVikram.Hegde@Sun.COM  *    manufacturer and revision number. If rootnex.conf
57211600SVikram.Hegde@Sun.COM  *    has matching info set in its blacklist property
57311600SVikram.Hegde@Sun.COM  *    then Intel IOMMu will be disabled
57411600SVikram.Hegde@Sun.COM  *
57511600SVikram.Hegde@Sun.COM  * 2. SMBIOS information
57611600SVikram.Hegde@Sun.COM  *
57711600SVikram.Hegde@Sun.COM  * 3. Driver installed - useful if a particular
57811600SVikram.Hegde@Sun.COM  *    driver or hardware is toxic if Intel IOMMU
57911600SVikram.Hegde@Sun.COM  *    is turned on.
58011600SVikram.Hegde@Sun.COM  */
58111600SVikram.Hegde@Sun.COM 
58211600SVikram.Hegde@Sun.COM static void
58311600SVikram.Hegde@Sun.COM blacklist_setup(void)
58411600SVikram.Hegde@Sun.COM {
58511600SVikram.Hegde@Sun.COM 	char **string_array;
58611600SVikram.Hegde@Sun.COM 	uint_t nstrings;
58711600SVikram.Hegde@Sun.COM 
58811600SVikram.Hegde@Sun.COM 	/*
58911600SVikram.Hegde@Sun.COM 	 * Check the rootnex.conf blacklist property.
59011600SVikram.Hegde@Sun.COM 	 * Fake up a dev_t since searching the global
59111600SVikram.Hegde@Sun.COM 	 * property list needs it
59211600SVikram.Hegde@Sun.COM 	 */
59311600SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string_array(
59411600SVikram.Hegde@Sun.COM 	    makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo,
59511600SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist",
59611600SVikram.Hegde@Sun.COM 	    &string_array, &nstrings) != DDI_PROP_SUCCESS) {
59711600SVikram.Hegde@Sun.COM 		return;
59811600SVikram.Hegde@Sun.COM 	}
59911600SVikram.Hegde@Sun.COM 
60011600SVikram.Hegde@Sun.COM 	/* smallest blacklist criteria works with multiples of 2 */
60111600SVikram.Hegde@Sun.COM 	if (nstrings % 2 != 0) {
60211600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist "
60311600SVikram.Hegde@Sun.COM 		    "rootnex.conf: number of strings must be a "
60411600SVikram.Hegde@Sun.COM 		    "multiple of 2");
60511600SVikram.Hegde@Sun.COM 		ddi_prop_free(string_array);
60611600SVikram.Hegde@Sun.COM 		return;
60711600SVikram.Hegde@Sun.COM 	}
60811600SVikram.Hegde@Sun.COM 
60911600SVikram.Hegde@Sun.COM 	black_array = string_array;
61011600SVikram.Hegde@Sun.COM 	nblacks = nstrings;
61111600SVikram.Hegde@Sun.COM }
61211600SVikram.Hegde@Sun.COM 
61311600SVikram.Hegde@Sun.COM static void
61411600SVikram.Hegde@Sun.COM blacklist_destroy(void)
61511600SVikram.Hegde@Sun.COM {
61611600SVikram.Hegde@Sun.COM 	if (black_array) {
61711600SVikram.Hegde@Sun.COM 		ddi_prop_free(black_array);
61811600SVikram.Hegde@Sun.COM 		black_array = NULL;
61911600SVikram.Hegde@Sun.COM 		nblacks = 0;
62011600SVikram.Hegde@Sun.COM 	}
62111600SVikram.Hegde@Sun.COM 
62211600SVikram.Hegde@Sun.COM 	ASSERT(black_array == NULL);
62311600SVikram.Hegde@Sun.COM 	ASSERT(nblacks == 0);
62411600SVikram.Hegde@Sun.COM }
62511600SVikram.Hegde@Sun.COM 
62611600SVikram.Hegde@Sun.COM 
62711600SVikram.Hegde@Sun.COM /*
62811600SVikram.Hegde@Sun.COM  * Now set all the fields in the order they are defined
62911600SVikram.Hegde@Sun.COM  * We do this only as a defensive-coding practice, it is
63011600SVikram.Hegde@Sun.COM  * not a correctness issue.
63111600SVikram.Hegde@Sun.COM  */
63211600SVikram.Hegde@Sun.COM static void *
63311600SVikram.Hegde@Sun.COM immu_state_alloc(int seg, void *dmar_unit)
63411600SVikram.Hegde@Sun.COM {
63511600SVikram.Hegde@Sun.COM 	immu_t *immu;
63611600SVikram.Hegde@Sun.COM 
63711600SVikram.Hegde@Sun.COM 	dmar_unit = immu_dmar_walk_units(seg, dmar_unit);
63811600SVikram.Hegde@Sun.COM 	if (dmar_unit == NULL) {
63911600SVikram.Hegde@Sun.COM 		/* No more IOMMUs in this segment */
64011600SVikram.Hegde@Sun.COM 		return (NULL);
64111600SVikram.Hegde@Sun.COM 	}
64211600SVikram.Hegde@Sun.COM 
64311600SVikram.Hegde@Sun.COM 	immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP);
64411600SVikram.Hegde@Sun.COM 
64511600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL);
64611600SVikram.Hegde@Sun.COM 
64711600SVikram.Hegde@Sun.COM 	mutex_enter(&(immu->immu_lock));
64811600SVikram.Hegde@Sun.COM 
64911600SVikram.Hegde@Sun.COM 	immu->immu_dmar_unit = dmar_unit;
65011600SVikram.Hegde@Sun.COM 	immu->immu_name = ddi_strdup(immu_dmar_unit_name(dmar_unit),
65111600SVikram.Hegde@Sun.COM 	    KM_SLEEP);
65211600SVikram.Hegde@Sun.COM 	immu->immu_dip = immu_dmar_unit_dip(dmar_unit);
65311600SVikram.Hegde@Sun.COM 
65411600SVikram.Hegde@Sun.COM 	/*
65511600SVikram.Hegde@Sun.COM 	 * the immu_intr_lock mutex is grabbed by the IOMMU
65611600SVikram.Hegde@Sun.COM 	 * unit's interrupt handler so we need to use an
65711600SVikram.Hegde@Sun.COM 	 * interrupt cookie for the mutex
65811600SVikram.Hegde@Sun.COM 	 */
65911600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER,
66011600SVikram.Hegde@Sun.COM 	    (void *)ipltospl(IMMU_INTR_IPL));
66111600SVikram.Hegde@Sun.COM 
66211600SVikram.Hegde@Sun.COM 	/* IOMMU regs related */
66311600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL);
66411658SVikram.Hegde@Sun.COM 	cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL);
66511658SVikram.Hegde@Sun.COM 	immu->immu_regs_busy = B_FALSE;
66611600SVikram.Hegde@Sun.COM 
66711600SVikram.Hegde@Sun.COM 	/* DVMA related */
66811600SVikram.Hegde@Sun.COM 	immu->immu_dvma_coherent = B_FALSE;
66911600SVikram.Hegde@Sun.COM 
67011600SVikram.Hegde@Sun.COM 	/* DVMA context related */
67111600SVikram.Hegde@Sun.COM 	rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL);
67211600SVikram.Hegde@Sun.COM 
67311600SVikram.Hegde@Sun.COM 	/* DVMA domain related */
67411600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_domain_list), sizeof (domain_t),
67511600SVikram.Hegde@Sun.COM 	    offsetof(domain_t, dom_immu_node));
67611600SVikram.Hegde@Sun.COM 
67711600SVikram.Hegde@Sun.COM 	/* DVMA special device lists */
67811600SVikram.Hegde@Sun.COM 	immu->immu_dvma_gfx_only = B_FALSE;
67911600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t),
68011600SVikram.Hegde@Sun.COM 	    offsetof(immu_devi_t, imd_spc_node));
68111600SVikram.Hegde@Sun.COM 	list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t),
68211600SVikram.Hegde@Sun.COM 	    offsetof(immu_devi_t, imd_spc_node));
68311600SVikram.Hegde@Sun.COM 
68411600SVikram.Hegde@Sun.COM 	/* interrupt remapping related */
68511600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL);
68611600SVikram.Hegde@Sun.COM 
68711600SVikram.Hegde@Sun.COM 	/* qinv related */
68811600SVikram.Hegde@Sun.COM 	mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL);
68911600SVikram.Hegde@Sun.COM 
69011600SVikram.Hegde@Sun.COM 	/*
69111600SVikram.Hegde@Sun.COM 	 * insert this immu unit into the system-wide list
69211600SVikram.Hegde@Sun.COM 	 */
69311600SVikram.Hegde@Sun.COM 	list_insert_tail(&immu_list, immu);
69411600SVikram.Hegde@Sun.COM 
69511600SVikram.Hegde@Sun.COM 	mutex_exit(&(immu->immu_lock));
69611600SVikram.Hegde@Sun.COM 
69711600SVikram.Hegde@Sun.COM 	ddi_err(DER_LOG, immu->immu_dip, "IMMU: unit setup");
69811600SVikram.Hegde@Sun.COM 
69911600SVikram.Hegde@Sun.COM 	immu_dmar_set_immu(dmar_unit, immu);
70011600SVikram.Hegde@Sun.COM 
70111600SVikram.Hegde@Sun.COM 	return (dmar_unit);
70211600SVikram.Hegde@Sun.COM }
70311600SVikram.Hegde@Sun.COM 
70411600SVikram.Hegde@Sun.COM static void
70511600SVikram.Hegde@Sun.COM immu_subsystems_setup(void)
70611600SVikram.Hegde@Sun.COM {
70711600SVikram.Hegde@Sun.COM 	int seg;
70811600SVikram.Hegde@Sun.COM 	void *unit_hdl;
70911600SVikram.Hegde@Sun.COM 
71011600SVikram.Hegde@Sun.COM 	ddi_err(DER_VERB, NULL,
71111600SVikram.Hegde@Sun.COM 	    "Creating state structures for Intel IOMMU units\n");
71211600SVikram.Hegde@Sun.COM 
71311600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_FALSE);
71411600SVikram.Hegde@Sun.COM 	ASSERT(immu_running == B_FALSE);
71511600SVikram.Hegde@Sun.COM 
71611600SVikram.Hegde@Sun.COM 	mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL);
71711600SVikram.Hegde@Sun.COM 	list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node));
71811600SVikram.Hegde@Sun.COM 
71911600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
72011600SVikram.Hegde@Sun.COM 
72111658SVikram.Hegde@Sun.COM 	ASSERT(immu_pgtable_cache == NULL);
72211658SVikram.Hegde@Sun.COM 
72311658SVikram.Hegde@Sun.COM 	immu_pgtable_cache = kmem_cache_create("immu_pgtable_cache",
72411658SVikram.Hegde@Sun.COM 	    sizeof (pgtable_t), 0,
72511658SVikram.Hegde@Sun.COM 	    pgtable_ctor, pgtable_dtor, NULL, NULL, NULL, 0);
72611658SVikram.Hegde@Sun.COM 
72711600SVikram.Hegde@Sun.COM 	unit_hdl = NULL;
72811600SVikram.Hegde@Sun.COM 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
72911600SVikram.Hegde@Sun.COM 		while (unit_hdl = immu_state_alloc(seg, unit_hdl)) {
73011600SVikram.Hegde@Sun.COM 			;
73111600SVikram.Hegde@Sun.COM 		}
73211600SVikram.Hegde@Sun.COM 	}
73311600SVikram.Hegde@Sun.COM 
73411600SVikram.Hegde@Sun.COM 	immu_regs_setup(&immu_list);	/* subsequent code needs this first */
73511600SVikram.Hegde@Sun.COM 	immu_dvma_setup(&immu_list);
73611600SVikram.Hegde@Sun.COM 	immu_intrmap_setup(&immu_list);
73711600SVikram.Hegde@Sun.COM 	immu_qinv_setup(&immu_list);
73811600SVikram.Hegde@Sun.COM 
73911600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
74011600SVikram.Hegde@Sun.COM }
74111600SVikram.Hegde@Sun.COM 
74211600SVikram.Hegde@Sun.COM /*
74311600SVikram.Hegde@Sun.COM  * immu_subsystems_startup()
74411600SVikram.Hegde@Sun.COM  * 	startup all units that were setup
74511600SVikram.Hegde@Sun.COM  */
74611600SVikram.Hegde@Sun.COM static void
74711600SVikram.Hegde@Sun.COM immu_subsystems_startup(void)
74811600SVikram.Hegde@Sun.COM {
74911600SVikram.Hegde@Sun.COM 	immu_t *immu;
75011600SVikram.Hegde@Sun.COM 
75111600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
75211600SVikram.Hegde@Sun.COM 
75311600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_TRUE);
75411600SVikram.Hegde@Sun.COM 	ASSERT(immu_running == B_FALSE);
75511600SVikram.Hegde@Sun.COM 
75611600SVikram.Hegde@Sun.COM 	immu_dmar_startup();
75711600SVikram.Hegde@Sun.COM 
75811600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
75911600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
76011600SVikram.Hegde@Sun.COM 
76111600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
76211600SVikram.Hegde@Sun.COM 
76311600SVikram.Hegde@Sun.COM 		immu_intr_register(immu);
76411600SVikram.Hegde@Sun.COM 		immu_dvma_startup(immu);
76511600SVikram.Hegde@Sun.COM 		immu_intrmap_startup(immu);
76611600SVikram.Hegde@Sun.COM 		immu_qinv_startup(immu);
76711600SVikram.Hegde@Sun.COM 
76811600SVikram.Hegde@Sun.COM 		/*
76911600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
77011600SVikram.Hegde@Sun.COM 		 * the actual startup. This will
77111600SVikram.Hegde@Sun.COM 		 * set immu->immu_running  field
77211600SVikram.Hegde@Sun.COM 		 * if the unit is successfully
77311600SVikram.Hegde@Sun.COM 		 * started
77411600SVikram.Hegde@Sun.COM 		 */
77511600SVikram.Hegde@Sun.COM 		immu_regs_startup(immu);
77611600SVikram.Hegde@Sun.COM 
77711600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
77811600SVikram.Hegde@Sun.COM 	}
77911600SVikram.Hegde@Sun.COM 
78011600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
78111600SVikram.Hegde@Sun.COM }
78211600SVikram.Hegde@Sun.COM 
78311600SVikram.Hegde@Sun.COM /* ##################  Intel IOMMU internal interfaces ###################### */
78411600SVikram.Hegde@Sun.COM 
78511600SVikram.Hegde@Sun.COM /*
78611600SVikram.Hegde@Sun.COM  * Internal interfaces for IOMMU code (i.e. not exported to rootnex
78711600SVikram.Hegde@Sun.COM  * or rest of system)
78811600SVikram.Hegde@Sun.COM  */
78911600SVikram.Hegde@Sun.COM 
79011600SVikram.Hegde@Sun.COM /*
79111600SVikram.Hegde@Sun.COM  * ddip can be NULL, in which case we walk up until we find the root dip
79211600SVikram.Hegde@Sun.COM  * NOTE: We never visit the root dip since its not a hardware node
79311600SVikram.Hegde@Sun.COM  */
79411600SVikram.Hegde@Sun.COM int
79511600SVikram.Hegde@Sun.COM immu_walk_ancestor(
79611600SVikram.Hegde@Sun.COM 	dev_info_t *rdip,
79711600SVikram.Hegde@Sun.COM 	dev_info_t *ddip,
79811600SVikram.Hegde@Sun.COM 	int (*func)(dev_info_t *, void *arg),
79911600SVikram.Hegde@Sun.COM 	void *arg,
80011600SVikram.Hegde@Sun.COM 	int *lvlp,
80111600SVikram.Hegde@Sun.COM 	immu_flags_t immu_flags)
80211600SVikram.Hegde@Sun.COM {
80311600SVikram.Hegde@Sun.COM 	dev_info_t *pdip;
80411600SVikram.Hegde@Sun.COM 	int level;
80511600SVikram.Hegde@Sun.COM 	int error = DDI_SUCCESS;
80611600SVikram.Hegde@Sun.COM 
80711600SVikram.Hegde@Sun.COM 	ASSERT(root_devinfo);
80811600SVikram.Hegde@Sun.COM 	ASSERT(rdip);
80911600SVikram.Hegde@Sun.COM 	ASSERT(rdip != root_devinfo);
81011600SVikram.Hegde@Sun.COM 	ASSERT(func);
81111600SVikram.Hegde@Sun.COM 
81211600SVikram.Hegde@Sun.COM 	/* ddip and immu can be NULL */
81311600SVikram.Hegde@Sun.COM 
81411600SVikram.Hegde@Sun.COM 	/* Hold rdip so that branch is not detached */
81511600SVikram.Hegde@Sun.COM 	ndi_hold_devi(rdip);
81611600SVikram.Hegde@Sun.COM 	for (pdip = rdip, level = 1; pdip && pdip != root_devinfo;
81711600SVikram.Hegde@Sun.COM 	    pdip = ddi_get_parent(pdip), level++) {
81811600SVikram.Hegde@Sun.COM 
81911600SVikram.Hegde@Sun.COM 		if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) {
82011600SVikram.Hegde@Sun.COM 			error = DDI_FAILURE;
82111600SVikram.Hegde@Sun.COM 			break;
82211600SVikram.Hegde@Sun.COM 		}
82311600SVikram.Hegde@Sun.COM 		if (func(pdip, arg) == DDI_WALK_TERMINATE) {
82411600SVikram.Hegde@Sun.COM 			break;
82511600SVikram.Hegde@Sun.COM 		}
82611600SVikram.Hegde@Sun.COM 		if (immu_flags & IMMU_FLAGS_DONTPASS) {
82711600SVikram.Hegde@Sun.COM 			break;
82811600SVikram.Hegde@Sun.COM 		}
82911600SVikram.Hegde@Sun.COM 		if (pdip == ddip) {
83011600SVikram.Hegde@Sun.COM 			break;
83111600SVikram.Hegde@Sun.COM 		}
83211600SVikram.Hegde@Sun.COM 	}
83311600SVikram.Hegde@Sun.COM 
83411600SVikram.Hegde@Sun.COM 	ndi_rele_devi(rdip);
83511600SVikram.Hegde@Sun.COM 
83611600SVikram.Hegde@Sun.COM 	if (lvlp)
83711600SVikram.Hegde@Sun.COM 		*lvlp = level;
83811600SVikram.Hegde@Sun.COM 
83911600SVikram.Hegde@Sun.COM 	return (error);
84011600SVikram.Hegde@Sun.COM }
84111600SVikram.Hegde@Sun.COM 
84211600SVikram.Hegde@Sun.COM /* ########################  Intel IOMMU entry points ####################### */
84311600SVikram.Hegde@Sun.COM /*
84411600SVikram.Hegde@Sun.COM  * immu_init()
84511600SVikram.Hegde@Sun.COM  *	called from rootnex_attach(). setup but don't startup the Intel IOMMU
84611600SVikram.Hegde@Sun.COM  *      This is the first function called in Intel IOMMU code
84711600SVikram.Hegde@Sun.COM  */
84811600SVikram.Hegde@Sun.COM void
84911600SVikram.Hegde@Sun.COM immu_init(void)
85011600SVikram.Hegde@Sun.COM {
85111600SVikram.Hegde@Sun.COM 	char *phony_reg = "A thing of beauty is a joy forever";
85211600SVikram.Hegde@Sun.COM 
85311600SVikram.Hegde@Sun.COM 	/* Set some global shorthands that are needed by all of IOMMU code */
85411600SVikram.Hegde@Sun.COM 	ASSERT(root_devinfo == NULL);
85511600SVikram.Hegde@Sun.COM 	root_devinfo = ddi_root_node();
85611600SVikram.Hegde@Sun.COM 
85711600SVikram.Hegde@Sun.COM 	/*
85811600SVikram.Hegde@Sun.COM 	 * Intel IOMMU only supported only if MMU(CPU) page size is ==
85911600SVikram.Hegde@Sun.COM 	 * IOMMU pages size.
86011600SVikram.Hegde@Sun.COM 	 */
86111600SVikram.Hegde@Sun.COM 	/*LINTED*/
86211600SVikram.Hegde@Sun.COM 	if (MMU_PAGESIZE != IMMU_PAGESIZE) {
86311600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL,
86411600SVikram.Hegde@Sun.COM 		    "MMU page size (%d) is not equal to\n"
86511600SVikram.Hegde@Sun.COM 		    "IOMMU page size (%d). "
86611600SVikram.Hegde@Sun.COM 		    "Disabling Intel IOMMU. ",
86711600SVikram.Hegde@Sun.COM 		    MMU_PAGESIZE, IMMU_PAGESIZE);
86811600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
86911600SVikram.Hegde@Sun.COM 		return;
87011600SVikram.Hegde@Sun.COM 	}
87111600SVikram.Hegde@Sun.COM 
87211600SVikram.Hegde@Sun.COM 	/*
873*11749SVikram.Hegde@Sun.COM 	 * Read rootnex.conf options. Do this before
874*11749SVikram.Hegde@Sun.COM 	 * boot options so boot options can override .conf options.
875*11749SVikram.Hegde@Sun.COM 	 */
876*11749SVikram.Hegde@Sun.COM 	read_conf_options();
877*11749SVikram.Hegde@Sun.COM 
878*11749SVikram.Hegde@Sun.COM 	/*
87911600SVikram.Hegde@Sun.COM 	 * retrieve the Intel IOMMU boot options.
88011600SVikram.Hegde@Sun.COM 	 * Do this before parsing immu ACPI table
88111600SVikram.Hegde@Sun.COM 	 * as a boot option could potentially affect
88211600SVikram.Hegde@Sun.COM 	 * ACPI parsing.
88311600SVikram.Hegde@Sun.COM 	 */
88411600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n");
88511600SVikram.Hegde@Sun.COM 	read_boot_options();
88611600SVikram.Hegde@Sun.COM 
88711600SVikram.Hegde@Sun.COM 	/*
88811600SVikram.Hegde@Sun.COM 	 * Check the IOMMU enable boot-option first.
88911600SVikram.Hegde@Sun.COM 	 * This is so that we can skip parsing the ACPI table
89011600SVikram.Hegde@Sun.COM 	 * if necessary because that may cause problems in
89111600SVikram.Hegde@Sun.COM 	 * systems with buggy BIOS or ACPI tables
89211600SVikram.Hegde@Sun.COM 	 */
89311600SVikram.Hegde@Sun.COM 	if (immu_enable == B_FALSE) {
89411600SVikram.Hegde@Sun.COM 		return;
89511600SVikram.Hegde@Sun.COM 	}
89611600SVikram.Hegde@Sun.COM 
89711600SVikram.Hegde@Sun.COM 	/*
89811600SVikram.Hegde@Sun.COM 	 * Next, check if the system even has an Intel IOMMU
89911600SVikram.Hegde@Sun.COM 	 * We use the presence or absence of the IOMMU ACPI
90011600SVikram.Hegde@Sun.COM 	 * table to detect Intel IOMMU.
90111600SVikram.Hegde@Sun.COM 	 */
90211600SVikram.Hegde@Sun.COM 	if (immu_dmar_setup() != DDI_SUCCESS) {
90311600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
90411600SVikram.Hegde@Sun.COM 		return;
90511600SVikram.Hegde@Sun.COM 	}
90611600SVikram.Hegde@Sun.COM 
90711600SVikram.Hegde@Sun.COM 	/*
90811600SVikram.Hegde@Sun.COM 	 * Check blacklists
90911600SVikram.Hegde@Sun.COM 	 */
91011600SVikram.Hegde@Sun.COM 	blacklist_setup();
91111600SVikram.Hegde@Sun.COM 
91211600SVikram.Hegde@Sun.COM 	if (blacklisted_smbios() == B_TRUE) {
91311600SVikram.Hegde@Sun.COM 		blacklist_destroy();
91411600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
91511600SVikram.Hegde@Sun.COM 		return;
91611600SVikram.Hegde@Sun.COM 	}
91711600SVikram.Hegde@Sun.COM 
91811600SVikram.Hegde@Sun.COM 	if (blacklisted_driver() == B_TRUE) {
91911600SVikram.Hegde@Sun.COM 		blacklist_destroy();
92011600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
92111600SVikram.Hegde@Sun.COM 		return;
92211600SVikram.Hegde@Sun.COM 	}
92311600SVikram.Hegde@Sun.COM 
92411600SVikram.Hegde@Sun.COM 	/*
92511600SVikram.Hegde@Sun.COM 	 * Read the "raw" DMAR ACPI table to get information
92611600SVikram.Hegde@Sun.COM 	 * and convert into a form we can use.
92711600SVikram.Hegde@Sun.COM 	 */
92811600SVikram.Hegde@Sun.COM 	if (immu_dmar_parse() != DDI_SUCCESS) {
92911600SVikram.Hegde@Sun.COM 		blacklist_destroy();
93011600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
93111600SVikram.Hegde@Sun.COM 		return;
93211600SVikram.Hegde@Sun.COM 	}
93311600SVikram.Hegde@Sun.COM 
93411600SVikram.Hegde@Sun.COM 	/*
93511600SVikram.Hegde@Sun.COM 	 * now that we have processed the ACPI table
93611600SVikram.Hegde@Sun.COM 	 * check if we need to blacklist this system
93711600SVikram.Hegde@Sun.COM 	 * based on ACPI info
93811600SVikram.Hegde@Sun.COM 	 */
93911600SVikram.Hegde@Sun.COM 	if (blacklisted_acpi() == B_TRUE) {
94011600SVikram.Hegde@Sun.COM 		immu_dmar_destroy();
94111600SVikram.Hegde@Sun.COM 		blacklist_destroy();
94211600SVikram.Hegde@Sun.COM 		immu_enable = B_FALSE;
94311600SVikram.Hegde@Sun.COM 		return;
94411600SVikram.Hegde@Sun.COM 	}
94511600SVikram.Hegde@Sun.COM 
94611600SVikram.Hegde@Sun.COM 	blacklist_destroy();
94711600SVikram.Hegde@Sun.COM 
94811600SVikram.Hegde@Sun.COM 	/*
94911600SVikram.Hegde@Sun.COM 	 * Check if system has HW quirks.
95011600SVikram.Hegde@Sun.COM 	 */
95111600SVikram.Hegde@Sun.COM 	pre_setup_quirks();
95211600SVikram.Hegde@Sun.COM 
95311600SVikram.Hegde@Sun.COM 	/* Now do the rest of the setup */
95411600SVikram.Hegde@Sun.COM 	immu_subsystems_setup();
95511600SVikram.Hegde@Sun.COM 
95611600SVikram.Hegde@Sun.COM 	/*
95711600SVikram.Hegde@Sun.COM 	 * Now that the IMMU is setup, create a phony
95811600SVikram.Hegde@Sun.COM 	 * reg prop so that suspend/resume works
95911600SVikram.Hegde@Sun.COM 	 */
96011600SVikram.Hegde@Sun.COM 	if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg",
96111600SVikram.Hegde@Sun.COM 	    (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) {
96211600SVikram.Hegde@Sun.COM 		ddi_err(DER_PANIC, NULL, "Failed to create reg prop for "
96311600SVikram.Hegde@Sun.COM 		    "rootnex node");
96411600SVikram.Hegde@Sun.COM 		/*NOTREACHED*/
96511600SVikram.Hegde@Sun.COM 	}
96611600SVikram.Hegde@Sun.COM 
96711600SVikram.Hegde@Sun.COM 	immu_setup = B_TRUE;
96811600SVikram.Hegde@Sun.COM }
96911600SVikram.Hegde@Sun.COM 
97011600SVikram.Hegde@Sun.COM /*
97111600SVikram.Hegde@Sun.COM  * immu_startup()
97211600SVikram.Hegde@Sun.COM  * 	called directly by boot code to startup
97311600SVikram.Hegde@Sun.COM  *      all units of the IOMMU
97411600SVikram.Hegde@Sun.COM  */
97511600SVikram.Hegde@Sun.COM void
97611600SVikram.Hegde@Sun.COM immu_startup(void)
97711600SVikram.Hegde@Sun.COM {
97811600SVikram.Hegde@Sun.COM 	/*
97911600SVikram.Hegde@Sun.COM 	 * If IOMMU is disabled, do nothing
98011600SVikram.Hegde@Sun.COM 	 */
98111600SVikram.Hegde@Sun.COM 	if (immu_enable == B_FALSE) {
98211600SVikram.Hegde@Sun.COM 		return;
98311600SVikram.Hegde@Sun.COM 	}
98411600SVikram.Hegde@Sun.COM 
98511600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
98611600SVikram.Hegde@Sun.COM 		ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, "
98711600SVikram.Hegde@Sun.COM 		    "skipping IOMU startup");
98811600SVikram.Hegde@Sun.COM 		return;
98911600SVikram.Hegde@Sun.COM 	}
99011600SVikram.Hegde@Sun.COM 
99111600SVikram.Hegde@Sun.COM 	pre_startup_quirks();
99211600SVikram.Hegde@Sun.COM 
99311600SVikram.Hegde@Sun.COM 	ddi_err(DER_CONT, NULL,
99411600SVikram.Hegde@Sun.COM 	    "?Starting Intel IOMMU (dmar) units...\n");
99511600SVikram.Hegde@Sun.COM 
99611600SVikram.Hegde@Sun.COM 	immu_subsystems_startup();
99711600SVikram.Hegde@Sun.COM 
99811600SVikram.Hegde@Sun.COM 	immu_running = B_TRUE;
99911600SVikram.Hegde@Sun.COM }
100011600SVikram.Hegde@Sun.COM 
100111600SVikram.Hegde@Sun.COM /*
100211600SVikram.Hegde@Sun.COM  * immu_map_sgl()
100311600SVikram.Hegde@Sun.COM  * 	called from rootnex_coredma_bindhdl() when Intel
100411600SVikram.Hegde@Sun.COM  *	IOMMU is enabled to build DVMA cookies and map them.
100511600SVikram.Hegde@Sun.COM  */
100611600SVikram.Hegde@Sun.COM int
100711600SVikram.Hegde@Sun.COM immu_map_sgl(ddi_dma_impl_t *hp, struct ddi_dma_req *dmareq,
100811600SVikram.Hegde@Sun.COM     int prealloc_count, dev_info_t *rdip)
100911600SVikram.Hegde@Sun.COM {
101011600SVikram.Hegde@Sun.COM 	if (immu_running == B_FALSE) {
101111600SVikram.Hegde@Sun.COM 		return (DDI_DMA_USE_PHYSICAL);
101211600SVikram.Hegde@Sun.COM 	}
101311600SVikram.Hegde@Sun.COM 
101411600SVikram.Hegde@Sun.COM 	return (immu_dvma_map(hp, dmareq, NULL, prealloc_count, rdip,
101511600SVikram.Hegde@Sun.COM 	    IMMU_FLAGS_DMAHDL));
101611600SVikram.Hegde@Sun.COM }
101711600SVikram.Hegde@Sun.COM 
101811600SVikram.Hegde@Sun.COM /*
101911600SVikram.Hegde@Sun.COM  * immu_unmap_sgl()
102011600SVikram.Hegde@Sun.COM  * 	called from rootnex_coredma_unbindhdl(), to unmap DVMA
102111600SVikram.Hegde@Sun.COM  * 	cookies and free them
102211600SVikram.Hegde@Sun.COM  */
102311600SVikram.Hegde@Sun.COM int
102411600SVikram.Hegde@Sun.COM immu_unmap_sgl(ddi_dma_impl_t *hp, dev_info_t *rdip)
102511600SVikram.Hegde@Sun.COM {
102611600SVikram.Hegde@Sun.COM 	if (immu_running == B_FALSE) {
102711600SVikram.Hegde@Sun.COM 		return (DDI_DMA_USE_PHYSICAL);
102811600SVikram.Hegde@Sun.COM 	}
102911600SVikram.Hegde@Sun.COM 
103011600SVikram.Hegde@Sun.COM 	return (immu_dvma_unmap(hp, rdip));
103111600SVikram.Hegde@Sun.COM }
103211600SVikram.Hegde@Sun.COM 
103311600SVikram.Hegde@Sun.COM /*
103411600SVikram.Hegde@Sun.COM  * Hook to notify IOMMU code of device tree changes
103511600SVikram.Hegde@Sun.COM  */
103611600SVikram.Hegde@Sun.COM void
103711600SVikram.Hegde@Sun.COM immu_device_tree_changed(void)
103811600SVikram.Hegde@Sun.COM {
103911600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
104011600SVikram.Hegde@Sun.COM 		return;
104111600SVikram.Hegde@Sun.COM 	}
104211600SVikram.Hegde@Sun.COM 
104311600SVikram.Hegde@Sun.COM 	ddi_err(DER_WARN, NULL, "Intel IOMMU currently "
104411600SVikram.Hegde@Sun.COM 	    "does not use device tree updates");
104511600SVikram.Hegde@Sun.COM }
104611600SVikram.Hegde@Sun.COM 
104711600SVikram.Hegde@Sun.COM /*
104811600SVikram.Hegde@Sun.COM  * Hook to notify IOMMU code of memory changes
104911600SVikram.Hegde@Sun.COM  */
105011600SVikram.Hegde@Sun.COM void
105111600SVikram.Hegde@Sun.COM immu_physmem_update(uint64_t addr, uint64_t size)
105211600SVikram.Hegde@Sun.COM {
105311600SVikram.Hegde@Sun.COM 	if (immu_setup == B_FALSE) {
105411600SVikram.Hegde@Sun.COM 		return;
105511600SVikram.Hegde@Sun.COM 	}
105611600SVikram.Hegde@Sun.COM 	immu_dvma_physmem_update(addr, size);
105711600SVikram.Hegde@Sun.COM }
105811600SVikram.Hegde@Sun.COM 
105911600SVikram.Hegde@Sun.COM /*
106011600SVikram.Hegde@Sun.COM  * immu_quiesce()
106111600SVikram.Hegde@Sun.COM  * 	quiesce all units that are running
106211600SVikram.Hegde@Sun.COM  */
106311600SVikram.Hegde@Sun.COM int
106411600SVikram.Hegde@Sun.COM immu_quiesce(void)
106511600SVikram.Hegde@Sun.COM {
106611600SVikram.Hegde@Sun.COM 	immu_t *immu;
106711600SVikram.Hegde@Sun.COM 	int ret = DDI_SUCCESS;
106811600SVikram.Hegde@Sun.COM 
106911600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
107011600SVikram.Hegde@Sun.COM 
107111600SVikram.Hegde@Sun.COM 	if (immu_running == B_FALSE)
107211600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
107311600SVikram.Hegde@Sun.COM 
107411600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_TRUE);
107511600SVikram.Hegde@Sun.COM 
107611600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
107711600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
107811600SVikram.Hegde@Sun.COM 
107911600SVikram.Hegde@Sun.COM 		/* if immu is not running, we dont quiesce */
108011600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_FALSE)
108111600SVikram.Hegde@Sun.COM 			continue;
108211600SVikram.Hegde@Sun.COM 
108311600SVikram.Hegde@Sun.COM 		/* flush caches */
108411600SVikram.Hegde@Sun.COM 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
108511600SVikram.Hegde@Sun.COM 		immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL);
108611600SVikram.Hegde@Sun.COM 		rw_exit(&(immu->immu_ctx_rwlock));
108711600SVikram.Hegde@Sun.COM 		immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL);
108811600SVikram.Hegde@Sun.COM 		immu_regs_wbf_flush(immu);
108911600SVikram.Hegde@Sun.COM 
109011600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
109111600SVikram.Hegde@Sun.COM 
109211600SVikram.Hegde@Sun.COM 		/*
109311600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
109411600SVikram.Hegde@Sun.COM 		 * the actual shutdown.
109511600SVikram.Hegde@Sun.COM 		 */
109611600SVikram.Hegde@Sun.COM 		immu_regs_shutdown(immu);
109711600SVikram.Hegde@Sun.COM 		immu_regs_suspend(immu);
109811600SVikram.Hegde@Sun.COM 
109911600SVikram.Hegde@Sun.COM 		/* if immu is still running, we failed */
110011600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_TRUE)
110111600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
110211600SVikram.Hegde@Sun.COM 		else
110311600SVikram.Hegde@Sun.COM 			immu->immu_regs_quiesced = B_TRUE;
110411600SVikram.Hegde@Sun.COM 
110511600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
110611600SVikram.Hegde@Sun.COM 	}
110711600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
110811600SVikram.Hegde@Sun.COM 
110911600SVikram.Hegde@Sun.COM 	if (ret == DDI_SUCCESS) {
111011600SVikram.Hegde@Sun.COM 		immu_running = B_FALSE;
111111600SVikram.Hegde@Sun.COM 		immu_quiesced = B_TRUE;
111211600SVikram.Hegde@Sun.COM 	}
111311600SVikram.Hegde@Sun.COM 
111411600SVikram.Hegde@Sun.COM 	return (ret);
111511600SVikram.Hegde@Sun.COM }
111611600SVikram.Hegde@Sun.COM 
111711600SVikram.Hegde@Sun.COM /*
111811600SVikram.Hegde@Sun.COM  * immu_unquiesce()
111911600SVikram.Hegde@Sun.COM  * 	unquiesce all units
112011600SVikram.Hegde@Sun.COM  */
112111600SVikram.Hegde@Sun.COM int
112211600SVikram.Hegde@Sun.COM immu_unquiesce(void)
112311600SVikram.Hegde@Sun.COM {
112411600SVikram.Hegde@Sun.COM 	immu_t *immu;
112511600SVikram.Hegde@Sun.COM 	int ret = DDI_SUCCESS;
112611600SVikram.Hegde@Sun.COM 
112711600SVikram.Hegde@Sun.COM 	mutex_enter(&immu_lock);
112811600SVikram.Hegde@Sun.COM 
112911600SVikram.Hegde@Sun.COM 	if (immu_quiesced == B_FALSE)
113011600SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
113111600SVikram.Hegde@Sun.COM 
113211600SVikram.Hegde@Sun.COM 	ASSERT(immu_setup == B_TRUE);
113311600SVikram.Hegde@Sun.COM 	ASSERT(immu_running == B_FALSE);
113411600SVikram.Hegde@Sun.COM 
113511600SVikram.Hegde@Sun.COM 	immu = list_head(&immu_list);
113611600SVikram.Hegde@Sun.COM 	for (; immu; immu = list_next(&immu_list, immu)) {
113711600SVikram.Hegde@Sun.COM 
113811600SVikram.Hegde@Sun.COM 		mutex_enter(&(immu->immu_lock));
113911600SVikram.Hegde@Sun.COM 
114011600SVikram.Hegde@Sun.COM 		/* if immu was not quiesced, i.e was not running before */
114111658SVikram.Hegde@Sun.COM 		if (immu->immu_regs_quiesced == B_FALSE) {
114211658SVikram.Hegde@Sun.COM 			mutex_exit(&(immu->immu_lock));
114311600SVikram.Hegde@Sun.COM 			continue;
114411658SVikram.Hegde@Sun.COM 		}
114511600SVikram.Hegde@Sun.COM 
114611600SVikram.Hegde@Sun.COM 		if (immu_regs_resume(immu) != DDI_SUCCESS) {
114711600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
114811658SVikram.Hegde@Sun.COM 			mutex_exit(&(immu->immu_lock));
114911600SVikram.Hegde@Sun.COM 			continue;
115011600SVikram.Hegde@Sun.COM 		}
115111600SVikram.Hegde@Sun.COM 
115211600SVikram.Hegde@Sun.COM 		/* flush caches before unquiesce */
115311600SVikram.Hegde@Sun.COM 		rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER);
115411600SVikram.Hegde@Sun.COM 		immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL);
115511600SVikram.Hegde@Sun.COM 		rw_exit(&(immu->immu_ctx_rwlock));
115611600SVikram.Hegde@Sun.COM 		immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL);
115711600SVikram.Hegde@Sun.COM 
115811600SVikram.Hegde@Sun.COM 		/*
115911600SVikram.Hegde@Sun.COM 		 * Set IOMMU unit's regs to do
116011600SVikram.Hegde@Sun.COM 		 * the actual startup. This will
116111600SVikram.Hegde@Sun.COM 		 * set immu->immu_regs_running  field
116211600SVikram.Hegde@Sun.COM 		 * if the unit is successfully
116311600SVikram.Hegde@Sun.COM 		 * started
116411600SVikram.Hegde@Sun.COM 		 */
116511600SVikram.Hegde@Sun.COM 		immu_regs_startup(immu);
116611600SVikram.Hegde@Sun.COM 
116711600SVikram.Hegde@Sun.COM 		if (immu->immu_regs_running == B_FALSE) {
116811600SVikram.Hegde@Sun.COM 			ret = DDI_FAILURE;
116911600SVikram.Hegde@Sun.COM 		} else {
117011600SVikram.Hegde@Sun.COM 			immu_quiesced = B_TRUE;
117111600SVikram.Hegde@Sun.COM 			immu_running = B_TRUE;
117211600SVikram.Hegde@Sun.COM 			immu->immu_regs_quiesced = B_FALSE;
117311600SVikram.Hegde@Sun.COM 		}
117411600SVikram.Hegde@Sun.COM 
117511600SVikram.Hegde@Sun.COM 		mutex_exit(&(immu->immu_lock));
117611600SVikram.Hegde@Sun.COM 	}
117711600SVikram.Hegde@Sun.COM 
117811600SVikram.Hegde@Sun.COM 	mutex_exit(&immu_lock);
117911600SVikram.Hegde@Sun.COM 
118011600SVikram.Hegde@Sun.COM 	return (ret);
118111600SVikram.Hegde@Sun.COM }
118211600SVikram.Hegde@Sun.COM 
118311600SVikram.Hegde@Sun.COM /* ##############  END Intel IOMMU entry points ################## */
1184