xref: /titanic_52/usr/src/uts/i86pc/io/immu_dmar.c (revision 50200e773f0242e336d032a7b43485e1bcfc9bfe)
13a634bfcSVikram Hegde /*
23a634bfcSVikram Hegde  * CDDL HEADER START
33a634bfcSVikram Hegde  *
43a634bfcSVikram Hegde  * The contents of this file are subject to the terms of the
53a634bfcSVikram Hegde  * Common Development and Distribution License (the "License").
63a634bfcSVikram Hegde  * You may not use this file except in compliance with the License.
73a634bfcSVikram Hegde  *
83a634bfcSVikram Hegde  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93a634bfcSVikram Hegde  * or http://www.opensolaris.org/os/licensing.
103a634bfcSVikram Hegde  * See the License for the specific language governing permissions
113a634bfcSVikram Hegde  * and limitations under the License.
123a634bfcSVikram Hegde  *
133a634bfcSVikram Hegde  * When distributing Covered Code, include this CDDL HEADER in each
143a634bfcSVikram Hegde  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153a634bfcSVikram Hegde  * If applicable, add the following below this CDDL HEADER, with the
163a634bfcSVikram Hegde  * fields enclosed by brackets "[]" replaced with your own identifying
173a634bfcSVikram Hegde  * information: Portions Copyright [yyyy] [name of copyright owner]
183a634bfcSVikram Hegde  *
193a634bfcSVikram Hegde  * CDDL HEADER END
203a634bfcSVikram Hegde  */
213a634bfcSVikram Hegde /*
227ff178cdSJimmy Vetayases  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
233a634bfcSVikram Hegde  */
243a634bfcSVikram Hegde 
253a634bfcSVikram Hegde /*
263a634bfcSVikram Hegde  * Copyright (c) 2009, Intel Corporation.
273a634bfcSVikram Hegde  * All rights reserved.
283a634bfcSVikram Hegde  */
293a634bfcSVikram Hegde 
303a634bfcSVikram Hegde 
313a634bfcSVikram Hegde #include <sys/debug.h>
323a634bfcSVikram Hegde #include <sys/sysmacros.h>
333a634bfcSVikram Hegde #include <sys/types.h>
343a634bfcSVikram Hegde #include <sys/kmem.h>
353a634bfcSVikram Hegde #include <sys/sunddi.h>
363a634bfcSVikram Hegde #include <sys/list.h>
373a634bfcSVikram Hegde #include <sys/pci.h>
383a634bfcSVikram Hegde #include <sys/pci_cfgspace.h>
393a634bfcSVikram Hegde #include <sys/pci_impl.h>
403a634bfcSVikram Hegde #include <sys/sunndi.h>
413a634bfcSVikram Hegde #include <sys/ksynch.h>
423a634bfcSVikram Hegde #include <sys/cmn_err.h>
433a634bfcSVikram Hegde #include <sys/bootconf.h>
443a634bfcSVikram Hegde #include <sys/int_fmtio.h>
453a634bfcSVikram Hegde #include <sys/smbios.h>
46c94adbf9SFrank Van Der Linden #include <sys/apic.h>
473a634bfcSVikram Hegde #include <sys/acpi/acpi.h>
483a634bfcSVikram Hegde #include <sys/acpica.h>
493a634bfcSVikram Hegde #include <sys/immu.h>
507ff178cdSJimmy Vetayases #include <sys/smp_impldefs.h>
513a634bfcSVikram Hegde 
523a634bfcSVikram Hegde static void dmar_table_destroy(dmar_table_t *tbl);
533a634bfcSVikram Hegde 
543a634bfcSVikram Hegde /*
553a634bfcSVikram Hegde  * internal global variables
563a634bfcSVikram Hegde  */
573a634bfcSVikram Hegde static char	*dmar_raw;		/* raw DMAR ACPI table */
583a634bfcSVikram Hegde static dmar_table_t *dmar_table;	/* converted form of DMAR table */
593a634bfcSVikram Hegde 
603a634bfcSVikram Hegde /*
613a634bfcSVikram Hegde  * global variables exported outside this file
623a634bfcSVikram Hegde  */
633a634bfcSVikram Hegde boolean_t dmar_print = B_FALSE;
643a634bfcSVikram Hegde kmutex_t ioapic_drhd_lock;
653a634bfcSVikram Hegde list_t ioapic_drhd_list;
663a634bfcSVikram Hegde 
673a634bfcSVikram Hegde /* ######################################################################### */
683a634bfcSVikram Hegde 
693a634bfcSVikram Hegde /*
703a634bfcSVikram Hegde  * helper functions to read the "raw" DMAR table
713a634bfcSVikram Hegde  */
723a634bfcSVikram Hegde 
733a634bfcSVikram Hegde static uint8_t
743a634bfcSVikram Hegde get_uint8(char *cp)
753a634bfcSVikram Hegde {
763a634bfcSVikram Hegde 	uint8_t val = *((uint8_t *)cp);
773a634bfcSVikram Hegde 	return (val);
783a634bfcSVikram Hegde }
793a634bfcSVikram Hegde 
803a634bfcSVikram Hegde static uint16_t
813a634bfcSVikram Hegde get_uint16(char *cp)
823a634bfcSVikram Hegde {
833a634bfcSVikram Hegde 	uint16_t val = *((uint16_t *)cp);
843a634bfcSVikram Hegde 	return (val);
853a634bfcSVikram Hegde }
863a634bfcSVikram Hegde 
873a634bfcSVikram Hegde static uint32_t
883a634bfcSVikram Hegde get_uint32(char *cp)
893a634bfcSVikram Hegde {
903a634bfcSVikram Hegde 	uint32_t val = *((uint32_t *)cp);
913a634bfcSVikram Hegde 	return (val);
923a634bfcSVikram Hegde }
933a634bfcSVikram Hegde 
943a634bfcSVikram Hegde static uint64_t
953a634bfcSVikram Hegde get_uint64(char *cp)
963a634bfcSVikram Hegde {
973a634bfcSVikram Hegde 	uint64_t val = *((uint64_t *)cp);
983a634bfcSVikram Hegde 	return (val);
993a634bfcSVikram Hegde }
1003a634bfcSVikram Hegde 
1013a634bfcSVikram Hegde static char *
1023a634bfcSVikram Hegde get_str(char *cp, uint_t len)
1033a634bfcSVikram Hegde {
1043a634bfcSVikram Hegde 	char *str = kmem_alloc(len + 1, KM_SLEEP);
1053a634bfcSVikram Hegde 
1063a634bfcSVikram Hegde 	(void) strlcpy(str, cp, len + 1);
1073a634bfcSVikram Hegde 
1083a634bfcSVikram Hegde 	return (str);
1093a634bfcSVikram Hegde }
1103a634bfcSVikram Hegde 
1113a634bfcSVikram Hegde static void
1123a634bfcSVikram Hegde scope_list_free(list_t *scope_list)
1133a634bfcSVikram Hegde {
1143a634bfcSVikram Hegde 	scope_t *scope;
1153a634bfcSVikram Hegde 
1163a634bfcSVikram Hegde 	if (list_is_empty(scope_list)) {
1173a634bfcSVikram Hegde 		list_destroy(scope_list);
1183a634bfcSVikram Hegde 		return;
1193a634bfcSVikram Hegde 	}
1203a634bfcSVikram Hegde 
1213a634bfcSVikram Hegde 	while ((scope = list_remove_head(scope_list)) != NULL) {
1223a634bfcSVikram Hegde 		kmem_free(scope, sizeof (scope_t));
1233a634bfcSVikram Hegde 	}
1243a634bfcSVikram Hegde 
1253a634bfcSVikram Hegde 	ASSERT(list_is_empty(scope_list));
1263a634bfcSVikram Hegde 	list_destroy(scope_list);
1273a634bfcSVikram Hegde }
1283a634bfcSVikram Hegde 
1293a634bfcSVikram Hegde static void
1303a634bfcSVikram Hegde drhd_list_destroy(list_t *drhd_list)
1313a634bfcSVikram Hegde {
1323a634bfcSVikram Hegde 	drhd_t *drhd;
1333a634bfcSVikram Hegde 
1343a634bfcSVikram Hegde 	ASSERT(drhd_list);
1353a634bfcSVikram Hegde 
1363a634bfcSVikram Hegde 	if (list_is_empty(drhd_list)) {
1373a634bfcSVikram Hegde 		list_destroy(drhd_list);
1383a634bfcSVikram Hegde 		return;
1393a634bfcSVikram Hegde 	}
1403a634bfcSVikram Hegde 
1413a634bfcSVikram Hegde 	while ((drhd = list_remove_head(drhd_list)) != NULL) {
1423a634bfcSVikram Hegde 		scope_list_free(&(drhd->dr_scope_list));
1433a634bfcSVikram Hegde 		kmem_free(drhd, sizeof (drhd_t));
1443a634bfcSVikram Hegde 	}
1453a634bfcSVikram Hegde 
1463a634bfcSVikram Hegde 	ASSERT(list_is_empty(drhd_list));
1473a634bfcSVikram Hegde 	list_destroy(drhd_list);
1483a634bfcSVikram Hegde }
1493a634bfcSVikram Hegde 
1503a634bfcSVikram Hegde static void
1513a634bfcSVikram Hegde rmrr_list_destroy(list_t *rmrr_list)
1523a634bfcSVikram Hegde {
1533a634bfcSVikram Hegde 	rmrr_t *rmrr;
1543a634bfcSVikram Hegde 
1553a634bfcSVikram Hegde 	ASSERT(rmrr_list);
1563a634bfcSVikram Hegde 
1573a634bfcSVikram Hegde 	if (list_is_empty(rmrr_list)) {
1583a634bfcSVikram Hegde 		list_destroy(rmrr_list);
1593a634bfcSVikram Hegde 		return;
1603a634bfcSVikram Hegde 	}
1613a634bfcSVikram Hegde 
1623a634bfcSVikram Hegde 	while ((rmrr = list_remove_head(rmrr_list)) != NULL) {
1633a634bfcSVikram Hegde 		scope_list_free(&(rmrr->rm_scope_list));
1643a634bfcSVikram Hegde 		kmem_free(rmrr, sizeof (rmrr_t));
1653a634bfcSVikram Hegde 	}
1663a634bfcSVikram Hegde 
1673a634bfcSVikram Hegde 	ASSERT(list_is_empty(rmrr_list));
1683a634bfcSVikram Hegde 	list_destroy(rmrr_list);
1693a634bfcSVikram Hegde }
1703a634bfcSVikram Hegde 
1713a634bfcSVikram Hegde /*
1723a634bfcSVikram Hegde  * parse_scope()
1733a634bfcSVikram Hegde  *      parse a scope structure in the "raw" table
1743a634bfcSVikram Hegde  */
1753a634bfcSVikram Hegde static scope_t *
1763a634bfcSVikram Hegde parse_scope(char *shead)
1773a634bfcSVikram Hegde {
1783a634bfcSVikram Hegde 	scope_t *scope;
1793a634bfcSVikram Hegde 	char *phead;
1803a634bfcSVikram Hegde 	int bus, dev, func;
1813a634bfcSVikram Hegde 	uint8_t startbus;
1823a634bfcSVikram Hegde 	uint8_t len;
1833a634bfcSVikram Hegde 	int depth;
1843a634bfcSVikram Hegde 
1853a634bfcSVikram Hegde 	ASSERT(shead);
1863a634bfcSVikram Hegde 
1873a634bfcSVikram Hegde 	scope = kmem_zalloc(sizeof (scope_t), KM_SLEEP);
1883a634bfcSVikram Hegde 	scope->scp_type = get_uint8(&shead[0]);
1893a634bfcSVikram Hegde 	scope->scp_enumid = get_uint8(&shead[4]);
1903a634bfcSVikram Hegde 
1913a634bfcSVikram Hegde 	len = get_uint8(&shead[1]);
1923a634bfcSVikram Hegde 	startbus = get_uint8(&shead[5]);
1933a634bfcSVikram Hegde 	depth = (len - 6)/2;
1943a634bfcSVikram Hegde 	ASSERT(depth >= 1);
1953a634bfcSVikram Hegde 
1963a634bfcSVikram Hegde 	phead = &shead[6];
1973a634bfcSVikram Hegde 
1983a634bfcSVikram Hegde 	bus = startbus;
1993a634bfcSVikram Hegde 	dev = get_uint8(phead++);
2003a634bfcSVikram Hegde 	func = get_uint8(phead++);
2013a634bfcSVikram Hegde 
2023a634bfcSVikram Hegde 	for (depth--; depth > 0; depth--) {
2033a634bfcSVikram Hegde 		bus = pci_getb_func(bus, dev, func, PCI_BCNF_SECBUS);
2043a634bfcSVikram Hegde 		dev = get_uint8(phead++);
2053a634bfcSVikram Hegde 		func = get_uint8(phead++);
2063a634bfcSVikram Hegde 	}
2073a634bfcSVikram Hegde 
2083a634bfcSVikram Hegde 	ASSERT(bus >= 0 && bus < 256);
2093a634bfcSVikram Hegde 	ASSERT(dev >= 0 && dev < 32);
2103a634bfcSVikram Hegde 	ASSERT(func >= 0 && func < 8);
2113a634bfcSVikram Hegde 
2123a634bfcSVikram Hegde 	/* ok we got the device BDF */
2133a634bfcSVikram Hegde 	scope->scp_bus = bus;
2143a634bfcSVikram Hegde 	scope->scp_dev = dev;
2153a634bfcSVikram Hegde 	scope->scp_func = func;
2163a634bfcSVikram Hegde 
2173a634bfcSVikram Hegde 	return (scope);
2183a634bfcSVikram Hegde }
2193a634bfcSVikram Hegde 
2203a634bfcSVikram Hegde 
2213a634bfcSVikram Hegde /* setup the ioapic_drhd structure */
2223a634bfcSVikram Hegde static void
2233a634bfcSVikram Hegde ioapic_drhd_setup(void)
2243a634bfcSVikram Hegde {
2253a634bfcSVikram Hegde 	mutex_init(&(ioapic_drhd_lock), NULL, MUTEX_DEFAULT, NULL);
2263a634bfcSVikram Hegde 
2273a634bfcSVikram Hegde 	mutex_enter(&(ioapic_drhd_lock));
2283a634bfcSVikram Hegde 	list_create(&(ioapic_drhd_list), sizeof (ioapic_drhd_t),
2293a634bfcSVikram Hegde 	    offsetof(ioapic_drhd_t, ioapic_node));
2303a634bfcSVikram Hegde 	mutex_exit(&(ioapic_drhd_lock));
2313a634bfcSVikram Hegde }
2323a634bfcSVikram Hegde 
2333a634bfcSVikram Hegde /* get ioapic source id for interrupt remapping */
2343a634bfcSVikram Hegde static void
2353a634bfcSVikram Hegde ioapic_drhd_insert(scope_t *scope, drhd_t *drhd)
2363a634bfcSVikram Hegde {
2373a634bfcSVikram Hegde 	ioapic_drhd_t *idt;
2383a634bfcSVikram Hegde 
2393a634bfcSVikram Hegde 	idt = kmem_zalloc(sizeof (ioapic_drhd_t), KM_SLEEP);
2403a634bfcSVikram Hegde 	idt->ioapic_ioapicid = scope->scp_enumid;
2413a634bfcSVikram Hegde 	idt->ioapic_sid = ((scope->scp_bus << 8) | (scope->scp_dev << 3) |
2423a634bfcSVikram Hegde 	    (scope->scp_func));
2433a634bfcSVikram Hegde 	idt->ioapic_drhd = drhd;
2443a634bfcSVikram Hegde 
2453a634bfcSVikram Hegde 	mutex_enter(&ioapic_drhd_lock);
2463a634bfcSVikram Hegde 	list_insert_tail(&ioapic_drhd_list, idt);
2473a634bfcSVikram Hegde 	mutex_exit(&ioapic_drhd_lock);
2483a634bfcSVikram Hegde }
2493a634bfcSVikram Hegde 
2503a634bfcSVikram Hegde static ioapic_drhd_t *
2513a634bfcSVikram Hegde ioapic_drhd_lookup(int ioapicid)
2523a634bfcSVikram Hegde {
2533a634bfcSVikram Hegde 	ioapic_drhd_t *idt;
2543a634bfcSVikram Hegde 
2553a634bfcSVikram Hegde 	mutex_enter(&ioapic_drhd_lock);
2563a634bfcSVikram Hegde 	idt = list_head(&ioapic_drhd_list);
2573a634bfcSVikram Hegde 	for (; idt; idt = list_next(&ioapic_drhd_list, idt)) {
2583a634bfcSVikram Hegde 		if (idt->ioapic_ioapicid == ioapicid) {
2593a634bfcSVikram Hegde 			break;
2603a634bfcSVikram Hegde 		}
2613a634bfcSVikram Hegde 	}
2623a634bfcSVikram Hegde 	mutex_exit(&ioapic_drhd_lock);
2633a634bfcSVikram Hegde 
2643a634bfcSVikram Hegde 	return (idt);
2653a634bfcSVikram Hegde }
2663a634bfcSVikram Hegde 
2673a634bfcSVikram Hegde static void
2683a634bfcSVikram Hegde ioapic_drhd_destroy(void)
2693a634bfcSVikram Hegde {
2703a634bfcSVikram Hegde 	ioapic_drhd_t *idt;
2713a634bfcSVikram Hegde 
2723a634bfcSVikram Hegde 	mutex_enter(&ioapic_drhd_lock);
2733a634bfcSVikram Hegde 	while (idt = list_remove_head(&ioapic_drhd_list)) {
2743a634bfcSVikram Hegde 		kmem_free(idt, sizeof (ioapic_drhd_t));
2753a634bfcSVikram Hegde 	}
2763a634bfcSVikram Hegde 	list_destroy(&ioapic_drhd_list);
2773a634bfcSVikram Hegde 	mutex_exit(&(ioapic_drhd_lock));
2783a634bfcSVikram Hegde 
2793a634bfcSVikram Hegde 	mutex_destroy(&(ioapic_drhd_lock));
2803a634bfcSVikram Hegde }
2813a634bfcSVikram Hegde 
2823a634bfcSVikram Hegde /*
2833a634bfcSVikram Hegde  * parse_drhd()
2843a634bfcSVikram Hegde  *   parse the drhd uints in dmar table
2853a634bfcSVikram Hegde  */
2863a634bfcSVikram Hegde static int
2873a634bfcSVikram Hegde parse_drhd(char *uhead, dmar_table_t *tbl)
2883a634bfcSVikram Hegde {
2893a634bfcSVikram Hegde 	drhd_t *drhd;
2903a634bfcSVikram Hegde 	int seg;
2913a634bfcSVikram Hegde 	int len;
2923a634bfcSVikram Hegde 	char *shead;
2933a634bfcSVikram Hegde 	scope_t *scope;
2943a634bfcSVikram Hegde 
2953a634bfcSVikram Hegde 	ASSERT(uhead);
2963a634bfcSVikram Hegde 	ASSERT(tbl);
2973a634bfcSVikram Hegde 	ASSERT(get_uint16(&uhead[0]) == DMAR_DRHD);
2983a634bfcSVikram Hegde 
2993a634bfcSVikram Hegde 	seg = get_uint16(&uhead[6]);
3003a634bfcSVikram Hegde 	if (seg < 0 || seg >= IMMU_MAXSEG) {
3013a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "invalid segment# <%d>"
3023a634bfcSVikram Hegde 		    "in DRHD unit in ACPI DMAR table", seg);
3033a634bfcSVikram Hegde 		return (DDI_FAILURE);
3043a634bfcSVikram Hegde 	}
3053a634bfcSVikram Hegde 
3063a634bfcSVikram Hegde 	drhd = kmem_zalloc(sizeof (drhd_t), KM_SLEEP);
3073a634bfcSVikram Hegde 	mutex_init(&(drhd->dr_lock), NULL, MUTEX_DEFAULT, NULL);
3083a634bfcSVikram Hegde 	list_create(&(drhd->dr_scope_list), sizeof (scope_t),
3093a634bfcSVikram Hegde 	    offsetof(scope_t, scp_node));
3103a634bfcSVikram Hegde 
3113a634bfcSVikram Hegde 	len = get_uint16(&uhead[2]);
3123a634bfcSVikram Hegde 	drhd->dr_include_all =
3133a634bfcSVikram Hegde 	    (get_uint8(&uhead[4]) & DMAR_INCLUDE_ALL) ? B_TRUE : B_FALSE;
3143a634bfcSVikram Hegde 	drhd->dr_seg = seg;
3153a634bfcSVikram Hegde 	drhd->dr_regs = get_uint64(&uhead[8]);
3163a634bfcSVikram Hegde 
3173a634bfcSVikram Hegde 	/*
3183a634bfcSVikram Hegde 	 * parse each scope.
3193a634bfcSVikram Hegde 	 */
3203a634bfcSVikram Hegde 	shead = &uhead[16];
3213a634bfcSVikram Hegde 	while (shead < &uhead[len - 1]) {
3223a634bfcSVikram Hegde 		scope = parse_scope(shead);
3233a634bfcSVikram Hegde 		if (scope == NULL) {
3243a634bfcSVikram Hegde 			return (DDI_FAILURE);
3253a634bfcSVikram Hegde 		}
3263a634bfcSVikram Hegde 
3273a634bfcSVikram Hegde 		if (scope->scp_type == DMAR_IOAPIC)  {
3283a634bfcSVikram Hegde 			ioapic_drhd_insert(scope, drhd);
3293a634bfcSVikram Hegde 		}
3303a634bfcSVikram Hegde 
3313a634bfcSVikram Hegde 		list_insert_tail(&(drhd->dr_scope_list), scope);
3323a634bfcSVikram Hegde 		shead += get_uint8(&shead[1]);
3333a634bfcSVikram Hegde 	}
3343a634bfcSVikram Hegde 
3353a634bfcSVikram Hegde 	list_insert_tail(&(tbl->tbl_drhd_list[drhd->dr_seg]), drhd);
3363a634bfcSVikram Hegde 
3373a634bfcSVikram Hegde 	return (DDI_SUCCESS);
3383a634bfcSVikram Hegde }
3393a634bfcSVikram Hegde 
3403a634bfcSVikram Hegde /*
3413a634bfcSVikram Hegde  * parse_rmrr()
3423a634bfcSVikram Hegde  *   parse the rmrr units in dmar table
3433a634bfcSVikram Hegde  */
3443a634bfcSVikram Hegde static int
3453a634bfcSVikram Hegde parse_rmrr(char *uhead, dmar_table_t *tbl)
3463a634bfcSVikram Hegde {
3473a634bfcSVikram Hegde 	rmrr_t *rmrr;
3483a634bfcSVikram Hegde 	int seg;
3493a634bfcSVikram Hegde 	int len;
3503a634bfcSVikram Hegde 	char *shead;
3513a634bfcSVikram Hegde 	scope_t *scope;
3523a634bfcSVikram Hegde 
3533a634bfcSVikram Hegde 	ASSERT(uhead);
3543a634bfcSVikram Hegde 	ASSERT(tbl);
3553a634bfcSVikram Hegde 	ASSERT(get_uint16(&uhead[0]) == DMAR_RMRR);
3563a634bfcSVikram Hegde 
3573a634bfcSVikram Hegde 	seg = get_uint16(&uhead[6]);
3583a634bfcSVikram Hegde 	if (seg < 0 || seg >= IMMU_MAXSEG) {
3593a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "invalid segment# <%d>"
3603a634bfcSVikram Hegde 		    "in RMRR unit in ACPI DMAR table", seg);
3613a634bfcSVikram Hegde 		return (DDI_FAILURE);
3623a634bfcSVikram Hegde 	}
3633a634bfcSVikram Hegde 
3643a634bfcSVikram Hegde 	rmrr = kmem_zalloc(sizeof (rmrr_t), KM_SLEEP);
3653a634bfcSVikram Hegde 	mutex_init(&(rmrr->rm_lock), NULL, MUTEX_DEFAULT, NULL);
3663a634bfcSVikram Hegde 	list_create(&(rmrr->rm_scope_list), sizeof (scope_t),
3673a634bfcSVikram Hegde 	    offsetof(scope_t, scp_node));
3683a634bfcSVikram Hegde 
3693a634bfcSVikram Hegde 	/* RMRR region is [base,limit] */
3703a634bfcSVikram Hegde 	len = get_uint16(&uhead[2]);
3713a634bfcSVikram Hegde 	rmrr->rm_seg = get_uint16(&uhead[6]);
3723a634bfcSVikram Hegde 	rmrr->rm_base = get_uint64(&uhead[8]);
3733a634bfcSVikram Hegde 	rmrr->rm_limit = get_uint64(&uhead[16]);
3743a634bfcSVikram Hegde 
3753a634bfcSVikram Hegde 	if (rmrr->rm_base > rmrr->rm_limit) {
3763a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "IMMU: BIOS bug detected: "
3773a634bfcSVikram Hegde 		    "RMRR: base (%lx) > limit (%lx)",
3783a634bfcSVikram Hegde 		    rmrr->rm_base, rmrr->rm_limit);
3793a634bfcSVikram Hegde 		list_destroy(&(rmrr->rm_scope_list));
3803a634bfcSVikram Hegde 		mutex_destroy(&(rmrr->rm_lock));
3813a634bfcSVikram Hegde 		kmem_free(rmrr, sizeof (rmrr_t));
3823a634bfcSVikram Hegde 		return (DDI_SUCCESS);
3833a634bfcSVikram Hegde 	}
3843a634bfcSVikram Hegde 
3853a634bfcSVikram Hegde 	/*
3863a634bfcSVikram Hegde 	 * parse each scope in RMRR
3873a634bfcSVikram Hegde 	 */
3883a634bfcSVikram Hegde 	shead = &uhead[24];
3893a634bfcSVikram Hegde 	while (shead < &uhead[len - 1]) {
3903a634bfcSVikram Hegde 		scope = parse_scope(shead);
3913a634bfcSVikram Hegde 		if (scope == NULL) {
3923a634bfcSVikram Hegde 			return (DDI_FAILURE);
3933a634bfcSVikram Hegde 		}
3943a634bfcSVikram Hegde 		list_insert_tail(&(rmrr->rm_scope_list), scope);
3953a634bfcSVikram Hegde 		shead += get_uint8(&shead[1]);
3963a634bfcSVikram Hegde 	}
3973a634bfcSVikram Hegde 
3983a634bfcSVikram Hegde 	list_insert_tail(&(tbl->tbl_rmrr_list[rmrr->rm_seg]), rmrr);
3993a634bfcSVikram Hegde 
4003a634bfcSVikram Hegde 	return (DDI_SUCCESS);
4013a634bfcSVikram Hegde }
4023a634bfcSVikram Hegde 
4033a634bfcSVikram Hegde #define	TBL_OEM_ID_SZ		(6)
4043a634bfcSVikram Hegde #define	TBL_OEM_TBLID_SZ	(8)
4053a634bfcSVikram Hegde 
4063a634bfcSVikram Hegde /*
4073a634bfcSVikram Hegde  * parse the "raw" DMAR table and convert it
4083a634bfcSVikram Hegde  * into a useful form.
4093a634bfcSVikram Hegde  */
4103a634bfcSVikram Hegde static int
4113a634bfcSVikram Hegde dmar_parse(dmar_table_t **tblpp, char *raw)
4123a634bfcSVikram Hegde {
4133a634bfcSVikram Hegde 	char *uhead;
4143a634bfcSVikram Hegde 	dmar_table_t *tbl;
4153a634bfcSVikram Hegde 	int i;
4163a634bfcSVikram Hegde 	char *unmstr;
4173a634bfcSVikram Hegde 
4183a634bfcSVikram Hegde 	ASSERT(raw);
4193a634bfcSVikram Hegde 	ASSERT(tblpp);
4203a634bfcSVikram Hegde 
4213a634bfcSVikram Hegde 	*tblpp = NULL;
4223a634bfcSVikram Hegde 
4233a634bfcSVikram Hegde 	/*
4243a634bfcSVikram Hegde 	 * do a sanity check. make sure the raw table
4253a634bfcSVikram Hegde 	 * has the right signature
4263a634bfcSVikram Hegde 	 */
4273a634bfcSVikram Hegde 	if (raw[0] != 'D' || raw[1] != 'M' ||
4283a634bfcSVikram Hegde 	    raw[2] != 'A' || raw[3] != 'R') {
4293a634bfcSVikram Hegde 		ddi_err(DER_WARN, NULL, "IOMMU ACPI "
4303a634bfcSVikram Hegde 		    "signature != \"DMAR\"");
4313a634bfcSVikram Hegde 		return (DDI_FAILURE);
4323a634bfcSVikram Hegde 	}
4333a634bfcSVikram Hegde 
4343a634bfcSVikram Hegde 	/*
4353a634bfcSVikram Hegde 	 * the platform has intel iommu, create processed ACPI struct
4363a634bfcSVikram Hegde 	 */
4373a634bfcSVikram Hegde 	tbl = kmem_zalloc(sizeof (dmar_table_t), KM_SLEEP);
4383a634bfcSVikram Hegde 	mutex_init(&(tbl->tbl_lock), NULL, MUTEX_DEFAULT, NULL);
4393a634bfcSVikram Hegde 
4403a634bfcSVikram Hegde 	tbl->tbl_raw = raw;
4413a634bfcSVikram Hegde 
4423a634bfcSVikram Hegde 	/*
4433a634bfcSVikram Hegde 	 * Note we explicitly show offsets for clarity
4443a634bfcSVikram Hegde 	 */
4453a634bfcSVikram Hegde 	tbl->tbl_rawlen = get_uint32(&raw[4]);
4463a634bfcSVikram Hegde 
4473a634bfcSVikram Hegde 	/* XXX TO DO verify checksum of table */
4483a634bfcSVikram Hegde 	tbl->tbl_oem_id = get_str(&raw[10], TBL_OEM_ID_SZ);
4493a634bfcSVikram Hegde 	tbl->tbl_oem_tblid = get_str(&raw[16], TBL_OEM_TBLID_SZ);
4503a634bfcSVikram Hegde 	tbl->tbl_oem_rev = get_uint32(&raw[24]);
4513a634bfcSVikram Hegde 	tbl->tbl_haw = get_uint8(&raw[36]) + 1;
4523a634bfcSVikram Hegde 	tbl->tbl_intrmap = (get_uint8(&raw[37]) & DMAR_INTRMAP_SUPPORT)
4533a634bfcSVikram Hegde 	    ? B_TRUE : B_FALSE;
4543a634bfcSVikram Hegde 
4553a634bfcSVikram Hegde 	/* create lists for DRHD and RMRR */
4563a634bfcSVikram Hegde 	for (i = 0; i < IMMU_MAXSEG; i++) {
4573a634bfcSVikram Hegde 		list_create(&(tbl->tbl_drhd_list[i]), sizeof (drhd_t),
4583a634bfcSVikram Hegde 		    offsetof(drhd_t, dr_node));
4593a634bfcSVikram Hegde 		list_create(&(tbl->tbl_rmrr_list[i]), sizeof (rmrr_t),
4603a634bfcSVikram Hegde 		    offsetof(rmrr_t, rm_node));
4613a634bfcSVikram Hegde 	}
4623a634bfcSVikram Hegde 
4633a634bfcSVikram Hegde 	ioapic_drhd_setup();
4643a634bfcSVikram Hegde 
4653a634bfcSVikram Hegde 	/*
4663a634bfcSVikram Hegde 	 * parse each unit. Currently only DRHD and RMRR types
4673a634bfcSVikram Hegde 	 * are parsed. We ignore all other types of units.
4683a634bfcSVikram Hegde 	 */
4693a634bfcSVikram Hegde 	uhead = &raw[48];
4703a634bfcSVikram Hegde 	while (uhead < &raw[tbl->tbl_rawlen - 1]) {
4713a634bfcSVikram Hegde 		unmstr = NULL;
4723a634bfcSVikram Hegde 		switch (get_uint16(uhead)) {
4733a634bfcSVikram Hegde 		case DMAR_DRHD:
4743a634bfcSVikram Hegde 			if (parse_drhd(uhead, tbl) != DDI_SUCCESS) {
4753a634bfcSVikram Hegde 				goto failed;
4763a634bfcSVikram Hegde 			}
4773a634bfcSVikram Hegde 			break;
4783a634bfcSVikram Hegde 		case DMAR_RMRR:
4793a634bfcSVikram Hegde 			if (parse_rmrr(uhead, tbl) != DDI_SUCCESS) {
4803a634bfcSVikram Hegde 				goto failed;
4813a634bfcSVikram Hegde 			}
4823a634bfcSVikram Hegde 			break;
4833a634bfcSVikram Hegde 		case DMAR_ATSR:
4843a634bfcSVikram Hegde 			unmstr = "ATSR";
4853a634bfcSVikram Hegde 			break;
4863a634bfcSVikram Hegde 		case DMAR_RHSA:
4873a634bfcSVikram Hegde 			unmstr = "RHSA";
4883a634bfcSVikram Hegde 			break;
4893a634bfcSVikram Hegde 		default:
4903a634bfcSVikram Hegde 			unmstr = "unknown unity type";
4913a634bfcSVikram Hegde 			break;
4923a634bfcSVikram Hegde 		}
4933a634bfcSVikram Hegde 		if (unmstr) {
4943a634bfcSVikram Hegde 			ddi_err(DER_NOTE, NULL, "DMAR ACPI table: "
4953a634bfcSVikram Hegde 			    "skipping unsupported unit type %s", unmstr);
4963a634bfcSVikram Hegde 		}
4973a634bfcSVikram Hegde 		uhead += get_uint16(&uhead[2]);
4983a634bfcSVikram Hegde 	}
4993a634bfcSVikram Hegde 
5003a634bfcSVikram Hegde 	*tblpp = tbl;
5013a634bfcSVikram Hegde 	return (DDI_SUCCESS);
5023a634bfcSVikram Hegde 
5033a634bfcSVikram Hegde failed:
5043a634bfcSVikram Hegde 	dmar_table_destroy(tbl);
5053a634bfcSVikram Hegde 	return (DDI_FAILURE);
5063a634bfcSVikram Hegde }
5073a634bfcSVikram Hegde 
5083a634bfcSVikram Hegde static char *
5093a634bfcSVikram Hegde scope_type(int devtype)
5103a634bfcSVikram Hegde {
5113a634bfcSVikram Hegde 	char *typestr;
5123a634bfcSVikram Hegde 
5133a634bfcSVikram Hegde 	switch (devtype) {
5143a634bfcSVikram Hegde 	case DMAR_ENDPOINT:
5153a634bfcSVikram Hegde 		typestr = "endpoint-device";
5163a634bfcSVikram Hegde 		break;
5173a634bfcSVikram Hegde 	case DMAR_SUBTREE:
5183a634bfcSVikram Hegde 		typestr = "subtree-device";
5193a634bfcSVikram Hegde 		break;
5203a634bfcSVikram Hegde 	case DMAR_IOAPIC:
5213a634bfcSVikram Hegde 		typestr = "IOAPIC";
5223a634bfcSVikram Hegde 		break;
5233a634bfcSVikram Hegde 	case DMAR_HPET:
5243a634bfcSVikram Hegde 		typestr = "HPET";
5253a634bfcSVikram Hegde 		break;
5263a634bfcSVikram Hegde 	default:
5273a634bfcSVikram Hegde 		typestr = "Unknown device";
5283a634bfcSVikram Hegde 		break;
5293a634bfcSVikram Hegde 	}
5303a634bfcSVikram Hegde 
5313a634bfcSVikram Hegde 	return (typestr);
5323a634bfcSVikram Hegde }
5333a634bfcSVikram Hegde 
5343a634bfcSVikram Hegde static void
5353a634bfcSVikram Hegde print_scope_list(list_t *scope_list)
5363a634bfcSVikram Hegde {
5373a634bfcSVikram Hegde 	scope_t *scope;
5383a634bfcSVikram Hegde 
5393a634bfcSVikram Hegde 	if (list_is_empty(scope_list))
5403a634bfcSVikram Hegde 		return;
5413a634bfcSVikram Hegde 
5423a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "\tdevice list:\n");
5433a634bfcSVikram Hegde 
5443a634bfcSVikram Hegde 	for (scope = list_head(scope_list); scope;
5453a634bfcSVikram Hegde 	    scope = list_next(scope_list, scope)) {
5463a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\t\ttype = %s\n",
5473a634bfcSVikram Hegde 		    scope_type(scope->scp_type));
5483a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\n\t\tbus = %d\n",
5493a634bfcSVikram Hegde 		    scope->scp_bus);
5503a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\t\tdev = %d\n",
5513a634bfcSVikram Hegde 		    scope->scp_dev);
5523a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\t\tfunc = %d\n",
5533a634bfcSVikram Hegde 		    scope->scp_func);
5543a634bfcSVikram Hegde 	}
5553a634bfcSVikram Hegde }
5563a634bfcSVikram Hegde 
5573a634bfcSVikram Hegde static void
5583a634bfcSVikram Hegde print_drhd_list(list_t *drhd_list)
5593a634bfcSVikram Hegde {
5603a634bfcSVikram Hegde 	drhd_t *drhd;
5613a634bfcSVikram Hegde 
5623a634bfcSVikram Hegde 	if (list_is_empty(drhd_list))
5633a634bfcSVikram Hegde 		return;
5643a634bfcSVikram Hegde 
5653a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "\ndrhd list:\n");
5663a634bfcSVikram Hegde 
5673a634bfcSVikram Hegde 	for (drhd = list_head(drhd_list); drhd;
5683a634bfcSVikram Hegde 	    drhd = list_next(drhd_list, drhd)) {
5693a634bfcSVikram Hegde 
5703a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\n\tsegment = %d\n",
5713a634bfcSVikram Hegde 		    drhd->dr_seg);
5723a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\treg_base = 0x%" PRIx64 "\n",
5733a634bfcSVikram Hegde 		    drhd->dr_regs);
5743a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\tinclude_all = %s\n",
5753a634bfcSVikram Hegde 		    drhd->dr_include_all == B_TRUE ? "TRUE" : "FALSE");
5763a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\tdip = 0x%p\n",
5773a634bfcSVikram Hegde 		    (void *)drhd->dr_dip);
5783a634bfcSVikram Hegde 
5793a634bfcSVikram Hegde 		print_scope_list(&(drhd->dr_scope_list));
5803a634bfcSVikram Hegde 	}
5813a634bfcSVikram Hegde }
5823a634bfcSVikram Hegde 
5833a634bfcSVikram Hegde 
5843a634bfcSVikram Hegde static void
5853a634bfcSVikram Hegde print_rmrr_list(list_t *rmrr_list)
5863a634bfcSVikram Hegde {
5873a634bfcSVikram Hegde 	rmrr_t *rmrr;
5883a634bfcSVikram Hegde 
5893a634bfcSVikram Hegde 	if (list_is_empty(rmrr_list))
5903a634bfcSVikram Hegde 		return;
5913a634bfcSVikram Hegde 
5923a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "\nrmrr list:\n");
5933a634bfcSVikram Hegde 
5943a634bfcSVikram Hegde 	for (rmrr = list_head(rmrr_list); rmrr;
5953a634bfcSVikram Hegde 	    rmrr = list_next(rmrr_list, rmrr)) {
5963a634bfcSVikram Hegde 
5973a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\n\tsegment = %d\n",
5983a634bfcSVikram Hegde 		    rmrr->rm_seg);
5993a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\tbase = 0x%lx\n",
6003a634bfcSVikram Hegde 		    rmrr->rm_base);
6013a634bfcSVikram Hegde 		ddi_err(DER_CONT, NULL, "\tlimit = 0x%lx\n",
6023a634bfcSVikram Hegde 		    rmrr->rm_limit);
6033a634bfcSVikram Hegde 
6043a634bfcSVikram Hegde 		print_scope_list(&(rmrr->rm_scope_list));
6053a634bfcSVikram Hegde 	}
6063a634bfcSVikram Hegde }
6073a634bfcSVikram Hegde 
6083a634bfcSVikram Hegde /*
6093a634bfcSVikram Hegde  * print DMAR table
6103a634bfcSVikram Hegde  */
6113a634bfcSVikram Hegde static void
6123a634bfcSVikram Hegde dmar_table_print(dmar_table_t *tbl)
6133a634bfcSVikram Hegde {
6143a634bfcSVikram Hegde 	int i;
6153a634bfcSVikram Hegde 
6163a634bfcSVikram Hegde 	if (dmar_print == B_FALSE) {
6173a634bfcSVikram Hegde 		return;
6183a634bfcSVikram Hegde 	}
6193a634bfcSVikram Hegde 
6203a634bfcSVikram Hegde 	/* print the title */
6213a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "#### Start of dmar_table ####\n");
6223a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "\thaw = %d\n", tbl->tbl_haw);
6233a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "\tintr_remap = %s\n",
6243a634bfcSVikram Hegde 	    tbl->tbl_intrmap == B_TRUE ? "<true>" : "<false>");
6253a634bfcSVikram Hegde 
6263a634bfcSVikram Hegde 	/* print drhd list */
6273a634bfcSVikram Hegde 	for (i = 0; i < IMMU_MAXSEG; i++) {
6283a634bfcSVikram Hegde 		print_drhd_list(&(tbl->tbl_drhd_list[i]));
6293a634bfcSVikram Hegde 	}
6303a634bfcSVikram Hegde 
6313a634bfcSVikram Hegde 
6323a634bfcSVikram Hegde 	/* print rmrr list */
6333a634bfcSVikram Hegde 	for (i = 0; i < IMMU_MAXSEG; i++) {
6343a634bfcSVikram Hegde 		print_rmrr_list(&(tbl->tbl_rmrr_list[i]));
6353a634bfcSVikram Hegde 	}
6363a634bfcSVikram Hegde 
6373a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "#### END of dmar_table ####\n");
6383a634bfcSVikram Hegde }
6393a634bfcSVikram Hegde 
6403a634bfcSVikram Hegde static void
641*50200e77SFrank Van Der Linden drhd_devi_create(drhd_t *drhd, int unit)
6423a634bfcSVikram Hegde {
6433a634bfcSVikram Hegde 	struct ddi_parent_private_data *pdptr;
6443a634bfcSVikram Hegde 	struct regspec reg;
6453a634bfcSVikram Hegde 	dev_info_t *dip;
6463a634bfcSVikram Hegde 
647*50200e77SFrank Van Der Linden 	dip = ddi_add_child(root_devinfo, IMMU_UNIT_NAME,
648*50200e77SFrank Van Der Linden 	    DEVI_SID_NODEID, unit);
6493a634bfcSVikram Hegde 
6503a634bfcSVikram Hegde 	drhd->dr_dip = dip;
6513a634bfcSVikram Hegde 
6523a634bfcSVikram Hegde 	reg.regspec_bustype = 0;
6533a634bfcSVikram Hegde 	reg.regspec_addr = drhd->dr_regs;
6543a634bfcSVikram Hegde 	reg.regspec_size = IMMU_REGSZ;
6553a634bfcSVikram Hegde 
6563a634bfcSVikram Hegde 	/*
6573a634bfcSVikram Hegde 	 * update the reg properties
6583a634bfcSVikram Hegde 	 *
6593a634bfcSVikram Hegde 	 *   reg property will be used for register
6603a634bfcSVikram Hegde 	 *   set access
6613a634bfcSVikram Hegde 	 *
6623a634bfcSVikram Hegde 	 * refer to the bus_map of root nexus driver
6633a634bfcSVikram Hegde 	 * I/O or memory mapping:
6643a634bfcSVikram Hegde 	 *
6653a634bfcSVikram Hegde 	 * <bustype=0, addr=x, len=x>: memory
6663a634bfcSVikram Hegde 	 * <bustype=1, addr=x, len=x>: i/o
6673a634bfcSVikram Hegde 	 * <bustype>1, addr=0, len=x>: x86-compatibility i/o
6683a634bfcSVikram Hegde 	 */
6693a634bfcSVikram Hegde 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE,
6703a634bfcSVikram Hegde 	    dip, "reg", (int *)&reg,
6713a634bfcSVikram Hegde 	    sizeof (struct regspec) / sizeof (int));
6723a634bfcSVikram Hegde 
673c94adbf9SFrank Van Der Linden 	/*
674c94adbf9SFrank Van Der Linden 	 * This is an artificially constructed dev_info, and we
675c94adbf9SFrank Van Der Linden 	 * need to set a few more things to be able to use it
676c94adbf9SFrank Van Der Linden 	 * for ddi_dma_alloc_handle/free_handle.
677c94adbf9SFrank Van Der Linden 	 */
678c94adbf9SFrank Van Der Linden 	ddi_set_driver(dip, ddi_get_driver(ddi_root_node()));
679c94adbf9SFrank Van Der Linden 	DEVI(dip)->devi_bus_dma_allochdl =
680c94adbf9SFrank Van Der Linden 	    DEVI(ddi_get_driver((ddi_root_node())));
6813a634bfcSVikram Hegde 
6823a634bfcSVikram Hegde 	pdptr = kmem_zalloc(sizeof (struct ddi_parent_private_data)
6833a634bfcSVikram Hegde 	    + sizeof (struct regspec), KM_SLEEP);
6843a634bfcSVikram Hegde 	pdptr->par_nreg = 1;
6853a634bfcSVikram Hegde 	pdptr->par_reg = (struct regspec *)(pdptr + 1);
6863a634bfcSVikram Hegde 	pdptr->par_reg->regspec_bustype = 0;
6873a634bfcSVikram Hegde 	pdptr->par_reg->regspec_addr = drhd->dr_regs;
6883a634bfcSVikram Hegde 	pdptr->par_reg->regspec_size = IMMU_REGSZ;
6893a634bfcSVikram Hegde 	ddi_set_parent_data(dip, pdptr);
6903a634bfcSVikram Hegde }
6913a634bfcSVikram Hegde 
6923a634bfcSVikram Hegde /*
6933a634bfcSVikram Hegde  * dmar_devinfos_create()
6943a634bfcSVikram Hegde  *
6953a634bfcSVikram Hegde  *   create the dev_info node in the device tree,
6963a634bfcSVikram Hegde  *   the info node is a nuxus child of the root
6973a634bfcSVikram Hegde  *   nexus
6983a634bfcSVikram Hegde  */
6993a634bfcSVikram Hegde static void
7003a634bfcSVikram Hegde dmar_devinfos_create(dmar_table_t *tbl)
7013a634bfcSVikram Hegde {
7023a634bfcSVikram Hegde 	list_t *drhd_list;
7033a634bfcSVikram Hegde 	drhd_t *drhd;
7043a634bfcSVikram Hegde 	int i, unit;
7053a634bfcSVikram Hegde 
7063a634bfcSVikram Hegde 	for (i = 0; i < IMMU_MAXSEG; i++) {
7073a634bfcSVikram Hegde 
7083a634bfcSVikram Hegde 		drhd_list = &(tbl->tbl_drhd_list[i]);
7093a634bfcSVikram Hegde 
7103a634bfcSVikram Hegde 		if (list_is_empty(drhd_list))
7113a634bfcSVikram Hegde 			continue;
7123a634bfcSVikram Hegde 
7133a634bfcSVikram Hegde 		drhd = list_head(drhd_list);
7143a634bfcSVikram Hegde 		for (unit = 0; drhd;
7153a634bfcSVikram Hegde 		    drhd = list_next(drhd_list, drhd), unit++) {
716*50200e77SFrank Van Der Linden 			drhd_devi_create(drhd, unit);
7173a634bfcSVikram Hegde 		}
7183a634bfcSVikram Hegde 	}
7193a634bfcSVikram Hegde }
7203a634bfcSVikram Hegde 
7213a634bfcSVikram Hegde static void
7223a634bfcSVikram Hegde drhd_devi_destroy(drhd_t *drhd)
7233a634bfcSVikram Hegde {
7243a634bfcSVikram Hegde 	dev_info_t *dip;
7253a634bfcSVikram Hegde 	int count;
7263a634bfcSVikram Hegde 
7273a634bfcSVikram Hegde 	dip = drhd->dr_dip;
7283a634bfcSVikram Hegde 	ASSERT(dip);
7293a634bfcSVikram Hegde 
7303a634bfcSVikram Hegde 	ndi_devi_enter(root_devinfo, &count);
7313a634bfcSVikram Hegde 	if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != DDI_SUCCESS) {
7323a634bfcSVikram Hegde 		ddi_err(DER_WARN, dip, "Failed to destroy");
7333a634bfcSVikram Hegde 	}
7343a634bfcSVikram Hegde 	ndi_devi_exit(root_devinfo, count);
7353a634bfcSVikram Hegde 	drhd->dr_dip = NULL;
7363a634bfcSVikram Hegde }
7373a634bfcSVikram Hegde 
7383a634bfcSVikram Hegde /*
7393a634bfcSVikram Hegde  * dmar_devi_destroy()
7403a634bfcSVikram Hegde  *
7413a634bfcSVikram Hegde  * destroy dev_info nodes for all drhd units
7423a634bfcSVikram Hegde  */
7433a634bfcSVikram Hegde static void
7443a634bfcSVikram Hegde dmar_devi_destroy(dmar_table_t *tbl)
7453a634bfcSVikram Hegde {
7463a634bfcSVikram Hegde 	drhd_t *drhd;
7473a634bfcSVikram Hegde 	list_t *drhd_list;
7483a634bfcSVikram Hegde 	int i;
7493a634bfcSVikram Hegde 
7503a634bfcSVikram Hegde 	for (i = 0; i < IMMU_MAXSEG; i++) {
7513a634bfcSVikram Hegde 		drhd_list = &(tbl->tbl_drhd_list[i]);
7523a634bfcSVikram Hegde 		if (list_is_empty(drhd_list))
7533a634bfcSVikram Hegde 			continue;
7543a634bfcSVikram Hegde 
7553a634bfcSVikram Hegde 		drhd = list_head(drhd_list);
7563a634bfcSVikram Hegde 		for (; drhd; drhd = list_next(drhd_list, drhd)) {
7573a634bfcSVikram Hegde 			drhd_devi_destroy(drhd);
7583a634bfcSVikram Hegde 		}
7593a634bfcSVikram Hegde 	}
7603a634bfcSVikram Hegde }
7613a634bfcSVikram Hegde 
7623a634bfcSVikram Hegde static int
7633a634bfcSVikram Hegde match_bdf(dev_info_t *ddip, void *arg)
7643a634bfcSVikram Hegde {
7653a634bfcSVikram Hegde 	immu_arg_t *imarg = (immu_arg_t *)arg;
7663a634bfcSVikram Hegde 	immu_devi_t *immu_devi;
7673a634bfcSVikram Hegde 
7683a634bfcSVikram Hegde 	ASSERT(ddip);
7693a634bfcSVikram Hegde 	ASSERT(imarg);
7703a634bfcSVikram Hegde 	ASSERT(imarg->ima_seg == 0);
7713a634bfcSVikram Hegde 	ASSERT(imarg->ima_bus >= 0);
7723a634bfcSVikram Hegde 	ASSERT(imarg->ima_devfunc >= 0);
7733a634bfcSVikram Hegde 	ASSERT(imarg->ima_ddip == NULL);
7743a634bfcSVikram Hegde 
7753a634bfcSVikram Hegde 	/* rdip can be NULL */
7763a634bfcSVikram Hegde 
7773a634bfcSVikram Hegde 	mutex_enter(&(DEVI(ddip)->devi_lock));
7783a634bfcSVikram Hegde 
7793a634bfcSVikram Hegde 	immu_devi = IMMU_DEVI(ddip);
7803a634bfcSVikram Hegde 	ASSERT(immu_devi);
7813a634bfcSVikram Hegde 
7823a634bfcSVikram Hegde 	if (immu_devi->imd_seg == imarg->ima_seg &&
7833a634bfcSVikram Hegde 	    immu_devi->imd_bus == imarg->ima_bus &&
7843a634bfcSVikram Hegde 	    immu_devi->imd_devfunc == imarg->ima_devfunc) {
7853a634bfcSVikram Hegde 		imarg->ima_ddip = ddip;
7863a634bfcSVikram Hegde 	}
7873a634bfcSVikram Hegde 
7883a634bfcSVikram Hegde 	mutex_exit(&(DEVI(ddip)->devi_lock));
7893a634bfcSVikram Hegde 
7903a634bfcSVikram Hegde 	return (imarg->ima_ddip ? DDI_WALK_TERMINATE : DDI_WALK_CONTINUE);
7913a634bfcSVikram Hegde }
7923a634bfcSVikram Hegde static void
7933a634bfcSVikram Hegde dmar_table_destroy(dmar_table_t *tbl)
7943a634bfcSVikram Hegde {
7953a634bfcSVikram Hegde 	int i;
7963a634bfcSVikram Hegde 
7973a634bfcSVikram Hegde 	ASSERT(tbl);
7983a634bfcSVikram Hegde 
7993a634bfcSVikram Hegde 	/* destroy lists for DRHD and RMRR */
8003a634bfcSVikram Hegde 	for (i = 0; i < IMMU_MAXSEG; i++) {
8013a634bfcSVikram Hegde 		rmrr_list_destroy(&(tbl->tbl_rmrr_list[i]));
8023a634bfcSVikram Hegde 		drhd_list_destroy(&(tbl->tbl_drhd_list[i]));
8033a634bfcSVikram Hegde 	}
8043a634bfcSVikram Hegde 
8053a634bfcSVikram Hegde 	/* free strings */
806be56ae36SFrank Van Der Linden 	kmem_free(tbl->tbl_oem_tblid, TBL_OEM_TBLID_SZ + 1);
807be56ae36SFrank Van Der Linden 	kmem_free(tbl->tbl_oem_id, TBL_OEM_ID_SZ + 1);
8083a634bfcSVikram Hegde 	tbl->tbl_raw = NULL; /* raw ACPI table doesn't have to be freed */
8093a634bfcSVikram Hegde 	mutex_destroy(&(tbl->tbl_lock));
8103a634bfcSVikram Hegde 	kmem_free(tbl, sizeof (dmar_table_t));
8113a634bfcSVikram Hegde }
8123a634bfcSVikram Hegde 
8133a634bfcSVikram Hegde /*
8143a634bfcSVikram Hegde  * #########################################################################
8153a634bfcSVikram Hegde  * Functions exported by dmar.c
8163a634bfcSVikram Hegde  * This file deals with reading and processing the DMAR ACPI table
8173a634bfcSVikram Hegde  * #########################################################################
8183a634bfcSVikram Hegde  */
8193a634bfcSVikram Hegde 
8203a634bfcSVikram Hegde /*
8213a634bfcSVikram Hegde  * immu_dmar_setup()
8223a634bfcSVikram Hegde  *	Check if the system has a DMAR ACPI table. If yes, the system
8233a634bfcSVikram Hegde  *	has Intel IOMMU hardware
8243a634bfcSVikram Hegde  */
8253a634bfcSVikram Hegde int
8263a634bfcSVikram Hegde immu_dmar_setup(void)
8273a634bfcSVikram Hegde {
8283a634bfcSVikram Hegde 	if (AcpiGetTable("DMAR", 1, (ACPI_TABLE_HEADER **)&dmar_raw) != AE_OK) {
8293a634bfcSVikram Hegde 		ddi_err(DER_LOG, NULL,
8303a634bfcSVikram Hegde 		    "No DMAR ACPI table. No Intel IOMMU present\n");
8313a634bfcSVikram Hegde 		dmar_raw = NULL;
8323a634bfcSVikram Hegde 		return (DDI_FAILURE);
8333a634bfcSVikram Hegde 	}
8343a634bfcSVikram Hegde 	ASSERT(dmar_raw);
8353a634bfcSVikram Hegde 	return (DDI_SUCCESS);
8363a634bfcSVikram Hegde }
8373a634bfcSVikram Hegde 
8383a634bfcSVikram Hegde /*
8393a634bfcSVikram Hegde  * immu_dmar_parse()
8403a634bfcSVikram Hegde  *  Called by immu.c to parse and convert "raw" ACPI DMAR table
8413a634bfcSVikram Hegde  */
8423a634bfcSVikram Hegde int
8433a634bfcSVikram Hegde immu_dmar_parse(void)
8443a634bfcSVikram Hegde {
8453a634bfcSVikram Hegde 	dmar_table_t *tbl = NULL;
8463a634bfcSVikram Hegde 
8473a634bfcSVikram Hegde 	/* we should already have found the "raw" table */
8483a634bfcSVikram Hegde 	ASSERT(dmar_raw);
8493a634bfcSVikram Hegde 
8503a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Processing DMAR ACPI table\n");
8513a634bfcSVikram Hegde 
8523a634bfcSVikram Hegde 	dmar_table = NULL;
8533a634bfcSVikram Hegde 
8543a634bfcSVikram Hegde 	/*
8553a634bfcSVikram Hegde 	 * parse DMAR ACPI table
8563a634bfcSVikram Hegde 	 */
8573a634bfcSVikram Hegde 	if (dmar_parse(&tbl, dmar_raw) != DDI_SUCCESS) {
8583a634bfcSVikram Hegde 		ASSERT(tbl == NULL);
8593a634bfcSVikram Hegde 		return (DDI_FAILURE);
8603a634bfcSVikram Hegde 	}
8613a634bfcSVikram Hegde 
8623a634bfcSVikram Hegde 	ASSERT(tbl);
8633a634bfcSVikram Hegde 
8643a634bfcSVikram Hegde 	/*
8653a634bfcSVikram Hegde 	 * create one devinfo for every drhd unit
8663a634bfcSVikram Hegde 	 * in the DMAR table
8673a634bfcSVikram Hegde 	 */
8683a634bfcSVikram Hegde 	dmar_devinfos_create(tbl);
8693a634bfcSVikram Hegde 
8703a634bfcSVikram Hegde 	/*
8713a634bfcSVikram Hegde 	 * print the dmar table if the debug option is set
8723a634bfcSVikram Hegde 	 */
8733a634bfcSVikram Hegde 	dmar_table_print(tbl);
8743a634bfcSVikram Hegde 
8753a634bfcSVikram Hegde 	dmar_table = tbl;
8763a634bfcSVikram Hegde 
8773a634bfcSVikram Hegde 	return (DDI_SUCCESS);
8783a634bfcSVikram Hegde }
8793a634bfcSVikram Hegde 
8803a634bfcSVikram Hegde void
8813a634bfcSVikram Hegde immu_dmar_startup(void)
8823a634bfcSVikram Hegde {
8833a634bfcSVikram Hegde 	/* nothing to do */
8843a634bfcSVikram Hegde }
8853a634bfcSVikram Hegde 
8863a634bfcSVikram Hegde void
8873a634bfcSVikram Hegde immu_dmar_shutdown(void)
8883a634bfcSVikram Hegde {
8893a634bfcSVikram Hegde 	/* nothing to do */
8903a634bfcSVikram Hegde }
8913a634bfcSVikram Hegde 
8923a634bfcSVikram Hegde void
8933a634bfcSVikram Hegde immu_dmar_destroy(void)
8943a634bfcSVikram Hegde {
8953a634bfcSVikram Hegde 	dmar_devi_destroy(dmar_table);
8963a634bfcSVikram Hegde 	dmar_table_destroy(dmar_table);
8973a634bfcSVikram Hegde 	ioapic_drhd_destroy();
8983a634bfcSVikram Hegde 	dmar_table = NULL;
8993a634bfcSVikram Hegde 	dmar_raw = NULL;
9003a634bfcSVikram Hegde }
9013a634bfcSVikram Hegde 
9023a634bfcSVikram Hegde boolean_t
9033a634bfcSVikram Hegde immu_dmar_blacklisted(char **strptr, uint_t nstrs)
9043a634bfcSVikram Hegde {
9053a634bfcSVikram Hegde 	dmar_table_t *tbl = dmar_table;
9063a634bfcSVikram Hegde 	int i;
9073a634bfcSVikram Hegde 	char oem_rev[IMMU_MAXNAMELEN];
9083a634bfcSVikram Hegde 
9093a634bfcSVikram Hegde 	ASSERT(tbl);
9103a634bfcSVikram Hegde 
9113a634bfcSVikram Hegde 	ASSERT((strptr == NULL) ^ (nstrs != 0));
9123a634bfcSVikram Hegde 
9133a634bfcSVikram Hegde 	/*
9143a634bfcSVikram Hegde 	 * Must be a minimum of 4
9153a634bfcSVikram Hegde 	 */
9163a634bfcSVikram Hegde 	if (nstrs < 4) {
9173a634bfcSVikram Hegde 		return (B_FALSE);
9183a634bfcSVikram Hegde 	}
9193a634bfcSVikram Hegde 
9203a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?System DMAR ACPI table information:\n");
9213a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?OEM-ID = <%s>\n", tbl->tbl_oem_id);
9223a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Table-ID = <%s>\n", tbl->tbl_oem_tblid);
9233a634bfcSVikram Hegde 	(void) snprintf(oem_rev, sizeof (oem_rev), "%d", tbl->tbl_oem_rev);
9243a634bfcSVikram Hegde 	ddi_err(DER_CONT, NULL, "?Revision = <%s>\n", oem_rev);
9253a634bfcSVikram Hegde 
9263a634bfcSVikram Hegde 	for (i = 0; nstrs - i >= 4; i++) {
9273a634bfcSVikram Hegde 		if (strcmp(*strptr++, "DMAR") == 0) {
9283a634bfcSVikram Hegde 			if (strcmp(*strptr++, tbl->tbl_oem_id) == 0 &&
9293a634bfcSVikram Hegde 			    ((char *)strptr == '\0' ||
9303a634bfcSVikram Hegde 			    strcmp(*strptr++, tbl->tbl_oem_tblid) == 0) &&
9313a634bfcSVikram Hegde 			    ((char *)strptr == '\0' ||
9323a634bfcSVikram Hegde 			    strcmp(*strptr++, oem_rev) == 0)) {
9333a634bfcSVikram Hegde 				return (B_TRUE);
9343a634bfcSVikram Hegde 			}
9353a634bfcSVikram Hegde 			i += 3; /* for loops adds 1 as well, so only 3 here */
9363a634bfcSVikram Hegde 		}
9373a634bfcSVikram Hegde 	}
9383a634bfcSVikram Hegde 	return (B_FALSE);
9393a634bfcSVikram Hegde }
9403a634bfcSVikram Hegde 
9413a634bfcSVikram Hegde void
9423a634bfcSVikram Hegde immu_dmar_rmrr_map(void)
9433a634bfcSVikram Hegde {
9443a634bfcSVikram Hegde 	int seg;
9453a634bfcSVikram Hegde 	int count;
9463a634bfcSVikram Hegde 	dev_info_t *rdip;
9473a634bfcSVikram Hegde 	scope_t *scope;
9483a634bfcSVikram Hegde 	rmrr_t *rmrr;
9493a634bfcSVikram Hegde 	dmar_table_t *tbl;
9503a634bfcSVikram Hegde 
9513a634bfcSVikram Hegde 	ASSERT(dmar_table);
9523a634bfcSVikram Hegde 
9533a634bfcSVikram Hegde 	tbl = dmar_table;
9543a634bfcSVikram Hegde 
9553a634bfcSVikram Hegde 	/* called during boot, when kernel is single threaded. No lock */
9563a634bfcSVikram Hegde 
9573a634bfcSVikram Hegde 	/*
9583a634bfcSVikram Hegde 	 * for each segment, walk the rmrr list looking for an exact match
9593a634bfcSVikram Hegde 	 */
9603a634bfcSVikram Hegde 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
9613a634bfcSVikram Hegde 		rmrr = list_head(&(tbl->tbl_rmrr_list)[seg]);
9623a634bfcSVikram Hegde 		for (; rmrr; rmrr = list_next(&(tbl->tbl_rmrr_list)[seg],
9633a634bfcSVikram Hegde 		    rmrr)) {
9643a634bfcSVikram Hegde 
9653a634bfcSVikram Hegde 			/*
9663a634bfcSVikram Hegde 			 * try to match BDF *exactly* to a device scope.
9673a634bfcSVikram Hegde 			 */
9683a634bfcSVikram Hegde 			scope = list_head(&(rmrr->rm_scope_list));
9693a634bfcSVikram Hegde 			for (; scope;
9703a634bfcSVikram Hegde 			    scope = list_next(&(rmrr->rm_scope_list), scope)) {
9713a634bfcSVikram Hegde 				immu_arg_t imarg = {0};
9723a634bfcSVikram Hegde 				memrng_t mrng = {0};
9733a634bfcSVikram Hegde 
9743a634bfcSVikram Hegde 				/* PCI endpoint devices only */
9753a634bfcSVikram Hegde 				if (scope->scp_type != DMAR_ENDPOINT)
9763a634bfcSVikram Hegde 					continue;
9773a634bfcSVikram Hegde 
9783a634bfcSVikram Hegde 				imarg.ima_seg = seg;
9793a634bfcSVikram Hegde 				imarg.ima_bus = scope->scp_bus;
9803a634bfcSVikram Hegde 				imarg.ima_devfunc =
9813a634bfcSVikram Hegde 				    IMMU_PCI_DEVFUNC(scope->scp_dev,
9823a634bfcSVikram Hegde 				    scope->scp_func);
9833a634bfcSVikram Hegde 				imarg.ima_ddip = NULL;
9843a634bfcSVikram Hegde 				imarg.ima_rdip = NULL;
9853a634bfcSVikram Hegde 
9863a634bfcSVikram Hegde 				ASSERT(root_devinfo);
9873a634bfcSVikram Hegde 				/* XXX should be optimized */
9883a634bfcSVikram Hegde 				ndi_devi_enter(root_devinfo, &count);
9893a634bfcSVikram Hegde 				ddi_walk_devs(ddi_get_child(root_devinfo),
9903a634bfcSVikram Hegde 				    match_bdf, &imarg);
9913a634bfcSVikram Hegde 				ndi_devi_exit(root_devinfo, count);
9923a634bfcSVikram Hegde 
9933a634bfcSVikram Hegde 				if (imarg.ima_ddip == NULL) {
9943a634bfcSVikram Hegde 					ddi_err(DER_WARN, NULL,
9953a634bfcSVikram Hegde 					    "No dip found for "
9963a634bfcSVikram Hegde 					    "bus=0x%x, dev=0x%x, func= 0x%x",
9973a634bfcSVikram Hegde 					    scope->scp_bus, scope->scp_dev,
9983a634bfcSVikram Hegde 					    scope->scp_func);
9993a634bfcSVikram Hegde 					continue;
10003a634bfcSVikram Hegde 				}
10013a634bfcSVikram Hegde 
10023a634bfcSVikram Hegde 				rdip = imarg.ima_ddip;
10033a634bfcSVikram Hegde 				/*
10043a634bfcSVikram Hegde 				 * This address must be in the BIOS reserved
10053a634bfcSVikram Hegde 				 * map
10063a634bfcSVikram Hegde 				 */
10073a634bfcSVikram Hegde 				if (!address_in_memlist(bios_rsvd,
10083a634bfcSVikram Hegde 				    (uint64_t)rmrr->rm_base, rmrr->rm_limit -
10093a634bfcSVikram Hegde 				    rmrr->rm_base + 1)) {
10103a634bfcSVikram Hegde 					ddi_err(DER_WARN, rdip, "RMRR range "
10113a634bfcSVikram Hegde 					    " [0x%" PRIx64 " - 0x%" PRIx64 "]"
1012e03dceedSVikram Hegde 					    " not in BIOS reserved map",
10133a634bfcSVikram Hegde 					    rmrr->rm_base, rmrr->rm_limit);
10143a634bfcSVikram Hegde 				}
10153a634bfcSVikram Hegde 
10163a634bfcSVikram Hegde 				/* XXX could be more efficient */
10173a634bfcSVikram Hegde 				memlist_read_lock();
10183a634bfcSVikram Hegde 				if (address_in_memlist(phys_install,
10193a634bfcSVikram Hegde 				    (uint64_t)rmrr->rm_base, rmrr->rm_limit -
10203a634bfcSVikram Hegde 				    rmrr->rm_base + 1)) {
10213a634bfcSVikram Hegde 					ddi_err(DER_WARN, rdip, "RMRR range "
10223a634bfcSVikram Hegde 					    " [0x%" PRIx64 " - 0x%" PRIx64 "]"
10233a634bfcSVikram Hegde 					    " is in physinstall map",
10243a634bfcSVikram Hegde 					    rmrr->rm_base, rmrr->rm_limit);
10253a634bfcSVikram Hegde 				}
10263a634bfcSVikram Hegde 				memlist_read_unlock();
10273a634bfcSVikram Hegde 
1028*50200e77SFrank Van Der Linden 				(void) immu_dvma_device_setup(rdip, 0);
10293a634bfcSVikram Hegde 
10303a634bfcSVikram Hegde 				ddi_err(DER_LOG, rdip,
10313a634bfcSVikram Hegde 				    "IMMU: Mapping RMRR range "
10323a634bfcSVikram Hegde 				    "[0x%" PRIx64 " - 0x%"PRIx64 "]",
10333a634bfcSVikram Hegde 				    rmrr->rm_base, rmrr->rm_limit);
10343a634bfcSVikram Hegde 
10353a634bfcSVikram Hegde 				mrng.mrng_start =
10363a634bfcSVikram Hegde 				    IMMU_ROUNDOWN((uintptr_t)rmrr->rm_base);
10373a634bfcSVikram Hegde 				mrng.mrng_npages =
10383a634bfcSVikram Hegde 				    IMMU_ROUNDUP((uintptr_t)rmrr->rm_limit -
10393a634bfcSVikram Hegde 				    (uintptr_t)rmrr->rm_base + 1) /
10403a634bfcSVikram Hegde 				    IMMU_PAGESIZE;
1041*50200e77SFrank Van Der Linden 
1042*50200e77SFrank Van Der Linden 				(void) immu_map_memrange(rdip, &mrng);
10433a634bfcSVikram Hegde 			}
10443a634bfcSVikram Hegde 		}
10453a634bfcSVikram Hegde 	}
10463a634bfcSVikram Hegde 
10473a634bfcSVikram Hegde }
10483a634bfcSVikram Hegde 
10493a634bfcSVikram Hegde immu_t *
10503a634bfcSVikram Hegde immu_dmar_get_immu(dev_info_t *rdip)
10513a634bfcSVikram Hegde {
10523a634bfcSVikram Hegde 	int seg;
10533a634bfcSVikram Hegde 	int tlevel;
10543a634bfcSVikram Hegde 	int level;
10553a634bfcSVikram Hegde 	drhd_t *drhd;
10563a634bfcSVikram Hegde 	drhd_t *tdrhd;
10573a634bfcSVikram Hegde 	scope_t *scope;
10583a634bfcSVikram Hegde 	dmar_table_t *tbl;
10593a634bfcSVikram Hegde 
10603a634bfcSVikram Hegde 	ASSERT(dmar_table);
10613a634bfcSVikram Hegde 
10623a634bfcSVikram Hegde 	tbl = dmar_table;
10633a634bfcSVikram Hegde 
10643a634bfcSVikram Hegde 	mutex_enter(&(tbl->tbl_lock));
10653a634bfcSVikram Hegde 
10663a634bfcSVikram Hegde 	/*
10673a634bfcSVikram Hegde 	 * for each segment, walk the drhd list looking for an exact match
10683a634bfcSVikram Hegde 	 */
10693a634bfcSVikram Hegde 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
10703a634bfcSVikram Hegde 		drhd = list_head(&(tbl->tbl_drhd_list)[seg]);
10713a634bfcSVikram Hegde 		for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg],
10723a634bfcSVikram Hegde 		    drhd)) {
10733a634bfcSVikram Hegde 
10743a634bfcSVikram Hegde 			/*
10753a634bfcSVikram Hegde 			 * we are currently searching for exact matches so
10763a634bfcSVikram Hegde 			 * skip "include all" (catchall) and subtree matches
10773a634bfcSVikram Hegde 			 */
10783a634bfcSVikram Hegde 			if (drhd->dr_include_all == B_TRUE)
10793a634bfcSVikram Hegde 				continue;
10803a634bfcSVikram Hegde 
10813a634bfcSVikram Hegde 			/*
10823a634bfcSVikram Hegde 			 * try to match BDF *exactly* to a device scope.
10833a634bfcSVikram Hegde 			 */
10843a634bfcSVikram Hegde 			scope = list_head(&(drhd->dr_scope_list));
10853a634bfcSVikram Hegde 			for (; scope;
10863a634bfcSVikram Hegde 			    scope = list_next(&(drhd->dr_scope_list), scope)) {
10873a634bfcSVikram Hegde 				immu_arg_t imarg = {0};
10883a634bfcSVikram Hegde 
10893a634bfcSVikram Hegde 				/* PCI endpoint devices only */
10903a634bfcSVikram Hegde 				if (scope->scp_type != DMAR_ENDPOINT)
10913a634bfcSVikram Hegde 					continue;
10923a634bfcSVikram Hegde 
10933a634bfcSVikram Hegde 				imarg.ima_seg = seg;
10943a634bfcSVikram Hegde 				imarg.ima_bus = scope->scp_bus;
10953a634bfcSVikram Hegde 				imarg.ima_devfunc =
10963a634bfcSVikram Hegde 				    IMMU_PCI_DEVFUNC(scope->scp_dev,
10973a634bfcSVikram Hegde 				    scope->scp_func);
10983a634bfcSVikram Hegde 				imarg.ima_ddip = NULL;
10993a634bfcSVikram Hegde 				imarg.ima_rdip = rdip;
11003a634bfcSVikram Hegde 				level = 0;
11013a634bfcSVikram Hegde 				if (immu_walk_ancestor(rdip, NULL, match_bdf,
11023a634bfcSVikram Hegde 				    &imarg, &level, IMMU_FLAGS_DONTPASS)
11033a634bfcSVikram Hegde 				    != DDI_SUCCESS) {
11043a634bfcSVikram Hegde 					/* skip - nothing else we can do */
11053a634bfcSVikram Hegde 					continue;
11063a634bfcSVikram Hegde 				}
11073a634bfcSVikram Hegde 
11083a634bfcSVikram Hegde 				/* Should have walked only 1 level i.e. rdip */
11093a634bfcSVikram Hegde 				ASSERT(level == 1);
11103a634bfcSVikram Hegde 
11113a634bfcSVikram Hegde 				if (imarg.ima_ddip) {
11123a634bfcSVikram Hegde 					ASSERT(imarg.ima_ddip == rdip);
11133a634bfcSVikram Hegde 					goto found;
11143a634bfcSVikram Hegde 				}
11153a634bfcSVikram Hegde 			}
11163a634bfcSVikram Hegde 		}
11173a634bfcSVikram Hegde 	}
11183a634bfcSVikram Hegde 
11193a634bfcSVikram Hegde 	/*
11203a634bfcSVikram Hegde 	 * walk the drhd list looking for subtree match
11213a634bfcSVikram Hegde 	 * i.e. is the device a descendant of a devscope BDF.
11223a634bfcSVikram Hegde 	 * We want the lowest subtree.
11233a634bfcSVikram Hegde 	 */
11243a634bfcSVikram Hegde 	tdrhd = NULL;
11253a634bfcSVikram Hegde 	tlevel = 0;
11263a634bfcSVikram Hegde 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
11273a634bfcSVikram Hegde 		drhd = list_head(&(tbl->tbl_drhd_list)[seg]);
11283a634bfcSVikram Hegde 		for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg],
11293a634bfcSVikram Hegde 		    drhd)) {
11303a634bfcSVikram Hegde 
11313a634bfcSVikram Hegde 			/* looking for subtree match */
11323a634bfcSVikram Hegde 			if (drhd->dr_include_all == B_TRUE)
11333a634bfcSVikram Hegde 				continue;
11343a634bfcSVikram Hegde 
11353a634bfcSVikram Hegde 			/*
11363a634bfcSVikram Hegde 			 * try to match the device scope
11373a634bfcSVikram Hegde 			 */
11383a634bfcSVikram Hegde 			scope = list_head(&(drhd->dr_scope_list));
11393a634bfcSVikram Hegde 			for (; scope;
11403a634bfcSVikram Hegde 			    scope = list_next(&(drhd->dr_scope_list), scope)) {
11413a634bfcSVikram Hegde 				immu_arg_t imarg = {0};
11423a634bfcSVikram Hegde 
11433a634bfcSVikram Hegde 				/* PCI subtree only */
11443a634bfcSVikram Hegde 				if (scope->scp_type != DMAR_SUBTREE)
11453a634bfcSVikram Hegde 					continue;
11463a634bfcSVikram Hegde 
11473a634bfcSVikram Hegde 				imarg.ima_seg = seg;
11483a634bfcSVikram Hegde 				imarg.ima_bus = scope->scp_bus;
11493a634bfcSVikram Hegde 				imarg.ima_devfunc =
11503a634bfcSVikram Hegde 				    IMMU_PCI_DEVFUNC(scope->scp_dev,
11513a634bfcSVikram Hegde 				    scope->scp_func);
11523a634bfcSVikram Hegde 
11533a634bfcSVikram Hegde 				imarg.ima_ddip = NULL;
11543a634bfcSVikram Hegde 				imarg.ima_rdip = rdip;
11553a634bfcSVikram Hegde 				level = 0;
11563a634bfcSVikram Hegde 				if (immu_walk_ancestor(rdip, NULL, match_bdf,
11573a634bfcSVikram Hegde 				    &imarg, &level, 0) != DDI_SUCCESS) {
11583a634bfcSVikram Hegde 					/* skip - nothing else we can do */
11593a634bfcSVikram Hegde 					continue;
11603a634bfcSVikram Hegde 				}
11613a634bfcSVikram Hegde 
11623a634bfcSVikram Hegde 				/* should have walked 1 level i.e. rdip */
11633a634bfcSVikram Hegde 				ASSERT(level > 0);
11643a634bfcSVikram Hegde 
11653a634bfcSVikram Hegde 				/* look for lowest ancestor matching drhd */
11663a634bfcSVikram Hegde 				if (imarg.ima_ddip && (tdrhd == NULL ||
11673a634bfcSVikram Hegde 				    level < tlevel)) {
11683a634bfcSVikram Hegde 					tdrhd = drhd;
11693a634bfcSVikram Hegde 					tlevel = level;
11703a634bfcSVikram Hegde 				}
11713a634bfcSVikram Hegde 			}
11723a634bfcSVikram Hegde 		}
11733a634bfcSVikram Hegde 	}
11743a634bfcSVikram Hegde 
11753a634bfcSVikram Hegde 	if ((drhd = tdrhd) != NULL) {
11763a634bfcSVikram Hegde 		goto found;
11773a634bfcSVikram Hegde 	}
11783a634bfcSVikram Hegde 
11793a634bfcSVikram Hegde 	for (seg = 0; seg < IMMU_MAXSEG; seg++) {
11803a634bfcSVikram Hegde 		drhd = list_head(&(tbl->tbl_drhd_list[seg]));
11813a634bfcSVikram Hegde 		for (; drhd; drhd = list_next(&(tbl->tbl_drhd_list)[seg],
11823a634bfcSVikram Hegde 		    drhd)) {
11833a634bfcSVikram Hegde 			/* Look for include all */
11843a634bfcSVikram Hegde 			if (drhd->dr_include_all == B_TRUE) {
11853a634bfcSVikram Hegde 				break;
11863a634bfcSVikram Hegde 			}
11873a634bfcSVikram Hegde 		}
11883a634bfcSVikram Hegde 	}
11893a634bfcSVikram Hegde 
11903a634bfcSVikram Hegde 	/*FALLTHRU*/
11913a634bfcSVikram Hegde 
11923a634bfcSVikram Hegde found:
11933a634bfcSVikram Hegde 	mutex_exit(&(tbl->tbl_lock));
11943a634bfcSVikram Hegde 
11953a634bfcSVikram Hegde 	/*
11963a634bfcSVikram Hegde 	 * No drhd (dmar unit) found for this device in the ACPI DMAR tables.
11973a634bfcSVikram Hegde 	 * This may happen with buggy versions of BIOSes. Just warn instead
11983a634bfcSVikram Hegde 	 * of panic as we don't want whole system to go down because of one
11993a634bfcSVikram Hegde 	 * device.
12003a634bfcSVikram Hegde 	 */
12013a634bfcSVikram Hegde 	if (drhd == NULL) {
12023a634bfcSVikram Hegde 		ddi_err(DER_WARN, rdip, "can't find Intel IOMMU unit for "
12033a634bfcSVikram Hegde 		    "device in ACPI DMAR table.");
12043a634bfcSVikram Hegde 		return (NULL);
12053a634bfcSVikram Hegde 	}
12063a634bfcSVikram Hegde 
12073a634bfcSVikram Hegde 	return (drhd->dr_immu);
12083a634bfcSVikram Hegde }
12093a634bfcSVikram Hegde 
12103a634bfcSVikram Hegde dev_info_t *
12113a634bfcSVikram Hegde immu_dmar_unit_dip(void *dmar_unit)
12123a634bfcSVikram Hegde {
12133a634bfcSVikram Hegde 	drhd_t *drhd = (drhd_t *)dmar_unit;
12143a634bfcSVikram Hegde 	return (drhd->dr_dip);
12153a634bfcSVikram Hegde }
12163a634bfcSVikram Hegde 
12173a634bfcSVikram Hegde void *
12183a634bfcSVikram Hegde immu_dmar_walk_units(int seg, void *dmar_unit)
12193a634bfcSVikram Hegde {
12203a634bfcSVikram Hegde 	list_t *drhd_list;
12213a634bfcSVikram Hegde 	drhd_t *drhd = (drhd_t *)dmar_unit;
12223a634bfcSVikram Hegde 
12233a634bfcSVikram Hegde 	drhd_list = &(dmar_table->tbl_drhd_list[seg]);
12243a634bfcSVikram Hegde 
12253a634bfcSVikram Hegde 	if (drhd == NULL) {
12263a634bfcSVikram Hegde 		return ((void *)list_head(drhd_list));
12273a634bfcSVikram Hegde 	} else {
12283a634bfcSVikram Hegde 		return ((void *)list_next(drhd_list, drhd));
12293a634bfcSVikram Hegde 	}
12303a634bfcSVikram Hegde }
12313a634bfcSVikram Hegde 
12323a634bfcSVikram Hegde void
12333a634bfcSVikram Hegde immu_dmar_set_immu(void *dmar_unit, immu_t *immu)
12343a634bfcSVikram Hegde {
12353a634bfcSVikram Hegde 	drhd_t *drhd = (drhd_t *)dmar_unit;
12363a634bfcSVikram Hegde 
12373a634bfcSVikram Hegde 	ASSERT(drhd);
12383a634bfcSVikram Hegde 	ASSERT(immu);
12393a634bfcSVikram Hegde 
12403a634bfcSVikram Hegde 	drhd->dr_immu = immu;
12413a634bfcSVikram Hegde }
12423a634bfcSVikram Hegde 
12433a634bfcSVikram Hegde boolean_t
12443a634bfcSVikram Hegde immu_dmar_intrmap_supported(void)
12453a634bfcSVikram Hegde {
12463a634bfcSVikram Hegde 	ASSERT(dmar_table);
12473a634bfcSVikram Hegde 	return (dmar_table->tbl_intrmap);
12483a634bfcSVikram Hegde }
12493a634bfcSVikram Hegde 
12503a634bfcSVikram Hegde /* for a given ioapicid, find the source id and immu */
12513a634bfcSVikram Hegde uint16_t
1252c94adbf9SFrank Van Der Linden immu_dmar_ioapic_sid(int ioapic_ix)
12533a634bfcSVikram Hegde {
12543a634bfcSVikram Hegde 	ioapic_drhd_t *idt;
12553a634bfcSVikram Hegde 
12567ff178cdSJimmy Vetayases 	idt = ioapic_drhd_lookup(psm_get_ioapicid(ioapic_ix));
12573a634bfcSVikram Hegde 	if (idt == NULL) {
12583a634bfcSVikram Hegde 		ddi_err(DER_PANIC, NULL, "cannot determine source-id for "
1259c94adbf9SFrank Van Der Linden 		    "IOAPIC (index = %d)", ioapic_ix);
12603a634bfcSVikram Hegde 		/*NOTREACHED*/
12613a634bfcSVikram Hegde 	}
12623a634bfcSVikram Hegde 
12633a634bfcSVikram Hegde 	return (idt->ioapic_sid);
12643a634bfcSVikram Hegde }
12653a634bfcSVikram Hegde 
12663a634bfcSVikram Hegde /* for a given ioapicid, find the source id and immu */
12673a634bfcSVikram Hegde immu_t *
1268c94adbf9SFrank Van Der Linden immu_dmar_ioapic_immu(int ioapic_ix)
12693a634bfcSVikram Hegde {
12703a634bfcSVikram Hegde 	ioapic_drhd_t *idt;
12713a634bfcSVikram Hegde 
12727ff178cdSJimmy Vetayases 	idt = ioapic_drhd_lookup(psm_get_ioapicid(ioapic_ix));
12733a634bfcSVikram Hegde 	if (idt) {
12743a634bfcSVikram Hegde 		return (idt->ioapic_drhd ? idt->ioapic_drhd->dr_immu : NULL);
12753a634bfcSVikram Hegde 	}
12763a634bfcSVikram Hegde 	return (NULL);
12773a634bfcSVikram Hegde }
1278