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 *)®, 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